使用`waitForReadyRead()`代替创建`readyRead()`信号的槽函数,是否合适?

7

使用Qt编写跨平台应用程序(包括在Windows上使用MinGW)。为了从SSL套接字读取数据,我创建了一个单独的线程。这个线程是由于历史原因而存在的,因为早期该应用程序是使用C套接字/ssl/crypto库编写的。现在所有这些都被Qt网络库替换。

对于阻塞线程,waitForReadyRead(milliseconds)似乎是更好的选择。根据Qt层次结构:

QIODevice
   |
QAbstractSocket
   |
QTcpSocket
   |
QSslSocket

QAbscractSocket::waitForReadyRead()的文档建议:

注意:此函数在Windows上可能会随机失败。如果您的软件将在Windows上运行,请考虑使用事件循环和readyRead()信号。

但是在QIODevice::waitForReadyRead()中没有提到类似的警告。

问题:是否可以在所有平台上一致地使用QSslSocket::waitForReadyRead()


为什么我不使用readyRead()信号?
由于某种奇怪的原因,如果我通过readyRead()槽调用某些方法,则不会被调用。而且,QSslSocket::write()也无法正常工作,而以上方法可以正常工作。由于我的代码复杂,所以我无法在这里呈现它。


你说你正在使用线程。如果readyRead发射器和接收器在不同的线程上,你确定接收器的线程有一个活动事件循环吗?另外,我无法帮助感觉到使用waitForReadyRead只会暂时掩盖真正潜在问题的症状。 - G.M.
最初只有一个线程,它建立了TLS连接并与服务器交换了几个自定义消息(读取和写入)。只有在这成功之后,我才开始一个新的循环来进行独占式读取。对于readyRead(),我确信我的代码/理解方面存在问题,导致它无法正常工作。但是,如果我必须使用waitFor...()方法,那么我的当前设计就可以自行工作。唯一的担心是它的Windows实现。另一个较小的问题是,有时Qt readyRead()会在多个连续数据消息之后被发出。在这种情况下,socket-select()更快。 - iammilind
4个回答

10
对于你的问题:是的,你可以使用QSslSocket::waitForReadyRead(),但在Windows上它可能会出现超时,即使数据已经到达套接字。所以如果超时发生了,你必须检查它是超时还是方法失败。检查很简单,只需要判断QAbstractSocket::bytesAvailable() > 0,如果有数据可读,否则就是超时。

当你使用较小的超时并且你的应用程序对延迟不敏感(例如温度传感器与具有温度历史记录的云之间的通信)时,这种方法是可行的。但如果任何不必要的延迟对你来说都是不可接受的,那么你应该使用信号/槽接口。

要了解更多信息,你可以查看Qt错误跟踪器上的错误报告


1
问题可能是使用资源。
当你使用 waitForReady* 时,你会创建一个约束条件,即一个线程只有一个套接字(否则你会遇到奇怪的 bug)。 现在的问题是你有多少个套接字?如果它取决于运行时数据,那么你可能不知道。 一些嵌入式系统有线程数量限制,这可能会影响你的应用程序,而且我认为这是唯一可能影响此类实现的限制。
你的问题的这部分:

为什么我没有使用 readyRead() 信号?由于某种奇怪的原因,如果我将某个方法与 readyRead() 槽绑定,则该方法不会被调用。 此外,QSslSocket::write() 也不起作用,而使用上述方法可以正常工作。由于我的代码复杂,我无法在此处呈现。

看起来很可疑。
我从未见过有人遇到过类似的问题。也许你的代码中有一些部分阻塞了事件循环?

我已经编辑了那个可疑的部分,因为那是我的编码错误。目前我只使用了一个套接字。也许这个问题现在更具体地涉及到wait..()方法。 - iammilind

1
根据您的问题。QIODevice的实现仅返回false。因此,没有必要提示有时会失败。 QAbstractSocket的实现在内部调用称为“nativeSelect”的东西,然后将其定向到相应的方法,具体取决于您运行的操作系统。对于Windows,select实现似乎有时会返回负假值。 但这不应该伤害您,因为您应该从下一次调用waitForReadyRead()中获取可用数据的提示。 QSslSocket的waitForReadyRead()在某些SSL检查之外内部使用了QAbstactSocket的实现。
关于信号和槽的问题。 我刚开始使用Qt时犯了一个错误,那就是在调用QApplication::exec()或其他内容之前尝试信号。 没有运行循环,信号槽机制无法工作。
希望这能给您一些提示。
问候

0

虽然这不是对问题的确切回答,但我发布了一个可能的waitForReadyRead()实现,可以在本地事件循环下使用。

class MySslSocket : QSslSocket
{
  Q_OBJECT 
public:
  virtual
  bool
  waitForReadyRead (int milliseconds) override final
  {
    QEventLoop eventLoop;
    QTimer timer;
    connect(this, SIGNAL(readyRead()), &eventLoop, SLOT(quit()));
    connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
    timer.setSingleShot(true);
    timer.start(milliseconds);

    eventLoop.exec();
    return timer.isActive();
  }
};

这可以专门用于Windows,也可以用于所有平台。


这绝对不是一个好的解决方案。你可以参考这个问题了解更多细节。 - Mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接