有些網絡應用在網線斷開後從新連上的狀況下tcp socket鏈接保持ESTABLISH狀態不變,假如應用程式不使用tcp的keepalive,在網線斷開以後,之前創建的 socket 連接仍然會保持在ESTABLISH 狀態不會改變。實際上tcp協議對這部分是有所處理的,須要服務端程式,在配置socket屬性時,使用 keepalive option,一旦有此配置,這些長時間無數據的連接會根據tcp的keepalive內核屬性,在大於(tcp_keepalive_time(tcp_keepalive_probes * tcp_keepalive_intvl))所對應的時間(單位爲秒)以後,斷開這些連接。
關於keep alive不管windows,仍是linux,keepalive就三個參數:linux
sk->keepalive_probes: 探測次數windows
sk->keepalive_time: 探測的超時服務器
sk->keepalive_intvl: 探測間隔網絡
對於一個已經創建的tcp鏈接,若是在keepalive_time時間內雙方沒有任何的數據包傳輸,則開啓keepalive功能的一端將發送 eepalive數據包,若沒有收到應答,則每隔keepalive_intvl時間再發送該數據包,發送keepalive_probes次。一直沒有收到應答,則發送rst包關閉鏈接。若收到應答,則將計時器清零。例如★:socket
sk->keepalive_probes = 3;tcp
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功能,你直接經過發送接收的函數返回值就能夠知道網絡是否異常。設置的方法(應用層):spa
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 ));
select和keep alive的關係
select是爲單個線程使用多個socket而設計的,跟檢測鏈接無關,若是隻是檢測一個socket的話,沒有必要使用select。開了keepalive機能的話,每次調用recv或send時檢查返回值,判斷是否出錯或爲0。若是出錯,再檢查errno查資料,看哪一個或哪幾個錯誤號表示連接斷了或不存在就能夠了。
另外,誰想按期檢查鏈接情況,誰就啓用keep alive。另外一端能夠不起,只是被動地對探測包進行響應,這種響應是tcp協議的基本要求,跟keep alive無關,並不須要客戶端和服務器端都開啓keep alive。
測試結果
按照以上舉例★的值在一端的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來解決的。