TCP連接 保持 保活

TCP連接

當網絡通信時採用TCP協議時,在真正的讀寫操作之前,server與client之間必須建立一個連接,當讀寫操作完成後,雙方不再需要這個連接時它們可以釋放這個連接,連接的建立是需要三次握手的,而釋放則需要4次握手,所以說每個連接的建立都是需要資源消耗和時間消耗的


TCP保活的必要性:


TCP的長連接理論上只要連接建立後,就會一直保持着
。但有時有一些防火牆之類的軟件會自動檢查主機的網絡連接狀況,比如說如果發現某個連接在幾分鐘之內都沒有數據通訊,則會關閉這個連接。有時客戶端與服務器需要實時的檢測連接狀態,就是需要知道對方是否還在線,如果對方不在線了,需要做相應的處理,這是就需要通過發送心跳包的方法監測鏈路的狀態


導致 TCP 連接斷連的因素 :

理想狀態下,一個 TCP 連接可以被長期保持。然而,在實際應用中,客戶端或服務器端上維持的一個看似正常的 TCP 連接可能已經斷連。TCP 連接主要受到兩個方面的影響而導致斷連:網絡中間節點客戶端 / 服務器節點參與通信的兩方節點

在實際網絡應用中,兩個主機之間的通信往往需要穿越多箇中間節點,例如路由器、網關、防火牆等。因此,兩個主機之間 TCP 連接的保持同樣會受到中間節點的影響,尤其是會受到防火牆(軟件或硬件防火牆)的限制。防火牆是一種裝置,有多種不同的實現方式(軟件實現、硬件設備實現或是軟硬件相結合實現),它需要依據一系列規則對進出的信息流進行掃描,並允許安全(符合規則)的信息交互、阻止不安全(違反規則)的信息交互。防火牆的工作特性決定了要維護一個網絡連接就需要耗費較多的資源,並且企業防火牆常常位於企業網絡的出入口,長時間維護非活躍的 TCP 連接必將導致網絡性能的下降。因此,大部分防火牆默認會關閉長時間處於非活躍狀態的連接而導致 TCP 連接斷連。類似的,如果中間節點異常導致來自客戶端關閉連接的請求無法傳遞到服務器端,也將導致服務器端的相應連接發生斷連。

另一方面,對於一個 TCP 連接兩端的主機而言,創建 TCP 連接需要耗費一定的系統資源。如果不再使用某個連接,那麼我們總是希望進行通信的兩個主機能夠主動關閉相應的連接,以便釋放所佔用的系統資源。然而,如果由於客戶端出現異常 ( 例如崩潰或異常重啓 ) 而導致連接未能正常關閉,這將導致服務器端的連接斷連。

無論是客戶端節點或是服務器端節點,斷連的 TCP 連接已經不能傳遞任何信息,因此,維護大量斷連的 TCP 連接將導致系統資源的浪費。這種系統資源的浪費可能並不會對客戶端節點帶來太大問題;然而,對於服務器主機而言,這可能會導致系統資源(尤指內存資源和 socket 資源)被耗盡而拒絕爲新的用戶請求提供服務。因此在實際應用中,服務器端需要採取相應的方法來探測 TCP 連接是否已經斷連。

探測 TCP 連接斷連的三種常用方法 

探測 TCP 連接是否斷連或是工作正常的原理比較簡單:定期向連接的遠程通信節點發送一定格式的信息並等待遠程通信節點的反饋,如果在規定時間內收到來自遠程節點的正確的反饋信息,那麼該連接就是正常的,否則該連接已經斷連。依據該原理,目前常用的探測方法有以下三種。

方法1:應用程序的自我探測 

應用程序本身附帶探測其自身建立的 TCP 連接的功能。這種方法具有極大的靈活性,可以依據應用本身的特點選擇相應的探測機制和功能實現。然而,實際應用中,大部分應用程序均沒有附帶自我探測的功能。

方法二:第三方應用程序的探測 

此種方法就是在服務節點上安裝相應的第三方應用程序來探測該節點上所有的 TCP 連接是否正常或是已經斷連。該方法最大的不足就是需要所有支持探測的客戶端能夠識別來自該探測應用的數據報文,因此,實際應用中比較少見。

方法三:TCP 協議層的保活探測 

最常用的探測方法就是採用 TCP 協議層提供的保活探測功能即 TCP 連接保活定時器。儘管該功能並不是 RFC 規範的一部分,但是幾乎所有的類 Unix 系統均實現了該功能,所以使得該探測方法被廣泛使用。

長連接與短連接

TCP短連接

我們模擬一下TCP短連接的情況,client向server發起連接請求,server接到請求,然後雙方建立連接。client向server發送消息,server迴應client,然後一次讀寫就完成了,這時候雙方任何一個都可以發起close操作,不過一般都是client先發起close操作。爲什麼呢,一般的server不會回覆完client後立即關閉連接的,當然不排除有特殊的情況。從上面的描述看,短連接一般只會在client/server間傳遞一次讀寫操作

短連接的優點是:管理起來比較簡單,存在的連接都是有用的連接,不需要額外的控制手段

TCP長連接

接下來我們再模擬一下長連接的情況,client向server發起連接,server接受client連接,雙方建立連接。Client與server完成一次讀寫之後,它們之間的連接並不會主動關閉,後續的讀寫操作會繼續使用這個連接。

首先說一下TCP/IP詳解上講到的TCP保活功能,保活功能主要爲服務器應用提供,服務器應用希望知道客戶主機是否崩潰,從而可以代表客戶使用資源。如果客戶已經消失,使得服務器上保留一個半開放的連接,而服務器又在等待來自客戶端的數據,則服務器將應遠等待客戶端的數據,保活功能就是試圖在服務器端檢測到這種半開放的連接。

使用WIN32 TCP自帶的保活


在默認的情況下,TCP連接是沒有保活的心跳的。這就是說,當一個TCP的socket,客戶端與服務端誰也不發送數據,會一直保持着連接。這其中如果有一方異常掉線,另一端永遠也不可能知道。這對於一些服務型的程序來說,將是災難性的後果。

所以,必須對創建的socket,啓用保活心跳,即Keepalive選項


WIN32環境下的TCP Keepalive參數設置
  而WIN32環境下的參數設置,就要麻煩一些,需要使用另外的一個函數WSAIoctl和一個結構struct tcp_keepalive。

  它們的原型分別爲:


[html] view plaincopy #include <winsock2.h>    
#include <mstcpip.h>    
    
int WSAIoctl(    
             SOCKET s,   
             DWORD dwIoControlCode,    
             LPVOID lpvInBuffer,    
             DWORD cbInBuffer,    
             LPVOID lpvOutBuffer,    
             DWORD cbOutBuffer,    
             LPDWORD lpcbBytesReturned,    
             LPWSAOVERLAPPED lpOverlapped,    
             LPWSAOVERLAPPED_COMPLETION lpCompletionRoutine    
);    
    
struct tcp_keepalive {   
    u_long onoff;    
    u_long keepalivetime;    
    u_long keepaliveinterval;    
};    
  在這裏,使用WSAIoctl的時候,dwIoControlCode要使用SIO_KEEPALIVE_VALS,lpvOutBuffer用不上,cbOutBuffer必須設置爲0。 

  struct tcp_keepalive結構的參數意義爲:

  onoff,是否開啓KEEPALIVE; keepalivetime,多長時間觸發Keepalive報文的發送; keepaliveinterval,多長時間沒有迴應觸發下一次發送。

  注意:這裏兩個時間單位都是毫秒而不是秒。


[html] view plaincopy #include <winsock2.h>    
#include <mstcpip.h>    
    
int    
socket_set_keepalive (int fd)    
{    
  struct tcp_keepalive kavars[1] = {    
      1,    
      10 * 1000,        /* 10 seconds */    
      5 * 1000          /* 5 seconds */   
  };    
    
  /* Set: use keepalive on fd */    
  alive = 1;    
  if (setsockopt   
      (fd, SOL_SOCKET, SO_KEEPALIVE, (const char *) &alive,    
       sizeof alive) != 0)    
    {    
      log_warn ("Set keep alive error: %s.\n", strerror (errno));    
      return -1;    
    }    
    
  if (WSAIoctl    
      (fd, SIO_KEEPALIVE_VALS, kavars, sizeof kavars, NULL, sizeof (int), &ret, NULL,    
       NULL) != 0)    
    {   
      log_warn ("Set keep alive error: %s.\n", strerror (WSAGetLastError ()));    
      return -1;    
    }    
    
  return 0;    
}  


最後說明一下 具體是用TCP協議自身的保活定時器實現心跳,還是通訊雙方通過自定義協議實現心跳包來保活,還是通過第三方應用來進行保活,要取決於你的項目需求,網絡負荷以及那種實現起來更便捷等多方面的因素,根據自身情況來使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章