C#中Socket关闭 Close、Dispose、Shutdown、Disconnect

1、翻译自StackOverflow的推论

原文:https://stackoverflow.com/questions/35229143/what-exactly-do-sockets-shutdown-disconnect-close-and-dispose-do

This answer made me think I have finally reached some glimpse of an understanding. Then I went testing for a bit and here's the summary of a newbie's view. Please correct me if I'm wrong because this is based on inference, not expertise.

Shutdown

Shutdown disables the Send and/or Receive methods, depending on the provided argument. It doesn't disable the underlying protocol handling and it never blocks.

If Send is disabled, it also queues up a zero-byte send packet into the underlying send buffer. When the other side receives this packet, it knows that your socket will no longer send any data.

If Receive is disabled, any data the other side might be trying to send will be lost.

If Receive is disabled without disabling Send, it just prevents the socket from receiving data. Since no zero-byte packet will be sent, the other side won't know anything about it until it tries to send something, and only if the socket's protocol requires acknowledging.

Shutdown 禁用了 Send 或者 Receive 方法,具体取决与调用方法时提供的参数。它不会禁用底层的协议处理并且从不造成阻塞。

如果 Send 被禁用,它仍会向底层的发送缓冲中入列一个零字节(zero-byte)数据包。当接收端收到这个数据包时,它将会知道发送端不会再发送任何数据。

如果 Receive 被禁用,发送端尝试发送的任何数据都会丢失。

如果只禁用了 Receive 但没有禁用 Send ,那么只会阻止Socket接收数据。因为没有发出零字节数据包,所以另一端对此不会有任何感知,除非它发送了某些Socket协议要求进行确认的信息。

Disconnect

First, Disconnect does the equivalent of Shutdown(SocketShutdown.Both).

Then it blocks, waiting for two things:

  1. For all the queued-up send data to be sent.
  2. For the other side to acknowledge the zero-byte packet (if applicable to the underlying protocol).

If you call Disconnect(false), system resources will be freed.

首先,Disconnect 不等同于 Shutdown(SocketShutdown.Both) 。

其次,它会造成阻塞,等待两件事:

  1. 所有已入列的数据被发送。
  2. 另一端确认零字节数据包(如果底层协议适用)。

如果调用了 Disconnect(false) ,系统资源将会被释放。

Close

Close frees system resources. May abruptly stop sending queued-up data. If called with the argument, will wait for the data to be sent, but only up to the specified timeout.

Close 会释放系统资源。它可能会突然停止发送已入列的数据。如果调用此方法时带有参数,那么他会在指定的超时时间内等待数据发送。

Dispose

Dispose is same as the Close overload without the timeout argument. To be more precise, Closewithout timeout is the same as Dispose.

If you use the using block on the socket, it will automatically call Dispose.

Dispose 与 Close 的不带 timeout 参数的重载相同。更准确地讲,应该是 Close 的不带 timeout 参数的重载与 Dispose相同。

如果对Socket使用了 using 块,那么将会自动调用 Dispose 。

2、摘录自MSDN的API说明

原文:https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket

 

Shutdown

禁用Socket的发送和/或接收功能,具体取决于提供给方法的参数。

当使用面向连接的Socket时,关闭Socket前总是应该先调用 Shutdown() 方法。这能够确保在已连接的Socket关闭前,其上的所有数据都发送和接收完成。然后调用 Close() 方法来释放此Socket相关的托管和未托管资源,在关闭后不要尝试复用此Socket。

调用方法时提供 SocketShutdown.Send 参数将会禁止后续对 Send() 方法的调用。如果正在使用无连接的Socket,指定此参数不会产生任何影响。

调用方法时提供 SocketShutdown.Receive 参数将会禁止后续对 Receive() 方法的调用。这一操作对较低的协议层无效。如果正在使用面向连接的协议,调用了 Shutdown() 方法后,在下列任一一种状态退出时,连接会被终止:

  • 在传入网络缓冲区(incoming network buffer)中的数据正等待接收。
  • 到达了更多的数据。

如果正在使用无连接的协议,数据报会被接受和排队。然而,如果对于正在接收的额外数据报没有可用的缓冲空间,这些数据报将会被丢弃,并且不会向发送者返回任何错误信息。不建议对无连接的Socket使用 Shutdown() 方法。

Disconnect

关闭Socket连接,并根据提供给方法的参数决定是否允许重用实例。

如果正在使用面向连接的协议,可以使用 Disconnect() 方法来关闭Socket。这一方法会结束连接并将Socket对象的 Connected 属性设置为 false 。然而,如果提供的 reuseSocket 参数为 true ,则可以重用Socket对象。

为确保在Socket关闭前所有数据都已发送和接收完成,应该在调用 Disconnect() 方法前调用 Shutdown() 方法。

如果需要在调用 Disconnect() 方法时不先行调用 Shutdown() 方法,应该将 DontLingerSocket 选项设置为 false 并指定一个非零的超时间隔,以确保已排队准备向外发送的数据发送完成。之后 Disconnect() 方法会阻塞直到数据发送完成或者超时。如果将 DontLinger设为 false (true, 参见 Socket.LingerState Property https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.lingerstate?view=netcore-3.1)  并设置 0 为超时间隔,Close() 方法会释放连接并自动抛弃已排队准备向外发送的数据。

Close

关闭Socket连接并释放所有相关资源,另外提供一个带有参数的重载来允许在指定的超时时间内将已经排队的数据发送出去。

Close() 方法会关闭远程主机连接,并释放所有与此Socket相关的托管资源和未托管资源。关闭后,Socket对象的 Connected 属性会设置为 false 。

对于面向连接的协议,建议在调用 Close() 方法前先调用 Shutdown() 方法。这能够确保在已连接的Socket关闭前,其上的所有数据都发送和接收完成。

如果需要在调用 Close() 方法时不先行调用 Shutdown() 方法,可以将 DontLingerSocket 选项设置为 false 并指定一个非零的超时间隔,以确保已排队准备向外发送的数据发送完成。之后 Disconnect() 方法会阻塞直到数据发送完成或者超时。如果将 DontLinger 设为 false 并设置 0 为超时间隔,Close() 方法会释放连接并自动抛弃已排队准备向外发送的数据。

Dispose

释放当前Socket实例所使用的未托管资源,并且提供可选操作来释放当前Socket实例所使用的托管资源。

在结束使用Socket时调用 Dispose() 方法,该方法会使Socket处于一种不可用状态。在调用 Dispose() 方法后,必须释放对Socket对象的所有引用,以便垃圾回收器回收被Socket对象占用的内存。

在释放对Socket对象的最后一个引用前总是应该调用 Dispose() 方法。否则,在垃圾回收器调用Socket对象的 Finalize() 方法前,Socket对象所使用的资源无法被释放。

 

总结:在停止使用Socket时,都应该先调用 Shutdown() 方法,以确保所有数据都发送和接收完成。尽量使用 using 块处理Socket对象,这样在结束使用Socket时,只需调用 Shutdown() 方法即可,系统会自动释放资源。

using(var socket = new Socket())
{
	// do something
	// ...
	socket.Shutdown(SocketShutdown.Both);
}

  官方提供的一些示例代码中,在停止使用Socket对象时都先调用了 Shutdown() 方法:

sender.Shutdown(SocketShutdown.Both);  
sender.Close();  

  从官方的另一段示例代码可以看出,对Socket对象调用了 Disconnect() 方法后,并不一定会立即断开连接,在此推测Socket对象会等待正在进行中的数据发送或接收操作。

client.Shutdown(SocketShutdown.Both);
client.Disconnect(true);
if (client.Connected) 
    Console.WriteLine("We're still connnected");
else 
    Console.WriteLine("We're disconnected");

posted on 2020-05-22 08:34  jshchg  阅读(6987)  评论(0编辑  收藏  举报

导航