当使用QTcpSocket
接收数据时,要使用的信号是readyRead()
,表示有新数据可用。
但是,当您在相应的插槽实现中读取数据时,将不会发出其他readyRead()
。
正如您已经在该函数中那样,在其中读取所有可用数据可能是有道理的。
问题描述
但是,假定此插槽的以下实现:
void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
}
如果在调用
readAll()
之后但离开插槽之前有一些数据到达该怎么办?如果这是另一个应用程序发送的最后一个数据包(或一段时间内至少是最后一个),该怎么办?
不会发射其他信号,因此您必须确保自己读取所有数据。
最小化问题的一种方法(但不能完全避免)
当然我们可以这样修改插槽:
void readSocketData()
{
while(socket->bytesAvailable())
datacounter += socket->readAll().length();
qDebug() << datacounter;
}
但是,我们尚未解决问题。仍然有可能数据仅在
socket->bytesAvailable()
-check之后到达(甚至将/另一个检查放在函数的绝对末尾也不能解决此问题)。确保能够重现问题
因为这个问题当然很少发生,所以我坚持使用该插槽的第一个实现,甚至还要添加一个人工超时,以确保出现此问题:
void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
// wait, to make sure that some data arrived
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
}
然后,我让另一个应用程序发送100,000个字节的数据。
这是发生了什么:
消息的第一部分已读取,但末尾不再读取,因为不会再次调用
readyRead()
。我的问题是:什么才是最好的保证,这个问题永远不会发生?
可能的解决方案
我想出的一种解决方案是,再次在末尾再次调用同一插槽,并在插槽的开头进行检查,是否还有其他数据要读取:
void readSocketData(bool selfCall) // default parameter selfCall=false in .h
{
if (selfCall && !socket->bytesAvailable())
return;
datacounter += socket->readAll().length();
qDebug() << datacounter;
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
QTimer::singleShot(0, this, SLOT(readSocketDataSelfCall()));
}
void readSocketDataSelfCall()
{
readSocketData(true);
}
因为我不直接调用插槽,而是使用
QTimer::singleShot()
,所以我假设QTcpSocket
不知道我再次调用该插槽,因此不再发出readyRead()
的问题不再发生。我之所以包含参数
bool selfCall
的原因是,不允许QTcpSocket
调用的插槽更早退出,否则会再次发生相同的问题,即数据恰好在错误的时间到达,并且readyRead()
没有发出。这真的是解决我的问题的最佳解决方案吗?
这个问题的存在是Qt中的设计错误还是我错过了一些东西?
最佳答案
简短答案QIODevice::readyRead()
的documentation指出:
因此,请确保您
QEventLoop
QApplication::processEvents()
QIODevice::waitForReadyRead()
QTcpSocket
实例。 现在,您应该始终收到对方发送的所有数据。
背景
readyRead()
发出QAbstractSocketPrivate::emitReadyRead()
信号,如下所示:// Only emit readyRead() when not recursing.
if (!emittedReadyRead && channel == currentReadChannel) {
QScopedValueRollback<bool> r(emittedReadyRead);
emittedReadyRead = true;
emit q->readyRead();
}
一旦
emittedReadyRead
块超出范围(由false
完成),if
变量就会回滚到QScopedValueRollback
。因此,错过readyRead()
信号的唯一机会是当控制流在之前再次达到if
条件时,最后一个readyRead()
信号的处理已完成(换句话说,当有递归时)。并且仅在上述情况下才可以进行递归。
关于c++ - 如何确保不会遗漏来自QTcpSocket的readyRead()信号?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16023533/