QTcpSocket使用過程中的一些問題記錄


目前,在將原來C的socket通訊改為使用Qt類庫QTcpSocket通訊,在修改過程中遇到不少問題,在此將問題一並記錄,以備后面使用。

 

        采用的通訊方式:QTimer定時器、QThread多線程和QTcpSocket通訊。QTimer設置定時通訊間隔(10s),QThread運行定時器響應函數,QTcpSocket進行數據發送。

1.問題一:QTcpSocket對象創建和使用要在同一個線程,否則報錯。

錯誤描述:QObject:Cannot create children for a parent that is in a different thread.

問題描述:開始是將QTcpSocket對象作為主界面的一個成員變量,因此在構造函數內初始化(new),然后在QThread線程內連接服務端以及進行數據發送。

問題解決:將初始化和連接都放在QThread內,並建立一個長連接,並采用一個標志位表示連接狀態,發送前判斷連接標志位,以此來進行連接或者發送。

me:連接標志一定要與QTcpSocket的QAbstractSocket::SocketState狀態進行關聯,可使用信號stateChanged(QAbstractSocket::SocketState)建立連接,在槽函數里修改連接標志

如:

connect(pTcpClientSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,
            SLOT(sltStateChanged(QAbstractSocket::SocketState)),Qt::DirectConnection);
.....
void CommTcpClient::sltStateChanged(QAbstractSocket::SocketState state)
{
    qDebug()<<"TcpSocketState------"<<state;
    switch(state)
    {
        case QAbstractSocket::ConnectedState:
        case QAbstractSocket::ConnectingState:
        case QAbstractSocket::ClosingState:
            isOpen=true;//isOpen即為連接標志
            emit sigCommStateUpdate(state);
            break;
        case QAbstractSocket::UnconnectedState:
            isOpen=false;
            emit sigCommStateUpdate(state);
            break;
        default:
            break;
    }
}

2.問題二:在QThread內將QTcpSocket對象信號(connected、error、stateChangeed)與槽函數連接報數據類型未注冊

錯誤描述:QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketError' (Make sure ‘QAbstractSocket::SocketError' is registered using qRegisterMetaType(). )

問題描述:connect自動連接在線程類中的信號默認是排隊模式QueuedConnection,因此需要注冊參數類型。

問題解決:方法一,將默認connect的連接方式改為Qt::DirectConnection,就不需要類型信息;方法二,在線程內注冊qRegisterMetaType參數類型。

me:不建議采用方法一,治標不治本。采用方法二,具體參考:http://www.cnblogs.com/liushui-sky/p/6422643.html

 

3.問題三:調用connectToHost建立連接后調用write發送數據失敗

錯誤描述:QNativeSocketEngine::write() was not called in QAbstractSocket::ConnectedState

問題描述:錯誤為不是在QTcpSocket連接狀態下調用write()函數,即調用write()時連接尚未建立

問題解決:方法一,在調用connectToHost后接着調用waitForConnected(timeout),並給延時參數賦值,之后在調用write()函數;

              方法二,在信號connected的槽函數里調用write()函數進行數據發送;

              方法三,在信號stateChangeed的槽函數內判斷當前QTcpSocket對象連接狀態state(),如果是QAbstractSocket::ConnectedState則調用write()函數進行數據發送;

實際是使用一個連接標志位來決定是否調用write()函數發送數據,在信號connected的槽函數里修改標志位為真,每次定時器到期都首先判斷標志位,為假則重新嘗試建立連接。

me:建議方法一方法三結合使用。stateChangeed信號在網線斷開的時候有個大概20s的延時,在這20s內socket連接還是在的。這個需要注意。

 

4.問題四:服務端沒開啟和網線沒連接錯誤不一樣

錯誤描述:服務端沒開啟錯誤為:QTcpSocket Connection Refused,網線沒連接錯誤為:Network operation timed out

問題描述:服務端沒開啟返回連接被拒絕,網線沒連接則會超時,這個可能和網絡情況有關系。。。

問題解決:不清楚具體原因,將上述現象作為一個判斷區分兩者的方法。

 

5.問題五:連接關閉

錯誤描述:直接調用disconnect,錯誤信息忘記了。。。

問題描述:

問題解決:改為調用disconnectFromHost()函數,這個函數會等待這個連接的相關操作(數據發送)完成后才關閉連接。

me:調用完disconnectFromHost函數后,在使用waitFromDisconnected進行等待,跟建立連接時采用的waitForConnected是一個道理。

 

6.問題六:保活keepalive

錯誤描述:不發keepalive包

問題描述:對QTcpSocket.socketDescriptor做keepalive配置,在連接成功前配置無效

問題解決:改為在在connected信號槽函數內配置,然后在disconnected信號槽函數內修改連接標志。這樣可以監測到對端異常斷開的情況,但是本端網線被拔還是監測不到,這是個遺留問題

me:keepalive還沒有試,回頭試一下效果,本端網線斷開可參考http://www.cnblogs.com/liushui-sky/p/6479510.html,http://www.cnblogs.com/liushui-sky/p/6479466.html監測本地相應網絡接口的連接狀態。

 QNetworkConfigurationManager::onlineStateChanged(),在有多個網絡連接(如多個網口、無線等)效果不好,不建議使用。

 

7.問題七:心跳機制

為了確定連接是否正常,考慮使用兩種方法:

方法一:使用問題六中的保活keepalive機制,在空閑時候發送空包,接收對端確認信息,從而確認連接是否正常;

方法二:使用心跳機制,本端定時發送心跳包,然后接收對端回復的心跳包,從而確認連接是否正常;或者在本端發送后接收對端回復的確認信息。

兩種方法都是通過數據包來確認連接,相比之下方法一配置簡單,不需要修改對端代碼,但是兩端要進行交互;方法二需要修改服務端代碼(在數據接收處增加回復確認信息代碼),但是相比方法一少了一次本端向對端發送數據包。(這里本端是指客戶端,對端指服務端)

 

注:me為本人的理解,不當之處還望大家指正。

轉自:http://blog.csdn.net/u011430225/article/details/52946332


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM