天天看點

TCP/IP中的SO_REUSEADDR和SO_KEEPALIVE

SO_REUSEADDR

據《UNIX網絡程式設計》卷一所說,此選項用于4種用途

我本人在TCP伺服器上隻處理過兩種,其他情況如有需要,請參考《UNIX網絡程式設計》卷一

1.引自《UNIX網絡程式設計》卷一

多程序綁定同一對IP和PORT,按時序舉個多程序模式tcp伺服器的例子

 a.父程序進行監聽;

 b.來syn包,請求連接配接;

 c.派生子程序處理使用者長連接配接請求;

 d.父程序挂掉;

 e.重新開機父程序。

此時,子程序複制繼承父程序的監聽socket,并且子程序并沒有消亡,如果在監聽前沒有對監聽socket設定此選項,重新開機父程序時,會導緻bind調用失敗。

2.先明确一個概念:socket是系統級的對象,并非屬于某個程序自己的資源。

言歸正傳,不考慮多程序方式的tcp伺服器,舉個最簡單的單程序+epoll方式的TCP伺服器的例子

伺服器程序挂掉(核心自動關閉所有此程序打開的檔案,其中包括socket),監聽socket進入TIME_WAIT狀态(此時socket仍存在于系統中),重新啟動伺服器,如果在監聽前沒有對監聽socket設定此選項,此時會導緻bind調用失敗。

KEEPALIVE機制,是TCP協定規定的TCP層(非應用層業務代碼實作的)檢測TCP本端到對方主機的TCP連接配接的連通性的行為。避免伺服器在用戶端出現各種不良狀況時無法感覺,而永遠等在這條TCP連接配接上。

該選項可以設定這個檢測行為的細節,如下代碼所示:

int keepAlive =1;    // 非0值,開啟keepalive屬性

intkeepIdle =60;    // 如該連接配接在60秒内沒有任何資料往來,則進行此TCP層的探測

intkeepInterval =5; // 探測發包廂隔為5秒

int keepCount = 3;       //嘗試探測的次數.如果第1次探測包就收到響應了,則後2次的不再發

setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive,sizeof(keepAlive));

setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE,(void*)&keepIdle,sizeof(keepIdle));

setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void*)&keepInterval,sizeof(keepInterval));

setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void*)&keepCount,sizeof(keepCount));

設定該選項後,如果60秒内在此套接口所對應連接配接的任一方向都沒有資料交換,TCP層就自動給對方發一個保活探測分節(keepaliveprobe)。這是一個對方必須響應的TCP分節。它會導緻以下三種情況:

   對方接收一切正常:以期望的ACK響應。60秒後,TCP将重新開始下一輪探測。

   對方已崩潰且已重新啟動:以RST響應。套接口的待處理錯誤被置為ECONNRESET。

   對方無任何響應:比如用戶端那邊已經斷網,或者用戶端直接當機。以設定的時間間隔嘗試3次,無響應就放棄。套接口的待處理錯誤被置為ETIMEOUT。

全局設定可更改/etc/sysctl.conf,加上:

net.ipv4.tcp_keepalive_intvl= 5

net.ipv4.tcp_keepalive_probes = 3

net.ipv4.tcp_keepalive_time = 60

在程式中表現為:

阻塞模型下,當TCP層檢測到對端socket不再可用時,核心無法主動通知應用層出錯,隻有應用層主動調用read()或者write()這樣的IO系統調用時,核心才會利用出錯來通知應用層。

非阻塞模型下,select或者epoll會傳回sockfd可讀,應用層對其進行讀取時,read()會報錯。

一點經驗:

實際上我們在做伺服器程式的時候,對用戶端的保活探測基本上不依賴于這個TCP層的keepalive探測機制。

而是我們自己做一套應用層的請求應答消息,在應用層實作這樣一個功能。