TCP/IP協議棧的心跳、丟包重傳、連接超時機制實例詳解
時間:2023-11-01 來源:華清遠見
TCP/IP協議棧的心跳、丟包重傳、連接超時機制實例詳解
概述
大家都使用過在線會議軟件,例如騰訊會議等。我們假設一個場景,當在開會時突然斷網,這時最常見的現象是畫面卡頓、音頻停止,也就是我們常說的掉線了。通常這種斷網一般TCPIP協議棧已經檢測到網絡異常,系統協議層已經將網絡斷開了;也可能軟件應用層的心跳機制檢測到網絡故障,斷開了與服務器的鏈接。對于系統TCPIP協議棧自身檢測出來的網絡異常,則可能存在兩種情況,一是TCPIP協議棧自身的心跳機制檢測出來的;二是TCP連接的丟包重傳機制檢測出異常。
如果用戶希望在60內如果網絡恢復,軟件能夠自動重連并繼續保持在會議中,這時可以通過TCP/IP協議棧的TCP連接的心跳、丟包重傳、連接超時等機制來完成。下面讓我們一起來了解一下它們的使用方法吧。
TCP/IP協議棧的心跳機制
ACK機制
TCP建立連接時的三次握手:

TCP連接是可靠的,在發送數據前要建立連接,收到數據后都會給對方恢復一個ACK包,表明我收到你的數據包了。對于數據發送端,如果數據發出去后沒有收到ACK包,則會觸發丟包重傳機制。不管是建立連接時,還是建立連接后的數據收發時,都有ACK包,TCP/IP協議棧的心跳包也是按照這個流程運行的。
心跳機制
TCP/IP協議棧有個默認的TCP心跳機制,這個心跳機制是和socket套接字(TCP套接字)綁定的,可以對指定的套接字開啟協議棧的心跳檢測機制。默認情況下,協議棧的心跳機制對socket套接字是關閉的,如要使用需人為開啟。在Windows中,默認是每隔2個小時發一次心跳包,客戶端程序將心跳包發給服務器后,接下來會有兩種情況:
網絡正常時:服務器收到心跳包,會立即回復ACK包,客戶端收到ACK包后,再等2個小時發送下一個心跳包。其中,心跳包發送時間間隔時間keepalivetime,Windows系統中默認是2小時,可配置。如果在2個小時的時間間隔內,客戶端和服務器有數據交互,客戶端會收到服務器的ACK包,也算作心跳機制的心跳包,2個小時的時間間隔會重新計時。
網絡異常時:服務器收不到客戶端發過去的心跳包,沒法回復ACK,Windows系統中默認的是1秒超時,1秒后會重發心跳包。如果還收不到心跳包的ACK,則1秒后重發心跳包,如果始終收不到心跳包,則在發出10個心跳包就達到了系統的上限,就認為網絡出故障了,協議棧就會直接將連接斷開了。其中,發出心跳包收不到ACK的超時時間稱為 keepaliveinterval,Windows系統中默認是1秒,可配置;收不到心跳包對應的ACK包的重發次數probe,Windows系統是固定的,是固定的10次,不可配置的。
所以TCP/IP協議棧的心跳機制能檢測出網絡異常,不過在默認配置下可能需要很久才能檢測出來,所以可以根據用戶的需求來靈活配置keepalivetime和keepaliveinterval參數。
心跳參數設置
開啟TCP/IP協議棧的默認心跳機制,不是對整個協議棧開啟心跳監測,而是對指定的socket套接字開啟。開啟心跳機制后,還可以修改心跳的時間參數。先調用setsockopt給目標套接字開啟心跳監測機制,再調用WSAIoctl去修改心跳檢測的默認時間參數,相關代碼如下所示:

上面的代碼可以看到,先調用setsockopt函數,傳入SO_KEEPALIVE參數,打開TCP連接的心跳開關,此時心跳參數使用系統默認的心跳參數值。緊接著,調用WSAIoCtrl函數,傳入SIO_KEEPALIVE_VALS參數,同時將設置好時間值的心跳參數結構體傳進去。下面對心跳參數結構體tcp_keepalive做個詳細的說明:
keepalivetime:默認2小時發送一次心跳保活包,比如發送第1個保活包之后,間隔2個小時后再發起下一個保活包。如果這期間有數據交互,也算是有效的保活包,這個時間段就不再發送保活包,發送下個保活包的時間間隔會從收發的最后一條數據的時刻開始重新從0計時。
keepaliveinterval:發送保活包后,沒有收到對端的ack的超時時間默認為1秒。假設和對端的網絡出問題了,給對端發送第1個保活包,1秒內沒有收到對端的ack,則發第2個保活包,1秒內沒有收到對端的保活包,再發送下一個保活包,.....,直到發送第10個保活包后,1秒鐘還沒收到ack回應,則達到發送10次保活包的探測次數上限,則認為網絡出問題了。
丟包重傳機制
如果網絡出故障時,客戶端與服務器之間正在進行TCP數據交互,客戶端給服務器發送數據包后因為網絡故障收不到服務器的ACK包,就會觸發客戶端的TCP丟包重傳,丟包重傳機制也能判斷出網絡出現異常。對于TCP連接,客戶端給服務器發送數據后沒有收到服務器的ACK包,會觸發丟包重傳。每次重傳的時間間隔會加倍,當重傳次數達到系統上限(Windows默認的上限是5次,Linux默認的上限是15次)后,協議棧就認為網絡出故障了,會直接將對應的連接關閉了。所以當網絡出現故障時有數據交互,協議棧會在數十秒內檢測到網路出現異常,就會直接將連接直接關閉掉。
決定報文是否有必要重傳的主要機制是重傳計時器(retransmission timer),它的主要功能是維護重傳超時(RTO)值。當報文使用TCP傳輸時,重傳計時器啟動,收到ACK時計時器停止。
報文發送至接收到ACK的時間稱為往返時間(RTT)。對若干次時間取平均值,該值用于確定最終RTO值。在最終RTO值確定之前,確定每一次報文傳輸是否有丟包發生使用重傳計時器,下圖說明了TCP重傳過程:

對于丟包重傳機制,可以通過給PC插拔網線來查看,先拔掉網線,等待幾秒鐘再將網線插上,可以發現接收端能夠接收到丟包的數據。
connect連接超時控制
對于tcp套接字,我們需要調用套接字函數connect去建立TCP連接,阻塞式的socket,在Windows下,如果遠端的IP和Port不可達,則會阻塞75s后返回SOCKET_ERROR,表明連接失敗。所以當我們測試遠端的IP和Port是否可以連接時,我們不使用阻塞式的socket,而是使用非阻塞式socket,然后調用select,通過select添加連接超時時間,實現連接超時的控制。具體實現可以參考如下代碼:


連接超時的原因有很多,例如網絡連接斷開,電腦設置等原因。多次連接失敗后可以提示用戶檢查電腦網絡設置或網線是否正確接入。

