使用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()
检查之后到达的情况(即使将检查放在函数的绝对末尾也无法解决这个问题)。
确保能够重现问题
由于这个问题很少出现,我会继续使用第一个槽函数实现,并添加人为超时,以确保问题发生:
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字节的数据。发生了以下情况:
新连接!
32768(或16K或48K)
信息的第一部分被读取,但是由于不会再次调用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的设计错误还是我遗漏了什么?
QCoreApplication :: processEvents()
可能会再次等同于您的场景,因为Qt处理传入数据,看到您当前在与readyRead()连接的插槽中,因此它不会再次调用它,您的检查器再次被忽略。 - leemes