gpt4 book ai didi

c++ - 如何确保不会错过来自 QTcpSocket 的 readyRead() 信号?

转载 作者:IT老高 更新时间:2023-10-28 12:51:53 25 4
gpt4 key购买 nike

当使用QTcpSocket接收数据时,使用的信号是readyRead(),表示有新数据可用。但是,当您在相应的插槽实现中读取数据时,不会发出额外的 readyRead()。这可能是有道理的,因为您已经在函数中,您正在读取所有可用数据。

问题描述

但是假设此插槽的以下实现:

void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
}

如果在调用 readAll() 之后但在离开槽之前有一些数据到达怎么办?如果这是其他应用程序发送的最后一个数据包(或者至少是一段时间内的最后一个)怎么办?不会发出额外的信号,因此您必须确保自己读取所有数据。

最小化问题的一种方法(但不能完全避免)

当然我们可以这样修改slot:

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 字节的数据。这就是发生的事情:

new connection!
32768 (or 16K or 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调用的slot不能早点退出,否则会出现同样的问题再次发生,该数据恰好在错误的时刻到达并且 readyRead() 不会发出。

这真的是解决我的问题的最佳解决方案吗?这个问题的存在是 Qt 中的设计错误还是我遗漏了什么?

最佳答案

简答

documentation QIODevice::readyRead() 状态:

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.

因此,请确保您

  • 不要在您的插槽内实例化 QEventLoop
  • 不要在你的 slot 中调用 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();
}

一旦 if block 超出范围(由 QScopedValueRollback 完成),emittedReadyRead 变量就会回滚到 false )。所以错过readyRead()信号的唯一机会是当控制流再次到达if条件处理最后一个之前>readyRead() 信号已经完成(换句话说,当有递归时)。

只有在上面列出的情况下才能进行递归。

关于c++ - 如何确保不会错过来自 QTcpSocket 的 readyRead() 信号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16023533/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com