How to make sure that readyRead() signals from QTcpSocket can't be missed?
Short answer
The documentation of QIODevice::readyRead()
states:
readyRead()
is not emitted recursively; if you reenter the event loop or callwaitForReadyRead()
inside a slot connected to thereadyRead()
signal, the signal will not be reemitted.
Thus, make sure that you
- don't instantiate a
QEventLoop
inside your slot, - don't call
QApplication::processEvents()
inside your slot, - don't call
QIODevice::waitForReadyRead()
inside your slot, - don't use the same
QTcpSocket
instance within different threads.
Now you should always receive all data sent by the other side.
Background
The readyRead()
signal is emitted by QAbstractSocketPrivate::emitReadyRead()
as follows:
// Only emit readyRead() when not recursing.
if (!emittedReadyRead && channel == currentReadChannel) {
QScopedValueRollback<bool> r(emittedReadyRead);
emittedReadyRead = true;
emit q->readyRead();
}
The emittedReadyRead
variable is rolled back to false
as soon as the if
block goes out of scope (done by the QScopedValueRollback
). So the only chance to miss a readyRead()
signal is when the control flow reaches the if
condition again before the processing of the last readyRead()
signal has finished (in other words, when there would be a recursion).
And a recursion should only be possible in the situations listed above.
I think scenario mentioned in this topic has two major cases which works differently, but in general QT doesn't have this problem at all and I will try to explain below why.
First case: Single threaded application.
Qt uses select() system call to poll open file descriptor for any change happened or operations available. Simple saying on every loop Qt checks if any of opened file descriptors have data available to read/closed etc. So on single threaded application flow looks like that (code part simplified)
int mainLoop(...) {
select(...);
foreach( descriptor which has new data available ) {
find appropriate handler
emit readyRead;
}
}
void slotReadyRead() {
some code;
}
So what will happend if new data arrived while program still inside slotReadyRead.. honestly nothing special. OS will buffer data, and as soon as control will return to next execute of select() OS will notify software that there are data available for particular file handle. It works in absolutely the same way for TCP sockets/files etc.
I can imaging situations where (in case of really long delays in slotReadyRead and a lot of data coming) you can experience an overrun within OS FIFO buffers (for example for serial ports) but that has more to do with a bad software design rather then QT or OS problems.
You should look on slots like readyRead like on a interrupt handlers and keep their logic only within fetch functionality which fills your internals buffers while processing should be done in separate threads or while application on idle etc.. Reason is that any such application in general is a mass service system and if it spends more time on serving one request then a time interval between two requests it's queue will overrun anyway.
Second scenario: multithreaded application
Actually this scenario is not that much differ from 1) expect that you should design right what happens in each of your threads. If you keep main loop with light wighted 'pseudo interrupt handlers' you will be absolutely fine and keep processing logic in other threads, but this logic should work with your own prefetch buffers rather then with QIODevice.