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序列,分别是shutdownWSASendDisconnect。而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 函数设置,optnameSO_LINGERoptvallinger 数据结构。也可以通过设置 optnameSO_DONTLINGER 来设置 l_onoff 的值。

closesocket函数的语义会受linger结构值得影响,如下:

l_onoffl_lingerType of closeWait for close?
zeroDo not careGraceful closeNo
nonzerozeroHardYes, but l_linger is zero, so the waiting time is zero, equivalent to No
nonzerononzeroGraceful 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的文件描述符。 ↩︎

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Socket编程中,shutdown()和close()都是用来关闭一个套接字(Socket)的方法,但二者有不同的作用。 shutdown()方法是用来关闭一个已连接的套接字,它可以在客户端和服务器端分别调用。shutdown()的调用会使得套接字不能再进行数据的发送和接收,但它可以继续进行一些管理操作,例如可以发送一些控制信息给对方,告知对方套接字已关闭。 而close()方法是用来关闭一个套接字的连接,它可以在客户端和服务器端分别调用。调用close()方法后,套接字不再使用,也不会再接收或发送数据。当客户端调用close()时,它会向服务器发送一个关闭连接的请求,服务器收到请求后也会调用close()方法关闭套接字。 ### 回答2: 在Socket编程中,shutdownclose都是用于关闭连接的方法,但它们有一些区别。 首先,shutdown是用于半关闭连接的操作,它可以关闭Socket的输入和输出流的其中一方。shutdown方法可以接受一个参数,该参数可以是常量Socket.SHUT_RD,用于关闭输入流;也可以是常量Socket.SHUT_WR,用于关闭输出流;还可以是常量Socket.SHUT_RDWR,用于关闭输入和输出流。通过这种方式,可以控制关闭连接的一方。 相比之下,close是用于完全关闭连接的操作,它会关闭Socket的输入和输出流,并释放相关的资源。调用close方法后,再次尝试使用该Socket将会抛出异常。 其次,shutdown可以在一个Socket处于连接状态时多次调用,并且可以对输入和输出流进行独立的关闭,而close方法只能在一个Socket连接被完全关闭后调用一次。 最后,通过调用shutdown方法关闭连接,可以保留Socket的状态信息和相关资源,并继续使用该Socket进行数据传输。而调用close方法关闭连接则会释放Socket相关的资源,并且无法再次使用该Socket。 综上所述,shutdownclose方法在Socket编程中都可以用于关闭连接,但shutdown方法可以控制关闭连接的一方,可以多次调用并保留Socket状态,而close方法只能完全关闭连接并释放相关资源。 ### 回答3: 在Socket编程中,shutdown()和close()是两个不同的方法,用于关闭连接和释放Socket资源。 shutdown()方法用于关闭已连接的Socket连接,它需要传入一个参数,来指定关闭的方式。参数可以是SHUT_RD、SHUT_WR或SHUT_RDWR。SHUT_RD表示禁止读取数据,SHUT_WR表示禁止写入数据,SHUT_RDWR表示同时禁止读写数据。通过调用shutdown()方法,我们可以选择关闭连接的读端、写端或者同时关闭两者,这样就可以保持双方的连接而不再传输数据。 相比之下,close()方法是用于完全关闭Socket连接,它不需要传入任何参数。调用close()方法会立即关闭Socket连接,并释放所有与该Socket相关的资源。关闭连接后,将无法再进行读取或写入操作,也无法再建立连接。 总结来说,shutdown()方法是暂时关闭Socket连接的一种手段,可以选择性地关闭读端、写端或者两者。而close()方法是永久关闭Socket连接,并释放相关资源的一种方式。关闭连接后,将无法再进行数据传输或者建立新的连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值