2017-06-14 3778 views
4

使用Qt编写跨平台应用程序(包括带有MinGW的Windows)。为了从SSL套接字读取数据,我创建了一个单独的线程。此线程出于历史原因,因为此前的应用程序是使用C socket/ssl/crypto库编写的。现在所有这些都被Qt网络库所取代。可以使用`waitForReadyRead()`而不是为`readyRead()`信号创建一个插槽吗?

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

QIODevice 
    | 
QAbstractSocket 
    | 
QTcpSocket 
    | 
QSslSocket 

QAbscractSocket::waitForReadyRead()文档建议:

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

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

问题:是否QSslSocket::waitForReadyRead()始终可用于所有平台?


为什么我不能用readyRead()信号?
由于一些奇怪的原因,如果我插入一些readyRead()方法,那么它不会被调用。此外,QSslSocket :: write()也不起作用,否则以上述方式起作用。由于我的代码复杂,我无法在此处显示。

+0

您声明您正在使用线程。如果'readyRead'发射器和接收器在不同的线程上,你确定接收器的线程有一个活动的事件循环?顺便说一句,我不禁觉得使用'waitForReadyRead'只会暂时掩盖真正潜在问题的症状。 –

+0

@ G.M。最初只有1个线程可以进行TLS连接并与服务器交换一对自定义消息(读写)。只有在这种情况下,我才会开始一个新的循环来实现独家阅读目的。对于'readyRead()',我确信必须从我身边编码/理解错误而不让它工作。但是,如果我必须使用'waitFor ...()'方法,那么我目前的设计就可以自行完成。唯一的担心是它的Windows实施。另一个较小的问题是,有时Qt'readyRead()会在多个连续的数据消息之后发出。 'socket-select()'在这里更快。 – iammilind

回答

4

对于你的问题:是的,你可以使用QSslSocket::waitForReadyRead()但在寡妇它甚至可以超时,即使数据来到套接字。所以如果发生超时,你必须检查它是否超时或方法失败。检查很简单,只要QAbstractSocket::bytesAvailable() > 0然后数据准备好读取,否则它超时。

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

欲了解更多信息,你可以看看Qt的错误跟踪器bug report

1

根据你的问题。 QIODevice的实现只会返回false。所以不需要有时会失败的暗示。 QAbstractSocket的实现在内部调用了一个名为“nativeSelect”的东西,然后将其定向到相应的方法,具体取决于您正在运行的操作系统。对于Windows,select实现有时似乎返回否定的错误。 但是这不应该伤害你,因为你应该从下次调用waitForReadyRead()时得到可用数据的提示。 QSslSocket的waitForReadyRead()内部使用QAbstactSocket的实现appart从一些SSL检查。

关于您的信号和插槽的问题。 我刚接触Qt时犯的一个错误是,我试图在通过调用QApplication :: exec()或其他方法启动MainLoop之前发出信号。 没有运行循环,信号插槽机制不起作用。

希望你能从中得到一些提示。

问候

0

虽然它不是一个确切的答案,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,也可以通用于所有平台。

+0

这当然不是一个好的解决方案。有关更多详细信息,请参阅此[问题](https://stackoverflow.com/q/35561182/2666212)。 – Mike

1

问题可能是使用资源。

当您使用waitForReady*时,您为每个线程创建约束一个套接字(否则您将有奇怪的错误)。 现在问题是你有多少个插座?如果它依赖于运行时数据,则可能不知道。 一些嵌入式系统可能会影响您的应用程序和IMO的行程数限制,这只是影响此类实施的限制。

你的问题的这部分:

为什么我不能用readyRead()信号?出于某种奇怪的原因,如果我插入readyRead()的某个方法,那么它不会被调用。此外,QSslSocket :: write()也不起作用,否则以上述方式工作 。由于我的代码的复杂性,我 无法在此处显示。

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

+0

我编辑了这个可疑的部分,因为这是我的编码错误。目前我只使用1个插座。可能是Qn现在更具体到'wait ..()'方法。 – iammilind