TCP KeepAlive
Wireshark抓包分析機制
--------------------------------
如上圖所示,TCP保活報文總是成對出現,包括TCP保活探測報文和TCP保活探測確認報文。
TCP保活探測報文是將之前TCP報文的確認序列號減1,並設置1個字節,內容為“00”的應用層數據,如下圖所示:
TCP保活探測報文
TCP保活探測確認報文就是對保活探測報文的確認,其報文格式如下:
因為Websocket通過Tcp Socket方式工作,現在考慮一個問題,在一次長連接中,服務器怎么知道消息的順序呢?這就涉及到tcp的序列號(Sequence Number)和確認號(Acknowledgment Number)。我的理解是序列號是發送的數據長度;確認號是接收的數據長度。這樣講比較抽象,我們從TCP三次握手開始(結合下圖)詳細分析一下。
包1:
TCP會話的每一端的序列號都從0開始,同樣的,確認號也從0開始,因為此時通話還未開始,沒有通話的另一端需要確認
包2:
服務端響應客戶端的請求,響應中附帶序列號0(由於這是服務端在該次TCP會話中發送的第一個包,所以序列號為0)和相對確認號1(表明服務端收到了客戶端發送的包1中的SYN)。需要注意的是,盡管客戶端沒有發送任何有效數據,確認號還是被加1,這是因為接收的包中包含SYN或FIN標志位。
包3:
和包2中一樣,客戶端使用確認號1響應服務端的序列號0,同時響應中也包含了客戶端自己的序列號(由於服務端發送的包中確認收到了客戶端發送的SYN,故客戶端的序列號由0變為1)此時,通信的兩端的序列號都為1。
包4:客戶端——>服務器
這是流中第一個攜帶有效數據的包(確切的說,是客戶端發送的HTTP請求),序列號依然為1,因為到上個包為止,還沒有發送任何數據,確認號也保持1不變,因為客戶端沒有從服務端接收到任何數據。需要注意的是,包中有效數據的長度為505字節
包5:客戶端<-----服務器
當上層處理HTTP請求時,服務端發送該包來確認客戶端在包4中發來的數據,需要注意的是,確認號的值增加了505(505是包4中有效數據長度),變為506,簡單來說,服務端以此來告知客戶端端,目前為止,我總共收到了506字節的數據,服務端的序列號保持為1不變。
包6:客戶端<-----服務器
這個包標志着服務端返回HTTP響應的開始,序列號依然為1,因為服務端在該包之前返回的包中都不帶有有效數據,該包帶有129字節的有效數據。
包7:
由於上個數據包的發送,TCP客戶端的確認序列號增長至130,從服務端接收了129字節的數據,客戶端的確認號由1增長至130
理解了序列號和確認序列號是怎么工作的之后,我們也就知道“TCP保活探測報文是將之前TCP報文的確認序列號減1,並設置1個字節數據”為什么要這么搞了。
序列號減一再加一:是為了保證一次連接中keep alive不影響序列號和確認序列號。Keep alive 中的1byte 00的數據並不是真正要傳遞的數據,而是tcp keep alive約定俗稱的規則。
linux中TCP Keepalive 超時時長的配置
[root@zmdsdkhost ~]# ls -l /proc/sys/net/ipv4/tcp_keepalive_* -rw-r--r-- 1 root root 0 May 18 11:27 /proc/sys/net/ipv4/tcp_keepalive_intvl -rw-r--r-- 1 root root 0 May 18 11:27 /proc/sys/net/ipv4/tcp_keepalive_probes -rw-r--r-- 1 root root 0 May 6 17:27 /proc/sys/net/ipv4/tcp_keepalive_time
前兩個參數以秒為單位,最后一個是純數字。這意味着,在發送第一個Keepalive探針之前,Keepalive例程要等待兩個小時(1200秒),然后每75秒重新發送一次。如果連續九次未收到ACK響應,則將該連接標記為斷開。
修改此值很簡單:您需要將新值寫入文件。假設您決定配置主機,以使保持活動狀態在十分鍾的通道不活動之后開始,然后每隔一分鍾發送一次探測。由於網絡干線的高度不穩定和間隔值較低,假設您還希望將探測數量增加到20個。
這是我們更改設置的方法:
# echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time # echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl # echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probes
sysctl 工具設置的方式:
命令行查看: #sysctl \ net.ipv4.tcp_keepalive_time \ net.ipv4.tcp_keepalive_intvl \ net.ipv4.tcp_keepalive_probes #輸出 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_keepalive_intvl = 75 net.ipv4.tcp_keepalive_probes = 9 #命令行設置 # sysctl -w \ net.ipv4.tcp_keepalive_time=600 \ net.ipv4.tcp_keepalive_intvl=60 \ net.ipv4.tcp_keepalive_probes=20 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 60 net.ipv4.tcp_keepalive_probes = 20
持久化配置方法
編輯配置文件/etc/sysctl.conf 【vim】
# vim /etc/sysctl.conf net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 60 net.ipv4.tcp_keepalive_probes = 20 :wq 保存退出 #sysctl -p 刷新配置生效
HTTP協議的長連接 Keep-Alive模式:
我們知道Http協議采用“請求-應答”模式,當使用普通模式,即非Keep-Alive模式時,每個請求/應答,客戶端和服務器都要新建一個連接,完成之后立即斷開連接;當使用Keep-Alive模式時,Keep-Alive功能使客戶端到服務器端的連接持續有效,當出現對服務器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。
http1.0中默認是關閉的,需要在http頭加入”Connection: Keep-Alive”,才能啟用Keep-Alive;
http 1.1中默認啟用Keep-Alive,如果加入”Connection: close “才關閉。目前大部分瀏覽器都是用http1.1協議,也就是說默認都會發起Keep-Alive的連接請求了,所以是否能完成一個完整的Keep- Alive連接就看服務器設置情況。下圖是普通模式和長連接模式的請求對比:
開啟Keep-Alive的優缺點:
優點:Keep-Alive模式更加高效,因為避免了連接建立和釋放的開銷。
缺點:長時間的Tcp連接容易導致系統資源無效占用,浪費系統資源。
Keep-Alive timeout設置:
Httpd守護進程,一般都提供了keep-alive timeout時間設置參數。比如nginx的keepalive_timeout,和Apache的KeepAliveTimeout。這個keepalive_timout時間值意味着:一個http產生的tcp連接在傳送完最后一個響應后,還需要hold住keepalive_timeout秒后,才開始關閉這個連接。
當httpd守護進程發送完一個響應后,理應馬上主動關閉相應的tcp連接,設置 keepalive_timeout后,httpd守護進程會想說:”再等等吧,看看瀏覽器還有沒有請求過來”,這一等,便是keepalive_timeout時間。如果守護進程在這個等待的時間里,一直沒有收到瀏覽器發過來http請求,則關閉這個http連接【控制服務器,發送TCP協議斷開請求:FIN數據包】。
如下以nginx為例
vim /etc/nginx/nginx.conf keepalive_timeout 65;
抓包觀察
參考文檔:
http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#configuringkernel
https://www.cnblogs.com/zxmbky/p/10281152.html