TCP socket如何判斷連接斷開

 

1、Socket SO_KEEPALIVE不能取代心跳線程的原因

(1)SO_KEEPALIVE是系統底層的機制,用於系統維護每一個tcp連接的。

(2)心跳線程屬於應用層,主要用於終端和服務器連接的檢查。

      即使SO_KEEPALIVE檢測到連接正常,但並不能保證終端和服務器連接的正常。有一種情況,服務器進程死了,但它和客戶端的tcp連接還連着(該連接由系統維護的)。

      這就是SO_KEEPALIVE不能取代心跳線程的原因吧。

 

2、判斷socket是否已經斷開

2.1、例子1

     最近在做一個TCP採集程序,使用到C/S的結構。功能比較的簡單,就是TCP採集程序作爲服務器,信令採集設備作爲客戶端,客戶端與服務器端之間建立長連接之後,開始發送信令報文給服務器。在服務器端使用多線程方式來處理每個客戶端的socket連接,服務器端不主動斷開鏈路,也沒有心跳機制來維護連接的狀態,客戶端發送數據的時間也是不一定的,只要有採集到信令數據時才進行發送。在客戶端socket斷開後,服務器端應該能夠知道並且釋放socket資源。
     判斷socket是否已經斷開的方法是使用非阻塞的select方式進行socket檢查,步驟如下:

1)設置接收到的socket爲異步方式;

2)使用select()函數測試一個socket是否可讀;

3)如果select()函數返回的值爲1,但是使用recv()函數讀取的數據長度爲0,那麼說明該socket已經斷開。

4)如果recv()返回值小於等於0時,客戶端的連接已經斷開,但是還需要判斷errno是否等於EINTR。如果errno=EINTR則說明recv()函數是由於程序接收到中斷信號後返回的,socket連接應該還是正常,步應該close掉socket連接
 

注:對於阻塞socket的recv函數會在以下三種情況下返回值:

1)接收到數據時會返回;

2)程序接收到信號時返回-1,errno=EINTR;

3)Socket出現問題時,返回-1,具體的錯誤碼請查看man recv;

4)一定要養成查看man說明,內容很詳細,很有幫助。

這種方法經過長時間的測試證明是有效的,僅供大家參考。

     此外,UNP卷一上有很多socket異常情況下的模擬解釋,大家可以去閱讀下。如果網絡中間有多級路由,路由當掉等很多情況出現,所以建議程序中在應用層中加入心跳(heartbeat機制)和重連來維持連接的狀態

      TCP protocol has a timer to determine if the connection is abnormally closed. But this timeout value is very long by default and if you want to check this situation as soon as possible to improve performance, the best solution is to introduce a keepalive mechanism in application protocol design.

      TCP協議有一個定時器來決定連接是否被異常關閉。但是該超時時間值缺省的情況下會非常長,如果你希望儘快的檢查出這種狀態改進性能,最好的方法就是在應用程序協議設計的時候引入keepalive(保持連接)機制。

2.2、例子2

     最近在做一個服務器端程序,C/S結構。功能方面比較簡單就是client端與server端建立連接,然後發送消息給server。我在server端會使用專門的線程處理一條socket連接。這就涉及到一個問題,如果socket連接斷開(異常,正常)後,我如何才能感知到?server端這邊是絕對被動的,sever端不能主動斷開連接。也沒有連接鏈路維持包之類的。client端發送數據的時間也是不定的。在socket連接斷開後,server要能夠感知到並釋放資源。

     這個問題在思考測試,詢問同事之後,找到了一個方法,可以做到這一點。

     當使用 select()函數測試一個socket是否可讀時,如果select()函數返回值爲1,且使用recv()函數讀取的數據長度爲0 時,就說明該socket已經斷開

     爲了更好的判定socket是否斷開,我判斷當recv()返回值小於等於0時,socket連接斷開。但是還需要判斷 errno是否等於 EINTR 。如果errno == EINTR 則說明recv函數是由於程序接收到信號後返回的,socket連接還是正常的,不應close掉socket連接。

PS:對於堵塞socket的recv函數會在以下三種情況下返回:
(1)recv到數據時,會返回。
(2)在整個程序接收到信號時,返回-1。errno = EINTR。//在程序的起始階段,屏蔽掉信號的除外。部分信號還是屏蔽不掉的。
(3)socket出現問題時,返回-1.具體錯誤碼看 man recv()
(4)一定要看 man 說明,很詳細,很有幫助。
這種方法經過長時間測試後,是有效的。所以寫出來讓大家參考一下,請大家發表意見。

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