linux c 檢測網線斷開
ESTABLISH 狀態不會改變。實際上 tcp 協議對這部分是有所處理的,需要服務端程式,在配置 socket 屬性時,
使用 keepalive option, 一旦有此配置,這些長時間無資料的連結會根據 tcp 的keepalive 核心屬性,
在 > (tcp_keepalive_time (tcp_keepalive_probes * tcp_keepalive_intvl))
所對應的時間(單位為秒)之後,斷開這些連結。
關於keep alive
無論windows,還是linux,keepalive就三個引數:
sk->keepalive_probes:探測次數
sk->keepalive_time 探測的超時
sk->keepalive_intvl 探測間隔
對 於一個已經建立的tcp連線。如果在keepalive_time時間內雙方沒有任何的資料包傳輸,則開啟keepalive功能的一端將傳送 eepalive資料包,若沒有收到應答,則每隔keepalive_intvl時間再發送該資料包,傳送keepalive_probes次。一直沒有 收到應答,則傳送rst包關閉連線。若收到應答,則將計時器清零。例如★:
sk->keepalive_probes = 3;
sk->keepalive_time = 30;
sk->keepalive_intvl = 1;
意 思就是說對於tcp連線,如果一直在socket上有資料來往就不會觸發keepalive,但是如果30秒一直沒有資料往來,則keep alive開始工作:傳送探測包,受到響應則認為網路,是好的,結束探測;如果沒有相應就每隔1秒發探測包,一共傳送3次,3次後仍沒有相應,
就 關閉連線,也就是從網路開始斷到你的socket能夠意識到網路異常,最多花33秒。但是如果沒有設定keep alive,可能你在你的socket(阻塞性)的上面,接收: recv會一直阻塞不能返回,除非對端主動關閉連線,因為recv不知道socket斷了。傳送:取決於資料量的大小,只要底層協議站的buffer能放 下你的傳送資料,應用程式級別的send就會一直成功返回。 直到buffer滿,甚至buffer滿了還要阻塞一段時間試圖等待buffer空閒。所以你對send的返回值的檢查根本檢測不到失敗。開啟了keep
alive功能,你直接通過傳送接收的函式返回值就可以知道網路是否異常。設定的方法(應用層):
int keepalive = 1; // 開啟keepalive屬性
int keepidle = 60; // 如該連線在60秒內沒有任何資料往來,則進行探測
int keepinterval = 5; // 探測時發包的時間間隔為5 秒
int keepcount = 3; // 探測嘗試的次數.如果第1次探測包就收到響應了,則後2次的不再發.
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive ));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));
2.select和keep alive的關係
select 是為單個執行緒使用多個socket而設計的,跟檢測連線無關,如果只是檢測一個socket的話,沒有必要使用select。開了keepalive機能 的話,每次呼叫recv或send時檢查返回值,判斷是否出錯或為0.如果出錯,再檢查errno查資料,看哪個或哪幾個錯誤號表示連結斷了或不存在就可 以了。
另外,誰想定期檢查連線狀況,誰就啟用keep alive。另一端可以不起,只是被動地對探測包進行響應,這種響應是tcp協議的基本要求,跟keep alive無關。並不需要客戶端和伺服器端都開啟keep alive。
3.測試結果
按照例★的值在一端的socket上開啟keep alive,然後阻塞在一個recv或者不停的send,這個時候拔了網線,測試從拔掉網線到recv/send返回失敗的時間。
在linux kernel裡頭的測試發現,對於阻塞型的socket,當recv的時候,如果沒有設定keep alive,即使網線拔掉或者ifdown,recv很長時間不會返回,最長達17分鐘,雖然這個時間比linux的預設超時時間()短了很多。但是如果 設定了keep alive,基本都在keepalive_time +keepalive_probes*keepalive_intvl =33秒內返回錯誤。
但是對於迴圈不停send的socket,當拔掉網線後,會持續一段時間send返 回成功(0~10秒左右,取決 於傳送資料的量),然後send阻塞,因為協議層的buffer滿了,在等待buffer空閒,大概90秒左右後才會返回錯誤。由此看來,send的時 候,keep alive似乎沒有起到作用,這個原因至今也不清楚。後來通過給send之前設定timer來解決的。