Qt QTcpSocket 對連線伺服器中斷的不同情況進行判定

wuyuan2011woaini發表於2024-03-26

簡述

對於一個C/S結構的程式,客戶端有些時候需要實時得知與伺服器的連線狀態。而對於客戶端與伺服器斷開連線的因素很多,現在就目前遇到的情況進行一下總結。
分為下面六種不同情況
  1. 客戶端網線斷開
  2. 客戶端網路斷開
  3. 客戶端透過HTTP代理連線伺服器,代理機器斷開代理
  4. 客戶端透過HTTP代理連線伺服器,代理機器的網路斷開
  5. 客戶端透過HTTP代理連線伺服器,代理機器的網線斷開
  6. 伺服器斷開

同時對於以上六種情況又分為連線伺服器之前和連線上伺服器之後,下面就分別對不同的情況進行分析。

開始連線伺服器之前

1、 客戶端網線斷開
此時用socket呼叫connectToHost方法連線伺服器會立即觸發QTcpSocket的error訊號,我們可以繫結相應的槽去處理連線失敗的結果。
2、 客戶端網路斷開
3、 客戶端透過HTTP代理連線伺服器,代理機器斷開代理
4、 客戶端透過HTTP代理連線伺服器,代理機器的網路斷開
5.、客戶端透過HTTP代理連線伺服器,代理機器的網線斷開
6、 伺服器斷開
此時用socket呼叫connectToHost方法連線伺服器並不會立即觸發QTcpSocket的error訊號,而是經過40s+的連線等待超時發出error訊號,見下圖。

已經連線上伺服器

1、 客戶端網線斷開
此時socket不會傳送error訊號,也不會傳送disconnect訊號,查詢資料是因為網線斷開是屬於物理鏈路層,tcp無法察覺到,socket仍處於連線狀態。
2、 客戶端網路斷開
3、 客戶端透過HTTP代理連線伺服器,代理機器斷開代理
4、 客戶端透過HTTP代理連線伺服器,代理機器的網路斷開
5.、客戶端透過HTTP代理連線伺服器,代理機器的網線斷開
第二和第三種情況下會立即觸發error訊號,而第四和第五種情況下會等待30s左右會傳送error訊號。
6、 伺服器斷開
此時socket會傳送disconnect訊號,可以繫結相應的槽去處理伺服器斷開的情況。

檢測與伺服器斷開的另外方法

對於有些程式(客戶端)需要立即知道與服務端連線狀態,而不是等待幾十秒之後才有訊號通知到或者根本就檢測不出與伺服器斷開,除了利用QTcpSocket提供的訊號(有幾種情況不會發出訊號或發出訊號延遲),這裡列出另外幾種處理方法。
1、傳送心跳包,即客戶端每隔一段時間傳送一條報文,報文不需附帶具體內容,只需要讓服務端知道這是一條心跳報文,並回發一條訊息,客戶端收到這條訊息後就得知與伺服器保持連線的狀態。
檢測本地網路,定義一個時鐘,每次timeout去檢測本地的網路,關於怎麼判斷本地網路是否通暢呢?
2、可以用windows提供的IsNetworkAlive方法,返回為false為網路異常。加上標頭檔案為#include “Sensapi.h”。同時需要包含Sensapi.lib。
(透過IsNetworkAlive方法判斷本地網路,在客戶端已經連線上伺服器,並且禁用網路時會立即傳送error訊號,在error訊號繫結的槽中去呼叫這個方法發現返回值為true,因為這種情況下禁用網路後會立即傳送error訊號,呼叫IsNetworkAlive方法時可能立即檢測不到網路異常。如果透過斷點的方式,在呼叫IsNetworkAlive時就會返回false)

1     DWORD dwFlag;
2     if (FALSE == IsNetworkAlive(&dwFlag))
3     {
4         qDebug() << "NetWorkError";
5     }

注意:
但是這種方法,在本地存在虛擬機器並且虛擬機器開啟時會失效,因為IsNetworkAlive會檢測本地所有的網路,在網線斷開後,可能檢測到虛擬機器網路正常,導致返回ture。
3、如果有自己的伺服器就ping伺服器(前提伺服器不會掛),否則就ping一個相對可靠的IP (比如百度),透過看他ping的結果怎麼樣.
同時在C++ 實現 ping 功能&& 域名(URL)解析實際 IP地址 這篇部落格中用C++實現了 ping的 功能,有興趣的小夥伴可以看一看,瞭解一下。

1 QProcess *cmd = new QProcess;
2 cmd->start("ping www.baidu.com");
3 // 等待ping 的結果
4 while (cmd->waitForFinished())
5 {
6     QString result = QString::fromLocal8Bit(cmd->readAll());
7     qDebug() << result;
8 }

1 QHostInfo::lookupHost("www.baidu.com", this, SLOT(lookedUp(QHostInfo)));
2 
3 void lookedUp(QHostInfo &host)
4 {
5      qDebug() << host.addresses().first().toString();
6 }
7 //得到IP 地址 就是在網際網路上 如果不能得到 就不行

4、QNetworkConfigurationManager::isOnline()。
當然這個只能檢查你是否有網路連結,而不能檢測你是否連線到網際網路。

對於需要自動重連的客戶端可以透過以上方法,在判斷出與伺服器斷開後可以重新連線,或者透過超時定時器進行重連,方法很多,在於嘗試。

相關文章