長連接和Keepalive詳解

TCP協議中有長連接和短連接之分。短連接在數據包發送完成後就會自己斷開,長連接在發包完畢後,會在一定的時間內保持連接,即我們通常所說的Keepalive(存活定時器)功能。
默認的Keepalive超時需要7,200,000 milliseconds,即2小時,探測次數爲5次。它的功效和用戶自己實現的心跳機制是一樣的。開啓Keepalive功能需要消耗額外的寬帶和流量,儘管這微不足道,但在按流量計費的環境下增加了費用,另一方面,Keepalive設置不合理時可能會因爲短暫的網絡波動而斷開健康的TCP連接。

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

 

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

 

個人計算機用戶使用TCP/IP協議通過Telnet登錄一臺主機,這是能夠說明需要使用存活定時器的一個常用例子。如果某個用戶在使用結束時只是關掉了電源,而沒有註銷(log off),那麼他就留下了一個半打開(half-open)的連接。如果客戶端消失,留給了服務器端半打開的連接,並且服務器又在等待客戶端的數據,那麼等待將永遠持續下去。存活特徵的目的就是在服務器端檢測這種半打開連接。

 

也可以在客戶端設置存活器選項,且沒有不允許這樣做的理由,但通常設置在服務器。如果連接兩端都需要探測對方是否消失,那麼就可以在兩端同時設置(比如NFS)。


keepalive工作原理:

 

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

 

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

 

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

 

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

 

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

 

 

 

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

 

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

linux內核包含對keepalive的支持。其中使用了三個參數:tcp_keepalive_time(開啓keepalive的閒置時 長)tcp_keepalive_intvlkeepalive探測包的發送間隔)和tcp_keepalive_probes(如果對方不予應答,探測包的發送次數);在liunx中,keepalive是一個開關選項,可以通過函數來使能。具體地說,可以使用以下代碼:
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));

 

 

 

tcp檢測到對端socket不再可用時(不能發出探測包,或探測包沒有收到ACK的響應包),select會返回socket可讀,並且在recv時返回-1,同時置上errnoETIMEDOUT。此時TCP的狀態是斷開的。



keepalive參數設置代碼如下:

// 開啓KeepAlive
BOOL bKeepAlive = TRUE;
int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof
(bKeepAlive));
if (nRet ==
SOCKET_ERROR)
{
return
FALSE;
}

// 設置KeepAlive參數

tcp_keepalive alive_in = {0};
tcp_keepalive alive_out
= {0
};
alive_in.keepalivetime
=5000; // 開始首次KeepAlive探測前的TCP空閉時間

alive_in.keepaliveinterval =1000; // 兩次KeepAlive探測間的時間間隔
alive_in.onoff = TRUE;
unsigned
long ulBytesReturn =0
;
nRet
= WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof
(alive_in),
&alive_out, sizeof(alive_out), &
ulBytesReturn, NULL, NULL);
if (nRet ==
SOCKET_ERROR)
{
return
FALSE;
}


開啓Keepalive選項之後,對於使用IOCP模型的服務器端程序來說,一旦檢測到連接斷開,GetQueuedCompletionStatus函 數將立即返回FALSE,使得服務器端能及時清除該連接、釋放該連接相關的資源。對於使用select模型的客戶端來說,連接斷開被探測到時,以recv 目的阻塞在socket上的select方法將立即返回SOCKET_ERROR,從而得知連接已失效,客戶端程序便有機會及時執行清除工作、提醒用戶或 重新連接。

TCP連接非正常斷開的檢測(KeepAlive探測)

 

此處的”非正常斷開”指TCP連接不是以優雅的方式斷開,如網線故障等物理鏈路的原因,還有突然主機斷電等原因

 

有兩種方法可以檢測:1.TCP連接雙方定時發握手消息2.利用TCP協議棧中的KeepAlive探測

第二種方法簡單可靠,只需對TCP連接兩個Socket設定KeepAlive探測。


在windows下使用,要包含MSTcpIP.h的頭文件。點擊下面的鏈接即可下載這個文件
MSTcpIP

備註:長連接雖好,但是比較好用但是佔用系統資源比較大。個人建議如無特殊需要,用自己的心跳包機制最好

發佈了40 篇原創文章 · 獲贊 121 · 訪問量 142萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章