Qt使用Keepalive機制與斷線重連
QTcpSocket
能夠檢測到Socket的連接與斷開狀態(tài),并觸發(fā)相關(guān)信號,我們只需要關(guān)聯(lián)信號與槽就能夠知道連接狀態(tài)。
還有一些特殊情況是無法觸發(fā)QTcpSocket::disconnected
信號,比如說:網(wǎng)線突然拔了、對端設(shè)備突然爆掉了等。這類情況由于對端socket未正常調(diào)用close()
方法而導(dǎo)致的。
我們可以定義一個心跳包去定期檢查對端的存活狀態(tài),這種做法在協(xié)議還未指定的初期是比較適合的,客戶端與服務(wù)端制定一套心跳請求與應(yīng)答機制來判斷對端的存活狀態(tài)。但是往往下位機的程序已經(jīng)存在(開發(fā)者不愿意修改或增加現(xiàn)有協(xié)議),這時候如何能夠在特殊情況下檢測到網(wǎng)絡(luò)斷開呢?

主角登場:Keepalive機制
keepalive簡介(摘自維基百科)
傳輸控制協(xié)議(TCP)存活包為可選特性,且默認關(guān)閉。存活包內(nèi)沒有數(shù)據(jù)。在以太網(wǎng)網(wǎng)絡(luò)中,存活包的大小為最小長度的幾幀(64字節(jié))。協(xié)議中,還有三個與存活包相關(guān)的參數(shù):
存活時長(英語:Keepalive time)即空閑時,兩次傳輸存活包的持續(xù)時間。TCP存活包時長可手動配置,默認不少于2個小時。
存活間隔(英語:Keepalive interval)即未收到上個存活包時,兩次連續(xù)傳輸存活包的時間間隔。
存活重試次數(shù)(英語:Keepalive retry)即在判斷遠程主機不可用前的發(fā)送存活包次數(shù)。當(dāng)兩個主機透過TCP/IP協(xié)議相連時,TCP存活包可用于判斷連接是否可用,并按需中斷。
多數(shù)支持TCP協(xié)議的主機也同時支持TCP存活包。每個主機按一定周期向其他主機發(fā)送TCP包來請求回應(yīng)。若發(fā)送主機未收到特定主機的回應(yīng)(ACK),則將從發(fā)送主機一側(cè)中斷連接。 若其他主機在連接關(guān)閉后發(fā)送TCP存活包,關(guān)閉連接的一方將發(fā)送RST包來表明舊連接已不可用。其他主機將關(guān)閉它一側(cè)的連接以新建連接。
空閑的TCP連接通常會隔每45秒或60秒發(fā)送一次存活包。在未連續(xù)收到三次ACK包時,連接將中斷。此行為因主機而異,如默認情況下的Windows主機將在7200000ms(2小時)后發(fā)送首個存活包,隨后再以1000ms的間隔發(fā)送5個存活包。若任意存活包未收到回應(yīng),連接將被中斷。

Qt開啟Keepalive(Linux與Windows)
Socket文件描述符的獲取
QAbstractSocket::socketDescriptor()
,在socket連接成功后可使通過m_socket->socketDescriptor();
獲取到QTcpSocket的文件描述符(FD),失敗時返回-1
,這邊獲取到的fd可以提供給int enableKeepalive(int fd);
作為參數(shù)用于啟用keepalive
。
文檔介紹:
Returns the native socket descriptor of the QAbstractSocket object if this is available; otherwise returns -1.
If the socket is using QNetworkProxy, the returned descriptor may not be usable with native socket functions.
The socket descriptor is not available when QAbstractSocket is in UnconnectedState.
斷線重連
在開啟keepalive后我們就可以在鏈路斷開時觸發(fā)QTcpSocket::disconnected
信號了,這時候我們只需要開啟一個定時器去試試檢查QTcpSocket
的狀態(tài),當(dāng)狀態(tài)為QAbstractSocket::UnconnectedState
時清理QTcpSocket
資源并嘗試重新與服務(wù)端建立連接即可。