TCP KeepAlive機制理解與實踐小結


0 前言

本文將主要通過抓包並查看報文的方式學習TCP KeepAlive機制,以此加深理解。

 

1 TCP KeepAlive機制簡介

TCP長連接下,客戶端和服務器若長時間無數據交互情況下,若一方出現異常情況關閉連接,抑或是連接中間路由出於某種機制斷開連接,而此時另一方不知道對方狀態而一直維護連接,浪費系統資源的同時,也會引起下次數據交互時出錯。

為了解決此問題,引入了TCP KeepAlive機制(並非標准規范,但操作系統一旦實現,默認情況下須為關閉,可以被上層應用開啟和關閉)。其基本原理是在此機制開啟時,當長連接無數據交互一定時間間隔時,連接的一方會向對方發送保活探測包,如連接仍正常,對方將對此確認回應。

關於TCP KeepAlive機制的詳細背景可以參考《TCP/IP詳解 卷1:協議》一書,在此不詳細贅述。

 

2 TCP KeepAlive設置參數和報文格式簡介

2.1 TCP KeepAlive參數

TCP KeepAlive機制主要涉及3個參數:

  • tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
    The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep-alives are sent only when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2 hours). An idle connection is terminated after approximately an additional 11 minutes (9 probes an interval of 75 seconds apart) when keep-alive is enabled.
    在TCP保活打開的情況下,最后一次數據交換到TCP發送第一個保活探測包的間隔,即允許的持續空閑時長,或者說每次正常發送心跳的周期,默認值為7200s(2h)。

  • tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
    The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no response is obtained from the other end.
    在tcp_keepalive_time之后,最大允許發送保活探測包的次數,到達此次數后直接放棄嘗試,並關閉連接,默認值為9(次)。

  • tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)
    The number of seconds between TCP keep-alive probes.
    在tcp_keepalive_time之后,沒有接收到對方確認,繼續發送保活探測包的發送頻率,默認值為75s。

 

2.2 TCP KeepAlive報文格式

TCP KeepAlive探測報文是一種沒有任何數據,同時ACK標志被置上的報文,報文中的序列號為上次發生數據交互時TCP報文序列號減1。比如上次本端和對端數據交互的最后時刻,對端回應給本端的ACK報文序列號為 N(即下次本端向對端發送數據,序列號應該為N),則本端向對端發送的保活探測報文序列號應該為 N-1。 在本文第四節,將通過抓包的方式再次介紹TCP KeepAlive報文。

 

3 TCP KeepAlive的配置

3.1 系統內核參數配置

Linux內核提供了通過sysctl命令查看和配置TCP KeepAlive參數的方法。

  • 查看當前內核TCP KeepAlive參數
    sysctl net.ipv4.tcp_keepalive_time
    sysctl net.ipv4.tcp_keepalive_probes
    sysctl net.ipv4.tcp_keepalive_intvl
  • 修改TCP KeepAlive參數
    sysctl net.ipv4.tcp_keepalive_time=3600

     

3.2 C語言socket設置

對於Socket而言,可以在程序中通過socket選項開啟TCP KeepAlive功能,並配置參數。對應的Socket選項分別為SO_KEEPALIVETCP_KEEPIDLETCP_KEEPCNTTCP_KEEPINTVL

int keepalive = 1;          // 開啟TCP KeepAlive功能
int keepidle = 7200;        // tcp_keepalive_time
int keepcnt = 9;            // tcp_keepalive_probes
int keepintvl = 75;         // tcp_keepalive_intvl

setsockopt(socketfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
setsockopt(socketfd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof (keepidle));
setsockopt(socketfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof (keepcnt));
setsockopt(socketfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof (keepintvl));

 

4 抓包實踐分析

4.1 實踐環境設置

將利用C語言分別編寫服務器側的代碼和客戶端的代碼,在服務器側開啟TCP KeepAlive功能,服務器的端口號設置為6699,客戶端的端口號為內核自動分配,並且每次客戶端重啟后內核分配的端口號可能不同,因此只需記住服務器端口號即可,另外一個相對的就是客戶端。

服務器側TCP KeepAlive參數具體設置如下:

tcp_keepalive_time = 55s
tcp_keepalive_probes = 2
tcp_keepalive_intvl = 6s

客戶端運行於個人MAC電腦上,服務器運行在另外一台Linux系統上,后續通過wireshark軟件進行抓包也是在MAC電腦上進行抓包。

 

4.2 wireshark抓包分析

將服務器開啟,並通過客戶端與之建立連接,過程中客戶端和服務器無任何數據交互。通過wireshark抓包分析客戶端和服務器間的數據交互,如下圖:
tcp_keepalive_1

可以看到,客戶端和服務器建立連接后,服務器每隔55s發送了一次TCP KEEPALIVE的探測報文,也驗證了上面服務器tcp_keepalive_time的設置,客戶端收到探活報文后,會作出回應。點擊具體報文可以查看報文詳情,如下圖所示:
tcp_keepalive_2

tcp_keepalive_3

本文2.2節中介紹過TCP KEEPALIVE探活報文的序列號為上次發生數據交互時TCP報文序列號減1,實踐中客戶端和服務器在建立連接三次握手之后,沒有發生任何數據交換,因此服務器收到客戶端發送的最后報文的ACK值為1(注意wireshark抓包的報文序列號為相對序列號,Relative Sequence Number),所以服務器發送保活探測報文的序列號應該為0,客戶端收到服務器探活報文后回應的確認報文序列號為1,和wireshark實際抓包相符合。

4.3 進一步測試

上面的測試中驗證了tcp_keepalive_time參數,接下來驗證TCP KeepAlive機制中另外兩個參數。為了模擬由於連接中間路由等異常導致探活報文收不到回應的場景,借助Linux iptables命令設置防火牆,阻斷TCP探活報文的傳輸。

這里將來自客戶端的報文丟棄,即模擬服務器發送TCP KEEPALIVE探活報文后遲遲收不到回應報文的場景。使用的命令為:sudo iptables -A INPUT -p tcp --sport $YOUR_CLIENT_PORT -j DROP,其中sport為系統為客戶端分配的端口號。

注意服務器是運行在Linux系統上的,客戶端和wireshark運行在本地MAC電腦上,因此利用上述iptables設置防火牆規則后,wireshark仍能抓到所有確認報文,只是在Linux系統這一側通過軟件防火牆將報文丟棄了。

設置報文過濾規則后,再次通過wireshark抓包,如下圖:
tcp_keepalive_4

從捕捉到的報文可以看到,3024.35s時間戳開始,服務器發送TCP KEEPALIVE探活報文后,一直收不到確認報文的返回,便隔6秒重新發送一個探活報文,即上文提到的服務器tcp_keepalive_intvl參數的設置。再等待2個tcp_keepalive_intvl時間間隔后,服務器仍未收到確認報文后,服務器發送了RST報文,以釋放網絡連接資源,這里的2次即tcp_keepalive_probes設置的。

注意:在完成上述測試后,借助sudo iptables -F命令清除所有的防火牆規則。

 

4 總結

本文借助wireshark軟件,對TCP KEEPALIVE報文進行抓包分析,分析了TCP保活機制中tcp_keepalive_timetcp_keepalive_probestcp_keepalive_intvl三個參數的實際效果,加深了對TCP KEEPALIVE機制的理解。

 

參考資料

[1] About TCP keepalive: (http://baotiao.github.io/tech/2015/09/25/tcp-keepalive/)
[2] https://www.codenong.com/cs105424711/


免責聲明!

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



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