可以使用 `waitForReadyRead()` 而不是为 `readyRead()` 信号创建一个插槽吗?
Posted
技术标签:
【中文标题】可以使用 `waitForReadyRead()` 而不是为 `readyRead()` 信号创建一个插槽吗?【英文标题】:Is it OK to use `waitForReadyRead()` instead of creating a slot for `readyRead()` signal? 【发布时间】:2017-06-14 12:21:44 【问题描述】:使用 Qt(包括带有 MinGW 的 Windows)编写跨平台应用程序。为了从 SSL 套接字读取数据,我正在创建一个单独的线程。由于历史原因,该线程存在,因为之前该应用程序是使用 C 套接字/ssl/crypto 库编写的。现在所有这些都被 Qt Network 库所取代。
对于阻塞线程,waitForReadyRead(milliseconds)
似乎是更好的选择。现在根据 Qt 层次结构:
QIODevice
|
QAbstractSocket
|
QTcpSocket
|
QSslSocket
QAbscractSocket::waitForReadyRead()
的文档建议:
注意:此功能在 Windows 上可能会随机失败。如果您的软件将在 Windows 上运行,请考虑使用事件循环和 readyRead() 信号。
但是QIODevice::waitForReadyRead()
中没有提到类似的警告。
问题:QSslSocket::waitForReadyRead()
是否始终可用于所有平台?
为什么我不使用 readyRead() 信号?出于某种奇怪的原因,如果我使用 readyRead() 插入某个方法然后它不会被调用。此外, QSslSocket::write() 也不起作用,否则与上述方法一起工作。由于我的代码很复杂,我无法在这里展示它。
【问题讨论】:
您声明您正在使用线程。如果readyRead
发射器和接收器在不同的线程上,你确定接收器的线程有一个活动的事件循环吗?顺便说一句,我不禁觉得使用waitForReadyRead
只会暂时掩盖真正潜在问题的症状。
@G.M.最初只有 1 个线程建立 TLS 连接并与服务器交换几个自定义消息(读取和写入)。只有当这成功时,我才开始一个新的循环用于独占读取目的。对于readyRead()
,我确信我这边一定存在编码/理解错误,才能让它发挥作用。但是,如果我必须使用 waitFor...()
方法,那么我当前的设计可以自行运行。唯一担心的是它的 Windows 实现。另一个较小的问题是,有时 Qt readyRead()
在多个连续数据消息之后发出。 socket-select()
在这里更快。
【参考方案1】:
对于您的问题:是的,您可以使用QSslSocket::waitForReadyRead()
,但在 Widows 上,即使数据到达套接字,它也会超时。因此,如果发生超时,您必须检查它是超时还是方法失败。检查很简单,只要QAbstractSocket::bytesAvailable() > 0
则数据已准备好读取,否则超时。
当您使用小超时并且您的应用程序对延迟不敏感(例如,温度传感器和具有温度历史记录的云之间的通信)时,这种方法是可以的。但是如果你不能接受任何不必要的延迟,那么你应该使用信号/槽接口。
有关更多信息,您可以查看 Qt 错误跟踪器上的 bug report。
【讨论】:
【参考方案2】:根据您的问题。 QIODevice 的实现除了返回 false 什么都不做。因此,不需要有时会失败的暗示。 QAbstractSocket 的实现在内部调用称为“nativeSelect”的东西,然后根据您运行的操作系统将其定向到相应的方法。对于 Windows,select 实现有时似乎返回一个否定的 false。 但这不应该对您造成伤害,因为您应该从下一次调用 waitForReadyRead() 中获得可用数据的提示。 QSslSocket 的 waitForReadyRead() 内部使用来自一些 SSL 检查的 QAbstactSocket 的实现装置。
关于信号和插槽的问题。 我刚接触 Qt 时犯的一个错误是,我试图在启动 MainLoop 之前通过调用 QApplication::exec() 或其他方法来发出信号。 如果没有运行循环,信号槽机制将无法工作。
希望你能从中得到一些提示。
问候
【讨论】:
【参考方案3】:问题可能是使用资源。
当您使用waitForReady*
时,您会为每个线程创建一个套接字(否则您会遇到奇怪的错误)。
现在的问题是你有多少个插座?如果它取决于运行时数据,您可能不知道。
一些嵌入式系统对可能影响您的应用程序的踏板数量有限制,而 IMO 这只是可能影响此类实施的限制。
这部分问题:
为什么我不使用 readyRead() 信号?出于某种奇怪的原因,如果我 使用 readyRead() 插入一些方法,然后它不会被调用。 此外, QSslSocket::write() 也不起作用,它有效 否则采用上述方法。由于我的代码的复杂性,我 无法在这里展示。
看起来很可疑。 我从来没有见过有人有类似的问题。也许您的代码的某些部分阻塞了事件循环?
【讨论】:
我已经编辑了可疑部分,因为这是我的编码错误。目前我只使用 1 个套接字。可能是 Qn 现在更特定于wait..()
方法。【参考方案4】:
虽然这不是 Qn 的确切答案,但我发布了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,也可以普遍用于所有平台。
【讨论】:
这当然不是一个好的解决方案。您可以参考这个question了解更多详情。以上是关于可以使用 `waitForReadyRead()` 而不是为 `readyRead()` 信号创建一个插槽吗?的主要内容,如果未能解决你的问题,请参考以下文章
使用QProcess,是不是需要在waitForFinished之后调用waitForReadyRead?
子线程中的 waitForReadyRead() 冻结 GUI 线程