windows 下 socket 的 shutdown 和 closesocket 的区别

2022-06-23 13:00:04 浏览数 (1)

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的数据结构:

代码语言:javascript复制
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 函数设置,optnameSO_LINGERoptvallinger 数据结构。也可以通过设置 optnameSO_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的文件描述符。 ↩︎

0 人点赞