2017-06-14 3769 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