在《UNIX網絡編程第1卷》中也有詳細的闡述:編程
SO_KEEPALIVE 保持鏈接檢測對方主機是否崩潰,避免(服務器)永遠阻塞於TCP鏈接的輸入。設置該選項後,若是2小時內在此套接口的任一方向都沒有數據交換,TCP就自 動給對方 發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.它會致使如下三種狀況:對方接收一切正常:以指望的ACK響應。2小時後,TCP將發出另外一個探測分 節。對方已崩潰且已從新啓動:以RST響應。套接口的待處理錯誤被置爲ECONNRESET,套接 口自己則被關閉。對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖獲得一個響應。在發出第一個探測分節11分鐘 15秒後若仍無響應就放棄。套接口的待處理錯誤被置爲ETIMEOUT,套接口自己則被關閉。如ICMP錯誤是「host unreachable(主機不可達)」,說明對方主機並無崩潰,可是不可達,這種狀況下待處理錯誤被置爲 EHOSTUNREACH。服務器
在該書的第158頁有更詳細的描述。網絡
根據上面的介紹咱們能夠知道對端以一種非優雅的方式斷開鏈接的時候,咱們能夠設置SO_KEEPALIVE屬性使得咱們在2小時之後發現對方的TCP鏈接是否依然存在。app
keepAlive = 1;
Setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));socket
若是咱們不能接受如此之長的等待時間,從TCP-Keepalive-HOWTO上能夠知道一共有兩種方式能夠設置,一種是修改內核關於網絡方面的 配置參數,另一種就是SOL_TCP字段的TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT三個選項。tcp
The tcp_keepidle parameter specifies the interval of inactivity that causes TCP to generate a KEEPALIVE transmission for an application that requests them. tcp_keepidle defaults to 14400 (two hours).ide
/*開始首次KeepAlive探測前的TCP空閉時間 */函數
The tcp_keepintvl parameter specifies the interval between the nine retries that are attempted if a KEEPALIVE transmission is not acknowledged. tcp_keepintvl defaults to 150 (75 seconds).
/* 兩次KeepAlive探測間的時間間隔 */繼承
The TCP_KEEPCNT option specifies the maximum number of keepalive probes to be sent. The value of TCP_KEEPCNT is an integer value between 1 and n, where n is the value of the systemwide tcp_keepcnt parameter.接口
/* 斷定斷開前的KeepAlive探測次數 */
所以咱們能夠獲得
int keepIdle = 6;
int keepInterval = 5;
int keepCount = 3;
Setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
Setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
Setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
咱們須要注意的TCP-Keepalive-HOWTO上這段話:
Remember that keepalive is not program−related, but socket−related, so if you have multiple sockets, you can handle keepalive for each of them separately.
這些屬性是sockt繼承的,非整個代碼內的全部sockets都繼承這個屬性,由於若是要應用到多個套接口上必須分別使用Setsockopt, Setsockopt是setsockopt的包裹函數。
若是心搏函數要維護客戶端的存活,即服務器必須每隔一段時間必須向客戶段發送必定的數據,那麼使用SO_KEEPALIVE是有很大的不足的。由於 SO_KEEPALIVE選項指"此套接口的任一方向都沒有數據交換",我不知道你們是怎麼理解這個實現的。在Linux 2.6系列上,上面話的理解是隻要打開SO_KEEPALIVE選項的套接口端檢測到數據發送或者數據接受就認爲是數據交換。
所以在這種狀況下使用 SO_KEEPALIVE選項 檢測對方是否非正常鏈接是徹底沒有做用的,在每隔一段時間發包的狀況, keep-alive的包是不可能被髮送的。上層程序在非正常端開的狀況下是能夠正常發送包到緩衝區的。非正常端開的狀況是指服務器沒有收到"FIN" 或者 "RST"包。
固然這種狀況也是比較好判定對方是否存活,我提出來的主要緣由是想看看你們對"此套接口的任一方向都沒有數據交換"是怎麼去理解的。