在高併發、長連接的 Linux 服務端開發中,文件描述符(FD)是極其寶貴的資源。若大量 FD 被「半開連接」佔用,將直接導致服務器無法接受新連接,嚴重影響可用性。本文剖析半開連接的產生機制,並對比內核級與應用級兩種解決方案的優劣。
當客戶端與服務端建立正常 TCP 連接後,若客戶端發生斷網、掉電、系統崩潰等異常情況,且未發送 FIN/RST 包,服務端的 TCP 協議棧將永遠不知道連接已失效。此時:
select/epoll 監測不到可讀/錯誤事件。這種「一邊以為連接正常,另一邊已消失」的狀態,即為半開連接。
TCP 協議棧提供 SO_KEEPALIVE 選項,機制如下:
ECONNRESET,Socket 關閉。ETIMEDOUT,Socket 關閉。大多數內核將保活時間(tcp_keepalive_time)、間隔(tcp_keepalive_intvl)、重試次數(tcp_keepalive_probes)作為全局參數維護,而非針對單個 Socket。若將無活動周期從 2 小時改為 2 分鐘,將影響該主機上所有啟用該選項的連接,引入不可控的網絡開銷與誤判風險,生產環境極少直接修改內核參數。
在應用協議層面模擬保活機制,彈性可控,不依賴內核全局參數:
shutdown(fd, SHUT_RDWR) 再 close(fd),確保發送 FIN 包,讓對端正常感知。總結:SO_KEEPALIVE 適合無法修改應用協議的存量系統;新建系統強烈建議在應用層實現心跳,掌握連接生命周期的完全控制權。