TCP/IP学习笔记(五)TCP的保活定时器
正常情況下,TCP連接的終止需要經歷四次揮手階段,體現在代碼上就是某一端主動調用close函數關閉套接字,隨后TCP向對端發送FIN位被置為1的報文段標志著連接的結束,同時對端響應應答報文段,并在隨后的某一時刻同樣調用close函數,發送FIN報文段,當確認完成后就標志著TCP連接正常終止。
然而,考慮一種情況,在TCP連接建立成功后,客戶端主機突然崩潰(斷電,斷網等),導致客戶端的TCP還沒來得及將FIN發送給服務器就已經關閉了(或者由于斷網導致FIN無法到達對端),而服務器通常又不會主動發送數據給客戶端。在這種情況下,客戶端已經關閉,服務器卻不知道,依然保持著維護一個連接應該有的所有東西(包括文件描述符,TCP緩沖區等),一個還好,當有大量這種半開連接存在于服務器中,會造成大量的浪費
保活定時器
在TCP協議中,有一個被稱作保活定時器的組件,它就是為了解決上述問題被引進的。該定時器的原理也非常簡單,當通信雙方在定時器規定的這段時間內沒有進行數據交互,那么開啟保活選項的一端TCP就會發送一個探查報文段,當對端TCP接收到報文段發現這是個探查報文時,會響應應答信息。
上述過程是TCP獨立于應用程序進行的,應用程序不會知道探查報文的發送和到達
此外,保活定時器的定時時間是兩小時,是系統級別的參數,可以更改,但是最好不要更改,會影響到系統進程
如果一個連接在兩小時內沒有任何數據交互,則服務器就向客戶端發送一個探查報文段(假設服務器開啟保活定時器),此時客戶端主機有四中可能的狀態
- 客戶端主機依然正常運行,并從服務器可達。客戶端TCP接收到探查報文段并返回應答報文段,此時服務器知道客戶端依然存在,所以重置保活定時器。另外,如果在定時器溢出之前進行了數據交互,定時器也會被重置
- 客戶端主機已崩潰,已關閉或者正在重啟。這種情況下,服務器無法得到客戶端回應,探測報文段在75秒后超時,服務器總共發送10個這樣的探測,每個間隔75秒(超時重傳)。如果服務器還是沒有接收到客戶端的應答,就認為客戶端已經關閉,隨后告知應用程序,關閉連接(套接字變為可讀,讀取錯誤信息為“連接超時”)
- 客戶端主機崩潰并已重啟。在這種情況下,由于客戶端重啟了系統,所有的TCP連接信息都已經丟失,當接收到服務器的探測報文段時,客戶端在本機中無法找到匹配的TCP連接,隨后發送一個復位報文段,服務器接收到復位報文段后告知應用程序,關閉連接(套接字變為可讀,讀取錯誤信息為“連接被重置”)
- 客戶端主機正常運行,但是從服務器不可達。這種情況通常是由于傳輸路徑中的某個路由器關閉導致,此種情況與客戶端主機崩潰并正在重啟時處理方式相同,因為都是收不到應答
下面通過tcpdump抓包分析后三種情況,分析時假設崩潰的一方是服務器,隨后觀察客戶端TCP探查報文段發送情況
另一端主機崩潰,已關閉或正在重啟
借用<TCP/IP詳解>中的示例,建立TCP連接后等待四小時后斷開服務器以太網電纜,觀察客戶端探查報文段發送情況
觀察tcpdump輸出結果(省略了三次握手的報文段)
結果中前三行表示客戶端發送”hello world”報文段,服務器對該報文段進行應答并回顯”hello world”報文段,客戶端對服務器的報文段進行應答
隨后的四小時內,客戶端先后兩次嘗試探查服務器是否關閉,首先看到的是一個ARP請求和ARP應答,隨后發送探查報文段并得到回應
接下來斷開服務器以太網電纜,并在兩個小時之后又進行探查,然而由于服務器主機以太網斷開,無法回應客戶端ARP請求,隨后的每75秒進行一次超時重傳,最終放棄,錯誤碼為“連接超時”
ARP請求主要用來獲取對端的物理地址,首先需要知道對方地址,才能發送數據,而由于服務器以太網斷開無法回應ARP請求,所以上述過程沒有看到TCP報文的發送就已經可以確認對方崩潰
另一端崩潰并重新啟動
借用<TCP/IP詳解>中的示例,在建立連接后將服務器以太網斷開,重新啟動,然后再連接到網絡上。由于服務器已重啟,對于之前所有的TCP連接信息都已丟失,當接收到客戶端發來的探查報文段時,會回復一個復位報文段
觀察tcpdump輸出結果(省去了連接建立過程)
可以看到,由于服務器以太網連接正常,ARP請求得到應答,隨后客戶端發送探查報文段,但是服務器已經重啟,沒有關于該連接的信息,只能返回一個復位報文段
另一端不可達
再次使用<TCP/IP詳解>中的示例,連接建立后先確認可以正常通訊,等待兩小時后發送探查報文并收到回應,隨后將中間鏈路斷開,觀察結果
觀察tcpdump輸出結果(省略掉連接的建立過程)
前三行是正常的回顯流程,4,5行是兩小時后的探查報文,隨后的若干行由于中間鏈路被斷開,引發來自客戶端主機路由器的ICMP不可達錯誤,隨后又連續發送了9個探查報文,最終返回給應用程序“沒有到達主機的路由”錯誤
SO_KEEPALIVE選項
linux內核中有關于保活定時器的三個參數
- tcp_keepalive_time,指空閑時長,tcp_keepalive_time時間沒有數據交互就發送探查報文段,默認7200s(2h)
- tcp_keepalive_intvl,發送探查報文段后如果沒有收到應答,tcp_keepalive_intvl時間后會重新發送,默認是75s
- tcp_keepalive_probes,發送探查報文沒有收到應答后繼續發送的次數,發送tcp_keepalive_probes次后認為對方已關閉,默認是9次
如果要更改,可以在/etc/sysctl.conf文件中寫入(值自己設置)
net.ipv4.tcp_keepalive_time = net.ipv4.tcp_keepalive_probes = net.ipv4.tcp_keepalive_intvl =保存后執行sysctl -p生效。這里是直接修改了內核參數,此后所有的進程的SO_KEEPALIVE時間都被更改
但是由于上述三個參數都是系統級別的參數,也就是說如果更改,那么就會影響開啟了SO_KEEPALIVE的系統進程,可能造成意想不到的后果
如果只想在某個應用程序中修改時間,除了開啟SO_KEEPALIVE選項外,需要同時設計三個TCP參數
#include <netinet/tcp.h>/* fd : 套接字描述符* time : 空閑時間,當time時間沒有數據交互的話就發送探查報文 */ int enableKeepAlive(int fd, int time) {int val = 1;::setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));val = time; //空閑時間::setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val));val = time / 3; //探查報文的發送間隔::setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val));val = 3; //探查報文的發送次數::setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); }總結
以上是生活随笔為你收集整理的TCP/IP学习笔记(五)TCP的保活定时器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TCP/IP学习笔记(四)TCP超时重传
- 下一篇: 每天一道LeetCode-----寻找给