天天看點

windows 下 socket 的 shutdown 和 closesocket 的差別

It is important to distinguish the difference between shutting down a socket connection and closing a socket.

分辨關閉(shutdown)一個socket連接配接和關閉一個socket的差別是重要的。

以下為了區分二者,分别用英文的shutdown和close表述。

shutdown a socket connection

shutdown一個socket連接配接涉及到兩個端點 (endpoint) 間協定消息的交換,以下稱為shutdown序列 (sequence)。

有兩種shutdown序列:

  • 優雅的 (graceful)
  • 強硬的 (abortive (also called hard))

優雅shutdown,顧名思義,就是比較優雅,如何個優雅法呢?我們知道,當應用層将要傳輸的資料傳遞給傳輸層時,傳輸層不一定會馬上進行傳輸,而是有一個等待隊列,是以在shutdown時,隊列中還有未發送的消息。把未發送的資料發送完再關閉連接配接,就是優雅shutdown。因為我們正常來說應該預期應用層發送的消息應該被完成發送,是以這種shutdown方式比較優雅。

相反,強硬shutdown就是丢棄等待隊列中的消息。

shutdown序列的發送也會被用來給相關應用層提供一個 FD_CLOSE 訓示,來提示應用層有一個shutdown正在進行。

close a socket

而close一個socket是另外一個含義,它會使得socket句柄1被釋放,這樣應用程式就不能再以任何方式引用或使用該套接字。

在Windows Socket下,有兩個函數可以用來發起一個shutdown序列,分别是shutdown和WSASendDisconnect。而closesocket函數用于釋放套接字句柄并釋放任何相關資源。

容易引起困惑的是,closesocket函數會隐式地引發一個shutdown序列(如果序列還沒發生的話)。事實上,使用closesocket來發起shutdown序列并釋放socket句柄已成為一個普遍的程式設計實踐。

為了促進這一使用,socket接口提供了控制機制,通過套接字選項允許程式員指明隐含的shutdown序列是應該優雅還是強硬,和指明closesocket函數是否應該逗留 (linger) 以允許優雅的shutdown序列有時間完成。這些重要的差別和以這種方式使用closesocket的後果仍然沒有被廣泛了解。

我們來看看linger的資料結構:

typedef struct linger {
  u_short l_onoff;
  u_short l_linger;
} LINGER, *PLINGER, *LPLINGER;           

複制

下面對其資料成員進行分析。

  • l_onoff

    值含義0socket 不會保持開啟,為預設值非0socket 會保持開啟一段特定時間

  • l_linger

    指明了在closesocket調用之後,為了允許等待隊列中的資料被發送而保持socket開啟的時間,以秒為機關,隻在 l_onoff 為非零值的時候有效。

以上資料結構可以通過setsockopt 函數設定,optname 為 SO_LINGER,optval 是 linger 資料結構。也可以通過設定 optname 為 SO_DONTLINGER 來設定 l_onoff 的值。

closesocket函數的語義會受linger結構值得影響,如下:

l_onoff l_linger Type of close Wait for close?
zero Do not care Graceful close No
nonzero zero Hard Yes, but l_linger is zero, so the waiting time is zero, equivalent to No
nonzero nonzero Graceful if all data is sent within timeout value specified in the l_linger member.Hard if all data could not be sent within timeout value specified in the l_linger member. Yes

值得注意的是,linger的含義是逗留,即是否阻塞,并不是是否為優雅關閉。

優雅關閉并不一定需要等待,比如 l_onoff 預設值為 0,此時調用closesocket之後,closesocket會立刻傳回,但等待隊列中的資料仍然在傳輸層發送着,并且在某段時間内,Windows Sockets provider不能釋放這個socket和其他資源,也就是說其他應用程式仍然可以使用該socket。

原文:

If the l_onoff member of the LINGER structure is zero on a stream socket, the closesocket call will return immediately and does not receive WSAEWOULDBLOCK whether the socket is blocking or nonblocking. However, any data queued for transmission will be sent, if possible, before the underlying socket is closed. This is also called a graceful disconnect or close. In this case, the Windows Sockets provider cannot release the socket and other resources for an arbitrary period, thus affecting applications that expect to use all available sockets. This is the default behavior for a socket.

而當l_onoff被設為非零值時,就不一定是優雅shutdown了,此時closesocket會阻塞直到隊列中的資料被發送完畢或者l_linger設定的時間已到。如果等待隊列中的資料不能在l_linger時間内發送完畢,那麼剩下的就會被丢棄。是以當l_onoff為非零值時,相當于有一定的時間容忍等待隊列的資料繼續發送。

參考:

[1] Graceful Shutdown, Linger Options, and Socket Closure

[2] LINGER structure

[3] closesocket function

  1. handle: A handle is the part of an object such as a tool, bag, or cup that you hold in order to be able to pick up and use the object. “柄、把”的意思,比如門把手,socket handle經常被翻譯成“句柄”感覺有點難了解,其實就像門把手,socket句柄就是用來使用socket的,我們通過操作socket檔案描述符來操作對應的socket,是以這裡的句柄指的就是socket的檔案描述符。 ↩︎