此文章為轉載,轉載路徑:http://blog.hehehehehe.cn/a/17107.htm
此方法可行,經(jīng)過自己的驗證!
有些網(wǎng)絡應用在網(wǎng)線斷開后重新連上的情況下tcp socket連接保持ESTABLISH狀態(tài)不變,假如應用程式不使用tcp的keepalive,在網(wǎng)線斷開之后,以前建立的 socket 鏈接仍然會保持在ESTABLISH 狀態(tài)不會改變。實際上tcp協(xié)議對這部分是有所處理的,需要服務端程式,在配置socket屬性時,使用 keepalive option,一旦有此配置,這些長時間無數(shù)據(jù)的鏈接會根據(jù)tcp的keepalive內核屬性,在大于(tcp_keepalive_time(tcp_keepalive_PRobes * tcp_keepalive_intvl))所對應的時間(單位為秒)之后,斷開這些鏈接。 關于keep alive無論windows,還是linux,keepalive就三個參數(shù):
sk->keepalive_probes: 探測次數(shù)
sk->keepalive_time: 探測的超時
sk->keepalive_intvl: 探測間隔
對于一個已經(jīng)建立的tcp連接,如果在keepalive_time時間內雙方?jīng)]有任何的數(shù)據(jù)包傳輸,則開啟keepalive功能的一端將發(fā)送 eepalive數(shù)據(jù)包,若沒有收到應答,則每隔keepalive_intvl時間再發(fā)送該數(shù)據(jù)包,發(fā)送keepalive_probes次。一直沒有收到應答,則發(fā)送rst包關閉連接。若收到應答,則將計時器清零。例如★:
sk->keepalive_probes = 3;
sk->keepalive_time = 30;
sk->keepalive_intvl = 1;
意思就是說對于tcp連接,如果一直在socket上有數(shù)據(jù)來往就不會觸發(fā)keepalive,但是如果30秒一直沒有數(shù)據(jù)往來,則keep alive開始工作:發(fā)送探測包,受到響應則認為網(wǎng)絡,是好的,結束探測;如果沒有相應就每隔1秒發(fā)探測包,一共發(fā)送3次,3次后仍沒有相應,就關閉連接,也就是從網(wǎng)絡開始斷到你的socket能夠意識到網(wǎng)絡異常,最多花33秒。但是如果沒有設置keep alive,可能你在你的socket(阻塞性)的上面,接收: recv會一直阻塞不能返回,除非對端主動關閉連接,因為recv不知道socket斷了。發(fā)送:取決于數(shù)據(jù)量的大小,只要底層協(xié)議站的buffer能放 下你的發(fā)送數(shù)據(jù),應用程序級別的send就會一直成功返回,直到buffer滿,甚至buffer滿了還要阻塞一段時間試圖等待buffer空閑,所以你對send的返回值的檢查根本檢測不到失敗。開啟了keep alive功能,你直接通過發(fā)送接收的函數(shù)返回值就可以知道網(wǎng)絡是否異常。設置的方法(應用層):
int keepalive = 1; // 開啟keepalive屬性
int keepidle = 60; // 如該連接在60秒內沒有任何數(shù)據(jù)往來,則進行探測
int keepinterval = 5; // 探測時發(fā)包的時間間隔為5 秒
int keepcount = 3; // 探測嘗試的次數(shù).如果第1次探測包就收到響應了,則后2次的不再發(fā).
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協(xié)議的基本要求,跟keep alive無關,并不需要客戶端和服務器端都開啟keep alive。
測試結果
按照以上舉例★的值在一端的socket上開啟keep alive,然后阻塞在一個recv或者不停的send,這個時候拔了網(wǎng)線,測試從拔掉網(wǎng)線到recv/send返回失敗的時間。
在linux kernel里頭的測試發(fā)現(xiàn),對于阻塞型的socket,當recv的時候,如果沒有設置keep alive,即使網(wǎng)線拔掉或者ifdown,recv很長時間不會返回,最長達17分鐘,雖然這個時間比linux的默認超時時間短了很多。但是如果設置了keep alive,基本都在keepalive_time+keepalive_probes*keepalive_intvl =33秒內返回錯誤。
但是對于循環(huán)不停send的socket,當拔掉網(wǎng)線后,會持續(xù)一段時間send返回成功(0~10秒左右,取決于發(fā)送數(shù)據(jù)的量),然后send阻塞,因為協(xié)議層的buffer滿了,在等待buffer空閑,大概90秒左右后才會返回錯誤。由此看來,send的時候,keep alive似乎沒有起到作用,這個原因至今也不清楚。后來通過給send之前設置timer來解決的。
新聞熱點
疑難解答