TCP之非阻塞connect和accept
套接字的預設狀態是阻塞的,這就意味著當發出一個不能立即完成的套接字呼叫時,其程序將被投入睡眠,等待響應操作完成,可能阻塞的套接字呼叫可分為以下四類:
(1) 輸入操作,包括read,readv,recv,recvfrom,recvmsg;
(2) 輸出操作,包括write,writev,send,sendto,sendmsg;
(3) 接受外來連線,即accept函式。
(4) 發起外出連線,即tcp的connect函式;
非阻塞connect:
當一個非阻塞的tcp套接字上呼叫connect時,connect將立即返回一個EINPROGRESS錯誤,不過已經發起的tcp三路握手繼續進行。我們接著使用select檢測這個連線或成功或失敗的已建立條件。非阻塞connect有三個用途:
(1) 我們可以把三路握手疊加在其他處理上,完成一個connect要花的RTT時間,而RTT波動很大,從區域網上的幾毫秒到幾百毫秒甚至是廣域網的幾秒。這段時間內也許有我們想要執行的其他工作可執行;
(2) 我們可以使用這個技術同時建立多個連線;這個技術隨著web瀏覽器流行起來;
(3) 既然使用select等待連線建立,我們可以給select指定一個時間限制,使得我們能夠縮短connect的超時。
非阻塞connect細節:
(1) 儘管套接字是非阻塞的,如果連線到的伺服器在同一個主機上,那麼當我們呼叫connect時候,連線通常立刻建立,我們必須處理這種情形;
(2) 源自Berkeley的實現(和posix)有關select和非阻塞connect的以下兩個原則:
--(a) 當連線成功建立時,描述符變為可寫;
--(b) 當連線建立遇到錯誤時,描述符變為既可讀又可寫;
非阻塞accept:
在比較忙的伺服器中,在建立三次握手之後,呼叫accept之前,可能出現客戶端斷開連線的情況,再這樣的情況下;如,三次握手之後,客戶端傳送rst,然後伺服器呼叫accept。posix指出這種情況errno設定為CONNABORTED;
注意Berkeley實現中,沒有返回這個錯誤,而是EPROTO,同時完成三次握手的連線會從已完成佇列中移除;在這種情況下,如果我們用select監聽到有新的連線完成,但之後又被從完成佇列中刪除,此時如果呼叫阻塞accept就會產生阻塞;
解決辦法:
(1) 使用select監聽套接字是否有完成連線的時候,總是把這個監聽套接字設定為非阻塞;
(2) 在後續的accept呼叫中忽略以下錯誤,EWOULDBLOCK(Berkeley實現,客戶中止連線), ECONNABORTED(posix實現,客戶中止連線), EPROTO(serv4實現,客戶中止連線)和EINTR(如果有訊號被捕獲);