如何确保从QTcpSocket的readyRead()信号不能错过? [英] How to make sure that readyRead() signals from QTcpSocket can't be missed?

查看:1465
本文介绍了如何确保从QTcpSocket的readyRead()信号不能错过?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当使用 QTcpSocket 接收数据时,要使用的信号是 readyRead()可用。
然而,当你在相应的槽实现中读取数据时,不会发出额外的 readyRead()
这可能是有意义的,因为你已经在函数中,你正在读取所有可用的数据。

When using QTcpSocket to receive data, the signal to use is readyRead(), which signals that new data is available. However, when you are in the corresponding slot implementation to read the data, no additional readyRead() will be emitted. This may make sense, as you are already in the function, where you are reading all the data that is available.

但是,假设此广告位的以下实施:

However assume the following implementation of this slot:

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

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

What if some data arrives after calling readAll() but before leaving the slot? What if this was the last data packet sent by the other application (or at least the last one for some time)? No additional signal will be emitted, so you have to make sure to read all the data yourself.

当然,像这样:

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

但是,我们还没有解决问题。仍然有可能数据到达 socket-> bytesAvailable() -check(甚至在函数的绝对结尾放置/另一个检查不会解决这个问题)。

However, we haven't solved the problem. It is still possible that data arrives just after the socket->bytesAvailable()-check (and even placing the/another check at the absolute end of the function doesn't solve this).

由于这个问题当然很少发生,我坚持到第一个插槽的实现,我甚至会添加一个人工超时,以确保问题发生:

As this problem of course happens very rarely, I stick to the first implementation of the slot, and I'll even add a an artificial timeout, to be sure that the problem occurs:

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个字节的数据。
这是发生了什么:

I then let another application send 100,000 bytes of data. This is what happens:


新连接!

32768(或16K或48K) p>

new connection!
32768 (or 16K or 48K)

读取消息的第一部分,但不再读取结束,因为 readyRead()将不会再次调用。

The first part of the message is read, but the end isn't read anymore, as readyRead() won't be called again.

我的问题是:最好的方法是什么,这个问题永远不会发生?

My question is: what is the best way to be sure, this problem never occurs?

我想出的一个解决方案是再次调用同一个插槽,并在插槽的开头检查,如果还有更多的数据需要读取:

One solution I came up with is calling the same slot again at the end again, and to check at the beginning of the slot, if there is any more data to read:

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()不会发生不能再发生了。

As I don't call the slot directly, but use QTimer::singleShot(), I assume that the QTcpSocket can't know that I'm calling the slot again, so the problem that readyRead() isn't emitted can't happen anymore.

我已经包含参数 bool selfCall 的原因是由 QTcpSocket 不允许更快退出,否则同样的问题可能再次发生,数据准确地到达错误的时刻,并且 readyRead()不发射。

The reason why I have included the parameter bool selfCall is that the slot which is called by the QTcpSocket isn't allowed to exit sooner, else the same problem can occur again, that data arrives exactly at the wrong moment and readyRead() isn't emitted.

这真的是解决我的问题的最佳解决方案吗?
是否存在这个问题是Qt中的设计错误还是我缺少某些东西?

Is this really the best solution to solve my problem? Is the existence of this problem a design error in Qt or am I missing something?

推荐答案

解决方案



QIODevice :: readyRead文档


如果您在连接到readyRead()信号的插槽
中重新输入事件循环或调用waitForReadyRead

If you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted.

因此,请确保您不要输入任何事件循环在您的插槽中调用 waitForReadyRead ,并且问题应该消失。 (详见下文)

So, just make sure that you don't enter any event loop or call waitForReadyRead within your slot, and the problem should be gone. (See below for further details.)

在相关的 QAbstractSocketPrivate :: canReadNotification 实现,发出 readyRead 信号如下(参见行号733):

Within the relevant QAbstractSocketPrivate::canReadNotification implementation, the readyRead signal is emitted as follows (see line number 733):

if (!emittedReadyRead && hasData) {
    QScopedValueRollback<bool> r(emittedReadyRead);
    emittedReadyRead = true;
    emit q->readyRead();
}

你可以看到 emitReadyRead 布尔变量一旦如果块超出范围(由 QScopedValueRollback 完成) 。因此,错过 readyRead 信号的唯一机会是程序控制流到达

You can see there that the emittedReadyRead boolean variable is reset 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 program control flow reaches the

if (!emittedReadyRead && hasData) {

当前 readyRead 信号处理已完成。这应该只有你

check again before the current readyRead signal processing has finished. And this should only be possible if you


  • 调用 waitForReadyRead strong>因此不要这样做),

  • 在插槽中执行任何事件处理,例如实例化 QEventLoop 或调用 QApplication :: processEvents 因此不要这样做) ,

  • 使用多个线程,并且不会正确同步对同一个 QTcpSocket 对象的访问(不要这样做

  • call waitForReadyRead inside your slot (so don't do this),
  • do any event handling inside your slot like e.g. instantiating a QEventLoop or calling QApplication::processEvents (so don't do this),
  • use multiple threads and don't synchronize access to the same QTcpSocket object properly (so, of course, also don't do this)

这篇关于如何确保从QTcpSocket的readyRead()信号不能错过?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆