本文将主要通過抓包并檢視封包的方式學習TCP KeepAlive機制,以此加深了解。
0 前言
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)
在TCP保活打開的情況下,最後一次資料交換到TCP發送第一個保活探測包的間隔,即允許的持續空閑時長,或者說每次正常發送心跳的周期,預設值為7200s(2h)。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_keepalive_probes (integer; default: 9; since Linux 2.2)
在tcp_keepalive_time之後,最大允許發送保活探測包的次數,到達此次數後直接放棄嘗試,并關閉連接配接,預設值為9(次)。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_intvl (integer; default: 75; since Linux 2.4)
在tcp_keepalive_time之後,沒有接收到對方确認,繼續發送保活探測包的發送頻率,預設值為75s。The number of seconds between TCP keep-alive probes.
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_KEEPALIVE
、
TCP_KEEPIDLE
TCP_KEEPCNT
TCP_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抓包分析用戶端和伺服器間的資料互動,如下圖:

可以看到,用戶端和伺服器建立連接配接後,伺服器每隔55s發送了一次TCP KEEPALIVE的探測封包,也驗證了上面伺服器
tcp_keepalive_time
的設定,用戶端收到探活封包後,會作出回應。點選具體封包可以檢視封包詳情,如下圖所示:
本文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抓包,如下圖:
從捕捉到的封包可以看到,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_time
tcp_keepalive_probes
tcp_keepalive_intvl
三個參數的實際效果,加深了對TCP KEEPALIVE機制的了解。
參考資料
[1] About TCP keepalive: (http://baotiao.github.io/tech/2015/09/25/tcp-keepalive/)
[2] https://www.codenong.com/cs105424711/