[Solved] Question about QIODevice::readyRead() signal
-
Hi all,
In the Qt documentation it states that "readyRead()":http://doc.qt.nokia.com/4.7/qiodevice.html#readyRead will be emitted each time there is new data in the socket waiting to be read. It also states that "readyRead() is not emitted recursively". What exactly does this mean ?In my situation I use a QTcpSocket to process network data. Does this mean that when my slot attached to the readyRead() signal is busy processing data from the socket and there is new data available, the same slot would not be called due to the fact that execution is already in the readyRead handler ? In essence don't the readyRead() signals get queued up and emitted like most other events ? I'm having doubts since I rely solely on readyRead() to notify me of network data and I am getting missed packets while processing is busy in readyRead().
thanks.
-
Just an idea:
bq. readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).
it means to me that once the slot finished its work the signal will not be re-emitted even if there is still data unconsumed by the lost itself. This is just my interpretation.
-
That is also how I read it. The behaviour makes perfect sense as well.
What you can do, is make your handler itself a while loop: while there is still more data to read, you keep on reading. If more data arrives while you were processing a data block, it will show up at the next iteration and you can immediately handle the new data.
-
Well that's how I thought so too. Seems to be it is correct. That is readyRead() won't be called again (or queued for later processing) if the application is already handling the slot for readyRead().
I overcame this by checking the QTcpSocket::bytesAvailable() function in a while loop as suggested above. Thanks.@void SocketsLayer::OnSocketReadyRead()
{
int iBytesAvailable = p_ClientSocket->bytesAvailable();
while(iBytesAvailable > 0)
{
if(p_ClientSocket->isValid())
{
char* pzBuff = new char[iBytesAvailable];
int iReadBytes = p_ClientSocket->read(pzBuff, iBytesAvailable);
if( iReadBytes > 0 )
{
// do something
}
else
qDebug("read bytes count is not valid !!"); // possibly an error
}
else
{
QMessageBox::warning(p_ParentWidget, "Warning", "Socket is not valid to read data from");
break;
}iBytesAvailable = p_ClientSocket->bytesAvailable();
}
}@ -
I have just recently run into the same problem....
In my readyRead signal handler, I read all bytesAvailable() bytes (once) and process them. Occasionally, after returning from the handler, it is not called again, though bytesAvailable() returns non-zero.
I, too, "fixed" the problem by having my handler loop on non-zero bytesAvailable() return.
But I'm still not completely comfortable, because:
- The documentation says: "readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted" I am not either calling waitForReadyRead() or (as far as I know) reentering the event loop. I know I'm not reentering the event loop deliberately. I suppose there's a chance that I'm calling a routine that reenters the event loop to get it's work done, but I don't think so. (Is there any list of such routines anywhere?) I've set a break point on every QEventLoop method I could find, and only the outer processEvents() is ever being called. None are called from my slot. Since the documentation seems to imply that, if you don't reenter the event handler or call waitForReadyRead() in your slot, readyRead signals will be queued, that's what I expect to happen. But it doesn't seem to be.
- It appears that all I have done is shrunk the timing window. The trouble happens if bytes come in while you're in your slot. When I was only checking bytesAvailable() at the beginning of my slot, this was a long window. Now that the last thing I do in my slot is get a 0 return from bytesAvailable() the window is very small. But not closed. Couldn't the following sequence occur?:
- My slot checks bytesAvailable(), which returns 0
- New bytes arrive, I'm still in my slot, so readyRead() will not be signaled
- My slot returns, the event loop does nothing
If so, what's the right way to handle it? (Of course, I could have a timer periodically check bytesAvailable(), but that doesn't seem very elegant.)
-
Er, kindly disregard my previous post.
I just discovered that the readyRead signal my slot is receiving is not from a vanilla QIODevice, but from a derivative -- QextSerialPort.
And QextSerialPort::readyRead implementation has nothing to do with QIODevice::readyRead -- it's actually emitted from a QSocketNotifier::active() handler (at least, for unix, which is where I am).
And since QextSerialPort is an add-on -- not part of the Qt distribution -- I reckon problems with it ought not be posted here.
-
[quote author="SeedOfLife" date="1317282267"]Well that's how I thought so too. Seems to be it is correct. That is readyRead() won't be called again (or queued for later processing) if the application is already handling the slot for readyRead(). I overcame this by checking the QTcpSocket::bytesAvailable() function in a while loop as suggested above. Thanks. @void SocketsLayer::OnSocketReadyRead() { int iBytesAvailable = p_ClientSocket->bytesAvailable(); while(iBytesAvailable > 0) { if(p_ClientSocket->isValid()) { char* pzBuff = new char[iBytesAvailable]; int iReadBytes = p_ClientSocket->read(pzBuff, iBytesAvailable); if( iReadBytes > 0 ) { // do something } else qDebug("read bytes count is not valid !!"); // possibly an error } else { QMessageBox::warning(p_ParentWidget, "Warning", "Socket is not valid to read data from"); break; } iBytesAvailable = p_ClientSocket->bytesAvailable(); } }@[/quote]
Basically that is the proper design. You could tighten up the code a bit so there is only one read of bytesAvailable() in the beginning of the loop instead of outside the loop, and then again inside the loop at the end. It's just a matter of personal preference.