PDA

View Full Version : Signal never received from network capture thread



skimpax
1st February 2010, 10:05
Hi,

My (sniffer) appliation has a capture QThread which is awaiting packets from a network interface and shall emit then this received packet to the main (GUI) thread for edicated treatment.
My problem is that emit is called but signal is never received by the client class which which was connected to this signal the main thread.

1 - Any idea why this does not work ?
2 - Can the client class connect to the QThread BEFORE the tread has been started (before call to start()) ?

Thanks.

Note:
The sniffer thread uses pcap library for low level sniffing.

Here below is the simplified code to give an idea of the implementation :



class SnifferThread : public QThread
{
Q_OBJECT;

signals:
void signalPacketReceived(uint8* pData, uint32 pDataLength, uint32 pOrd);
//...
};

void SnifferThread::run()
{
pcapfp_ = pcap_open_live(itf.c_str(), PCAP_MAX_NB_BYTE, 1, -1, errorBuffer);
pcap_pkthdr pktHeader;

while (canSniff_)
{
isSniffing_ = true;

const uint8* data = pcap_next(pcapfp_, &pktHeader);
if ( data != 0 )
{
// ...
emit signalPacketReceived(buffer, bufSize, ++frameNumber_);
}
else
{
// No packet available: very short asleep
QThread::msleep(20);
}
}
}

high_flyer
1st February 2010, 11:07
My problem is that emit is called but signal is never received by the client class which which was connected to this signal the main thread.


A common issue is typos in the connect() line.
Do you get a connect() no such signal/slot warning while building?


2 - Can the client class connect to the QThread BEFORE the tread has been started (before call to start()) ?
If you have the addresses of the emitting and receiving objects, you can connect them at any time.

skimpax
1st February 2010, 11:21
I use to always check the return code of connect() with an assertion and it is OK : client successfuly connect to the sniffer thread signal.



bool ok = connect(...)
assert(ok == true);


Is there a way to go in debug mode inside Qt so that we can see the event queue state/length at the emetter side (my sniffer thread) ?

high_flyer
1st February 2010, 12:16
Is there a way to go in debug mode inside Qt so that we can see the event queue state/length at the emetter side (my sniffer thread) ?
Yes, if you link against the debug Qt build.

Are you using queued signals?

skimpax
1st February 2010, 13:35
Yes, I use debug libs for Qt.
I do not specify anything about signal/slot : just connect as usual (connect() without last parameter, so 'automatic'), assuming qt detects it shall be queued.

Lesiok
1st February 2010, 14:04
Standard receipt 1787453685 :D : try to add betwen lines 29 and 30 (at the end of loop)

QCoreApplication::processEvents();
Because (I think) sender and receiver of signal are in separeted threads so signala are queued. In line 23 the signal is put in queue. Then the signal is received to slot by event dispatcher, but You don't give them chance to do it by going around in while loop. It will be better to don't use while loop in SnifferThread::run() method but make only one prob to read data and call them by QTimer.
P.S.
Did You run somewhere SnifferThread::exec() method ?

skimpax
1st February 2010, 16:09
No nowhere I execute exec() method.
Using QCoreApplication::processEvents(); inside main loop does not change anyhing.

Lesiok
1st February 2010, 17:06
No nowhere I execute exec() method.
I think that this is a source of Yours problem. Quotation from QThread doc : Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit().
It is necessary to call this function to start event handling. So Yours class must look something like this :

class SnifferThread : public QThread
{
Q_OBJECT;

signals:
void signalPacketReceived(uint8* pData, uint32 pDataLength, uint32 pOrd);
slots:
void sniffOnePacket();
//...
private:
QTimer m_timer;
};

void SnifferThread::run()
{
pcapfp_ = pcap_open_live(itf.c_str(), PCAP_MAX_NB_BYTE, 1, -1, errorBuffer);
connect(&m_timer,SIGNAL(timeout()),this,SLOT(sniffOnePacket ()));
m_timer.start(20);
exec();
disconnect(&m_timer,SIGNAL(timeout()),this,SLOT(sniffOnePacket ()));
}

void SnifferThread::sniffOnePacket()
{
pcap_pkthdr pktHeader;

if(canSniff_)
{
isSniffing_ = true;

const uint8* data = pcap_next(pcapfp_, &pktHeader);
if ( data != 0 )
{
// ...
emit signalPacketReceived(buffer, bufSize, ++frameNumber_);
}
}
}
This code is not tested.

skimpax
2nd February 2010, 18:16
I also understood my error was that I never call QThread::exec().

I changed my implementation to delegate sniffing activity to a dedicated class.
In my SniffferThread::run(), I just instanciate this SnifferCore object and send it the start signal, then QThread::start() goes in exec().

The main difference between our both implementation is that you rely on a timer, while I use QCoreApplication::processevents() to let the Qt event loop processing.

Your proposal is very interesting : I will check the avantages of each to do my final implementation.



void SnifferThread::run()
{
if ( core_ == 0 )
{
core_ = new SnifferCore(this);
}

emit signalAction(true);
exec();
}

void SnifferCore::slotAction(bool pStart)
{
if ( pStart )
{
openDeviceNetwork();
pcap_pkthdr pktHeader;

while (canSniff_)
{
isSniffing_ = true;

// Handle pause case
bool first = true;
while (canSniff_ )
{
const uint8* data = pcap_next(pcapfp_, &pktHeader);
if ( data != 0 )
{
emit signalPacketReceived(data , ...);
}
else
{
// No packet available: very short asleep
QCoreApplication::processEvents();
}
}
)

cleanup();
}


Many thanks :D

skimpax
4th February 2010, 13:59
Reply to myself as I lost a lot of time to understand why my reworked implementation did not work:

I reworked my test code above by replacing :

void SnifferCore::slotAction(bool pStart);
by something like :


typedef enum
{
ActStart,
ActStop
} Action_e;
void SnifferCore::slotAction(Action_e pAction);

The connection was OK, but the slot slotAction(Action_e) was never called at runtime !!!
I put back a bool instead... and it work !!!
I used an uint32 parameter (which is a #define of unsigned int) and it did not work.
I used an int instead and it work.

The key point is to use qRegisterMetaType

Just by adding the line : qRegisterMetaType<Action_e>("Action_e"); things are OK.


qRegisterMetaType<Action_e>("Action_e");
ok = connect(myThread_, SIGNAL(signalAction(Action_e)),
this, SLOT(slotAction(Action_e)));
ASSERT(ok == true);


For any other people, if you have problem with queued signal/slot, don't forget, if you are using non-native type as parameter, to invoque qRegisterMetaType<YourType>("YourType"); before connecting !!!

So many time lost :( , but so many learnt on Qt :) ...