TCP心跳 | TCP keepAlive(轉)

應用層對於每一個socket採用以下函數來開啓 keepalive機制,其參數將採用系統上述配置。linux

setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));編程

注意:keepalive是一個TCP協議包,並非應用層數據包,意即經過recv等函數從應用層上是沒法得到該協議包。可經過抓包工具來看。windows

==================================================================服務器

1、什麼是keepalive定時器?[1]網絡

在一個空閒的(idle)TCP鏈接上,沒有任何的數據流,許多TCP/IP的初學者都對此感到驚奇。也就是說,若是TCP鏈接兩端沒有任何一個進程在向對方發送數據,那麼在這兩個TCP模塊之間沒有任何的數據交換。你可能在其它的網絡協議中發現有輪詢(polling),但在TCP中它不存在。言外之意就是咱們只要啓動一個客戶端進程,同服務器創建了TCP鏈接,無論你離開幾小時,幾天,幾星期或是幾個月,鏈接依舊存在。中間的路由器可能崩潰或者重啓,電話線可能go down或者back up,只要鏈接兩端的主機沒有重啓,鏈接依舊保持創建。併發

這就能夠認爲不論是客戶端的仍是服務器端的應用程序都沒有應用程序級(application-level)的定時器來探測鏈接的不活動狀態(inactivity),從而引發任何一個應用程序的終止。然而有的時候,服務器須要知道客戶端主機是否已崩潰而且關閉,或者崩潰但重啓。許多實現提供了存活定時器來完成這個任務。app

存活定時器是一個包含爭議的特徵。許多人認爲,即便須要這個特徵,這種對對方的輪詢也應該由應用程序來完成,而不是由TCP中實現。此外,若是兩個終端系統之間的某個中間網絡上有鏈接的暫時中斷,那麼存活選項(option)就可以引發兩個進程間一個良好鏈接的終止。例如,若是正好在某個中間路由器崩潰、重啓的時候發送存活探測,TCP就將會認爲客戶端主機已經崩潰,但事實並不是如此。socket

存活(keepalive)並非TCP規範的一部分。在Host Requirements RFC羅列有不使用它的三個理由:(1)在短暫的故障期間,它們可能引發一個良好鏈接(good connection)被釋放(dropped),(2)它們消費了沒必要要的寬帶,(3)在以數據包計費的互聯網上它們(額外)花費金錢。然而,在許多的實現中提供了存活定時器。tcp

一些服務器應用程序可能表明客戶端佔用資源,它們須要知道客戶端主機是否崩潰。存活定時器能夠爲這些應用程序提供探測服務。Telnet服務器和Rlogin服務器的許多版本都默認提供存活選項。函數

我的計算機用戶使用TCP/IP協議經過Telnet登陸一臺主機,這是可以說明須要使用存活定時器的一個經常使用例子。若是某個用戶在使用結束時只是關掉了電源,而沒有註銷(log off),那麼他就留下了一個半打開(half-open)的鏈接。在圖18.16,咱們看到如何在一個半打開鏈接上經過發送數據,獲得一個復位(reset)返回,但那是在客戶端,是由客戶端發送的數據。若是客戶端消失,留給了服務器端半打開的鏈接,而且服務器又在等待客戶端的數據,那麼等待將永遠持續下去。存活特徵的目的就是在服務器端檢測這種半打開鏈接。
2、keepalive如何工做?[1]

在此描述中,咱們稱使用存活選項的那一段爲服務器,另外一端爲客戶端。也能夠在客戶端設置該選項,且沒有不容許這樣作的理由,但一般設置在服務器。若是鏈接兩端都須要探測對方是否消失,那麼就能夠在兩端同時設置(好比NFS)。

若在一個給定鏈接上,兩小時以內無任何活動,服務器便向客戶端發送一個探測段。(咱們將在下面的例子中看到探測段的樣子。)客戶端主機必須是下列四種狀態之一:

1) 客戶端主機依舊活躍(up)運行,而且從服務器可到達。從客戶端TCP的正常響應,服務器知道對方仍然活躍。服務器的TCP爲接下來的兩小時復位存活定時器,若是在這兩個小時到期以前,鏈接上發生應用程序的通訊,則定時器從新爲往下的兩小時復位,而且接着交換數據。

2) 客戶端已經崩潰,或者已經關閉(down),或者正在重啓過程當中。在這兩種狀況下,它的TCP都不會響應。服務器沒有收到對其發出探測的響應,而且在75秒以後超時。服務器將總共發送10個這樣的探測,每一個探測75秒。若是沒有收到一個響應,它就認爲客戶端主機已經關閉並終止鏈接。

3) 客戶端曾經崩潰,但已經重啓。這種狀況下,服務器將會收到對其存活探測的響應,但該響應是一個復位,從而引發服務器對鏈接的終止。

4) 客戶端主機活躍運行,但從服務器不可到達。這與狀態2相似,由於TCP沒法區別它們兩個。它所能代表的僅是未收到對其探測的回覆。

服務器沒必要擔憂客戶端主機被關閉而後重啓的狀況(這裏指的是操做員執行的正常關閉,而不是主機的崩潰)。當系統被操做員關閉時,全部的應用程序進程(也就是客戶端進程)都將被終止,客戶端TCP會在鏈接上發送一個FIN。收到這個FIN後,服務器TCP向服務器進程報告一個文件結束,以容許服務器檢測這種狀態。

在第一種狀態下,服務器應用程序不知道存活探測是否發生。凡事都是由TCP層處理的,存活探測對應用程序透明,直到後面2,3,4三種狀態發生。在這三種狀態下,經過服務器的TCP,返回給服務器應用程序錯誤信息。(一般服務器向網絡發出一個讀請求,等待客戶端的數據。若是存活特徵返回一個錯誤信息,則將該信息做爲讀操做的返回值返回給服務器。)在狀態2,錯誤信息相似於「鏈接超時」。狀態3則爲「鏈接被對方復位」。第四種狀態看起來像鏈接超時,或者根據是否收到與該鏈接相關的ICMP錯誤信息,而可能返回其它的錯誤信息。

windows 實現:

 

在一個正常的TCP鏈接上,當咱們用無限等待的方式調用下面的Recv或Send的時候:

   ret=recv(s,&buf[idx],nLeft,flags);

   或

   ret=send(s,&buf[idx],nLeft,flags);

   若是TCP鏈接被對方正常關閉,也就是說,對方是正確地調用了closesocket(s)或者shutdown(s)的話,那麼上面的Recv或Send調用就能立刻返回,而且報錯。這是因爲closesocket(s)或者shutdown(s)有個正常的關閉過程,會告訴對方「TCP鏈接已經關閉,你不須要再發送或者接受消息了」。可是,若是是網線忽然被拔掉,TCP鏈接的任何一端的機器忽然斷電或重啓動,那麼這時候正在執行Recv或Send操做的一方就會由於沒有任何鏈接中斷的通知而一直等待下去,也就是會被長時間卡住。這種情形解決的辦法是啓動TCP編程裏的keepAlive機制。

複製代碼
   struct TCP_KEEPALIVE inKeepAlive = {0};
     unsigned long ulInLen = sizeof(struct TCP_KEEPALIVE);
     struct TCP_KEEPALIVE utKeepAlive = {0};
     unsigned long ulOutLen = sizeof(struct TCP_KEEPALIVE);
     unsigned long ulBytesReturn = 0;

    inKeepAlive.onoff=1;
     inKeepAlive.keepaliveinterval=5000; //單位爲毫秒
    inKeepAlive.keepalivetime=1000;      //單位爲毫秒
    ret=WSAIoctl(s, SIO_KEEPALIVE_VALS, (LPVOID)&inKeepAlive, ulInLen, 
                           (LPVOID)&outKeepAlive, ulOutLen, &ulBytesReturn, NULL, NULL);
複製代碼

  此處的keepalivetime表示的是TCP鏈接處於暢通時候的探測頻率,一旦探測包沒有返回,就以keepaliveinterval的頻率發送,通過若干次的重試,若是探測包都沒有返回,那麼就得出結論:TCP鏈接已經斷開,因而上面的Recv或Send調用也就能立刻返回,不會無限制地卡住了。

 

上圖是對上面文字的說明。亮條以前,TCP處於暢通狀態,KeepAlive是以1000毫秒(keepalivetime的值)的頻率發送探測包,在發送到第32個探測包的時候,探測包沒有返回,因而就以5000毫秒(keepalivetime的值)的頻率發送探測包,重發幾回後,探測包都沒有返回,因而就得出結論:此TCP鏈接已經斷開了!

 

對於Win2K/XP/2003,能夠從下面的註冊表項找到影響整個系統全部鏈接的keepalive參數:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]

「KeepAliveTime」=dword:006ddd00 「KeepAliveInterval」=dword:000003e8 「MaxDataRetries」=」5″

 

  對於實用程序來講,2小時的空閒時間太長。所以,咱們須要手工開啓Keepalive功能並設置合理的Keepalive參數。在XP和WIN2003系統上,能夠針對單獨的socket來設置,可是在windows 2000,不能單獨設置,若是設置,那麼影響是整個系統的全部socket。

linux實現:

SO_KEEPALIVE/TCP_KEEPCNT/TCP_KEEPIDLE/TCP_KEEPINTVL 若是一方已經關閉或異常終止鏈接,而另外一方殊不知道,咱們將這樣的TCP鏈接稱爲半打開的。TCP經過保活定時器(KeepAlive)來檢測半打開鏈接。 在高併發的網絡服務器中,常常會出現漏掉socket的狀況,對應的結果有一種狀況就是出現大量的CLOSE_WAIT狀態的鏈接。這個時候,能夠經過設置KEEPALIVE選項來解決這個問題,固然還有其餘的方法能夠解決這個問題,詳細的狀況能夠查看參考資料8。

複製代碼
使用方法以下:
//Setting For KeepAlive
 int keepalive = 1;
 setsockopt(incomingsock,SOL_SOCKET,SO_KEEPALIVE,(void*)(&keepalive),(socklen_t)sizeof(keepalive));
                         
 int keepalive_time = 30;
 setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPIDLE,(void*)(&keepalive_time),(socklen_t)sizeof(keepalive_time));
 int keepalive_intvl = 3;
 setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPINTVL,(void*)(&keepalive_intvl),(socklen_t)sizeof(keepalive_intvl));
int keepalive_probes= 3;
 setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPCNT,(void*)(&keepalive_probes),(socklen_t)sizeof(keepalive_probes));
設置SO_KEEPALIVE選項來開啓KEEPALIVE,而後經過TCP_KEEPIDLE、TCP_KEEPINTVL和TCP_KEEPCNT設置keepalive的開始時間、間隔、次數等參數。
 固然,也能夠經過設置/proc/sys/net/ipv4/tcp_keepalive_time、tcp_keepalive_intvl和tcp_keepalive_probes等內核參數來達到目的,可是這樣的話,會影響全部的socket,所以建議使用setsockopt設置。
複製代碼

http://blog.csdn.net/cccallen/article/details/8003324

相關文章
相關標籤/搜索