Socket.Close并不能真正关闭TCP套接字吗?(C#)

11
似乎使用socket.Close()关闭TCP套接字时,无法完全关闭套接字。 在以下示例中,我正在尝试连接到未打开的端口9999上的example.com,并在短暂超时后尝试关闭套接字。
  for (int i = 0; i < 200; i++)
  {
    Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    sock.LingerState = new LingerOption(false, 0);
    sock.BeginConnect("www.example.com", 9999, OnSocketConnected, sock);
    System.Threading.Thread.Sleep(50);
    sock.Close();
  }

但是当我在循环完成后查看netstat时,我发现有很多半开放的套接字:

  TCP    israel-xp:6506         www.example.com:9999   SYN_SENT
  TCP    israel-xp:6507         www.example.com:9999   SYN_SENT
  TCP    israel-xp:6508         www.example.com:9999   SYN_SENT
  TCP    israel-xp:6509         www.example.com:9999   SYN_SENT

编辑 。 缺少一些上下文信息。我正在使用beginconnect,因为我希望套接字连接失败(9999未打开),在我的实际代码中,一旦设置了计时器,我就调用socket.Close()。 在OnSocketConnected中,我调用EndConnect,它会引发异常(尝试调用已处理的对象的方法)。 我的目标是对套接字连接阶段进行短暂超时。

你有什么线索吗? 谢谢!

4个回答

18

按设计,关闭 socket 前应始终调用 Shutdown

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

这样做有效地禁用了套接字的发送和接收功能,因此即使操作系统仍然控制着它,一旦你关闭它,它将不会接受传入的数据。

Jon Skeet 还指出,由于您正在异步打开连接,可能在尝试关闭连接时实际上正在进行连接。但是,如果您在其上调用Shutdown,则不允许接收信息,就像您现在遇到的情况。

编辑:您只能Shutdown已连接的套接字,请在编写代码时注意此问题。


如果套接字尚未连接,ShutDown会抛出异常 - 这恰好是我的情况。 - r0u1i
然后,您应该使用OnSocketConnected来调用ShutDown并关闭它,或者类似的操作。 - Ed Altorfer
@Dean,今天这是第二个问题,Jon和我同时回答了。他已经有太多的声望了,也许他可以给我一些。 :) - Ed Altorfer
但是从OnSocketConnected调用ShutDown错过了尝试超时连接过程的重点。等待OnSocketConnected被调用可能需要很长时间。 - r0u1i
但这没关系,因为你不再关心套接字了。如果您尝试连接它,则没有完全杀死套接字的方法。您需要设置标志,然后关闭它,或者不使用那样的异步连接。或者,您可以只关闭它,并意识到在操作系统清理套接字之前可能会发送一些消息。 - Ed Altorfer

14

这将关闭套接字的.NET部分。然而,根据TCP规范,操作系统必须保持套接字的低级细节一段时间以便检测重传等情况。在这种情况下,它很可能会保留套接字一段时间,以便检测发送的SYN数据包的回复,这样可以更合理地回复,而不会将回复与进一步发送的数据包混淆。


2
这听起来很有道理,但将linger设置为false不应该解决它吗? - r0u1i
哦,Linger不会做这个。因此,实际上没有办法让Windows终止套接字。 - r0u1i
1
默认情况下,它将在 FIN_WAIT 状态停留 4 分钟,然后 Windows 真正关闭它。更有趣的是,如果您重新打开到相同目的地/端口的另一个连接,Windows 将重用与 FIN_WAIT 匹配的连接。 - Jay
2
@Jay:如果处理那些倾向于在每次上电启动新套接字时选择相同目标端口号的TCP硬件,后一点可能会引起相当大的烦恼。如果连接处于活动状态,并且这种情况发生时,设备将确定该端口号不好并尝试一个新的端口号。然而,如果连接处于FIN_WAIT状态,设备需要很长时间才能发现有什么不对劲。 - supercat

10

您正在调用 *Begin*Connect - 因此它是异步的。您很可能在连接之前就试图关闭套接字,因此当它连接时,它仍然保持打开状态。

尝试同步连接 - 或在OnSocketConnected中关闭它,以便您可以看到关闭实际连接的套接字的效果。


我故意使用BeginConnect,并且我的实际代码在调用Close之前等待超时。只是在这种套接字上调用EndConnect(其中dest端口未打开)会需要很长时间才能完成。 换句话说,我希望在X毫秒后超时套接字连接阶段(而不是Windows默认的荒谬高数量级的毫秒数)。 - r0u1i
我认为调用 Socket.Dispose() 是目前 API 中取消异步连接的唯一方法。看起来这是有意为之的。 - binki

0
你正在每个循环中初始化一个新的Socket...使用*.close()关闭旧的,然后在开始时使用与之前Socket相同的参数创建一个新的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接