若是TCP鏈接被對方正常關閉,也就是說,對方是正確地調用了close或者shutdown的話,那麼Recv或Send調用就能立刻返回,而且報錯。這是因爲close或者shutdown有個正常的關閉過程,會告訴對方「TCP鏈接已經關閉,你不須要再發送或者接受消息了」。編程
可是,若是鏈接被意外斷開,客戶端並無正常關閉socket。雙方並未按照協議上的四次揮手去斷開鏈接。那麼這時候正在執行Recv或Send操做的一方就會由於沒有任何鏈接中斷的通知而一直等待下去,也就是會被長時間卡住。像這種若是一方已經關閉或異常終止鏈接,而另外一方殊不知道,咱們將這樣的TCP鏈接稱爲半打開的。服務器
解決意外中斷辦法都是利用保活機制。而保活機制分又可讓底層實現也可本身實現。網絡
一種是應用層的心跳機制;socket
還有一種就是啓用TCP的keepAlive機制;tcp
以服務器端爲例,若是當前server端檢測到超過必定時間沒有數據傳輸,那麼會向client端發送一個keep-alive packet(該keep-alive packet就是ACK和當前TCP序列號減一的組合),此時client端應該爲如下三種狀況之一:server
1. client端仍然存在,網絡鏈接情況良好。此時client端會返回一個ACK。server端接收到ACK後重置計時器(復位存活定時器),在必定時間後再發送探測。若是鏈接上一直有數據傳輸,那麼在該時間基礎上向後推延一段時間。基礎
2. 客戶端異常關閉,或是網絡斷開。在這兩種狀況下,client端都不會響應。服務器沒有收到對其發出探測的響應,而且在必定時間(系統默認爲1000 ms)後重復發送keep-alive packet,而且重複發送必定次數。cli
3. 客戶端曾經崩潰,但已經重啓。這種狀況下,服務器將會收到對其存活探測的響應,但該響應是一個復位(RST),從而引發服務器對鏈接的終止。服務器端
在應用層socket編程的表現爲:當tcp檢測到對端socket再也不可用時(不能發出探測包,或探測包沒有收到ACK的響應包),select/epoll會返回socket可讀,而且在recv時返回-1,同時置上errno爲ETIMEDOUT。select
範例: