如何判断 Socket 连接已断开

16

在客户端,我需要知道套接字连接何时/是否已经断开。然而,Socket.Connected属性始终返回true,即使在服务器端已经断开并且我尝试通过它发送数据后。有人可以帮助我弄清楚这里发生了什么。我需要知道套接字何时已经断开。

        Socket serverSocket = null;
        TcpListener listener = new TcpListener(1530);
        listener.Start();
        listener.BeginAcceptSocket(new AsyncCallback(delegate(IAsyncResult result)
        {
            Debug.WriteLine("ACCEPTING SOCKET CONNECTION");
            TcpListener currentListener = (TcpListener)result.AsyncState;
            serverSocket = currentListener.EndAcceptSocket(result);
        }), listener);


        Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Debug.WriteLine("client socket connected: " + clientSocket.Connected);//should be FALSE, and it is
        clientSocket.Connect("localhost", 1530);
        Debug.WriteLine("client socket connected: " + clientSocket.Connected);//should be TRUE, and it is

        Thread.Sleep(1000);
        serverSocket.Close();//closing the server socket here
        Thread.Sleep(1000);

        clientSocket.Send(new byte[0]);//sending data should cause the socket to update its Connected property.
        Debug.WriteLine("client socket connected: " + clientSocket.Connected);//should be FALSE, but its always TRUE

请参考https://dev59.com/r3RB5IYBdhLWcg3wF0DH进行进一步讨论。 - EricLaw
5个回答

10
经过一些测试,看起来 Socket.Connected 的文档是错误的,或者至少是误导性的。只有在调用 clientSocket.close() 后,clientSocket.Connected 才会变成 false。我认为这是对原始的 C Berkeley sockets API 及其术语的遗留影响。当套接字与本地地址相关联时,它就被绑定了;当套接字与远程地址相关联时,它就连接了。即使远程端已经关闭了连接,本地套接字仍然保持着关联,所以它仍然处于“已连接”状态。
不过,下面有一个方法是可以工作的:
!(socket.Poll(0, SelectMode.SelectRead) && socket.Available == 0)
它依赖于这样一个事实,即关闭的连接即使没有可用数据也将被标记为可读。
如果您想检测诸如网络电缆损坏或计算机突然关机等情况,则情况会更加复杂。在这些情况下,您的计算机从未收到指示套接字已关闭的数据包。它需要通过发送数据包并注意到没有响应返回来检测远程方已经消失。您可以将其作为协议的一部分在应用程序级别执行此操作,也可以使用TCP KeepAlive选项。从.NET使用TCP Keep Alive不是特别容易的;您最好在您的协议中构建一个保持活动的机制(或者您可以问一个单独的问题,“如何在.NET中启用TCP KeepAlive并设置保持活动间隔?”)。

socket.Poll(0, SelectMode.SelectRead) 对我来说总是返回 true,即使服务器端的连接已经关闭。 - BowserKingKoopa
此外,MSDN文档指出Poll方法无法检测某些情况,例如“断开的网络电缆或远程主机非正常关闭”。我只想知道服务器端套接字是否存在,而不关心为什么它不存在。 - BowserKingKoopa
@BrowserKingKoopa:哎呀!我最初发布的代码测试了套接字是否已断开连接。我已经修复了它。当远程一侧已经断开连接时,Poll()将返回true,但socket.Available将为0。我还添加了一段关于处理破损网络电缆的内容。 - Daniel Stutzbach

4

只需像平常一样向您的套接字写入数据即可。如果出现异常,说明连接已断开且数据无法传送。

如果您没有要写入的内容...那么如果它断开了谁在乎呢?它可能现在已经断开了,但在您需要它之前回来 - 为什么要拆除它,然后循环重新连接直到链接修复...特别是当您根本没有要说的话时?

如果这让您感到困扰,请在您的协议中实现保持活动状态。然后您每隔30秒就有话可说。


如果服务器发送了所有数据并且不保持连接,它应该关闭套接字,客户端应该正确处理它。从客户端的角度来看,如果它收到了所有想要的数据,它也会关闭连接,服务器将处理它。你只需要在s.Receive/s.Send上捕获异常,就可以知道何时有人断开连接,Poll也会在断开的套接字上抛出异常。 - hoodoos
套接字可以双向工作,主要问题是当您使用套接字进行推送时,但套接字在您不知情的情况下断开连接,因此读取网络流会得到无数据但没有错误的结果。 - Royi Mindel

3
也许解决方案是通过发送一些虚拟数据来检查是否超时?

2
我建议去掉高级语言的内容,探索在低级IO发生了什么。
我所研究的最低层是在编写isectd时(可在sourceforge上找到)。使用select()系统调用,关闭套接字的描述符变为可以读取,并且当isectd尝试recv()时,可以确认套接字的断开状态。
作为解决方案,我建议不要编写自己的套接字IO,而是使用其他人的中间件。有很多好的选择。别忘了考虑简单的队列服务。
PS. 我会提供以上所有内容的URL,但我的声望(1)不允许这样做。

0

客户端的clientSocket.Send()方法是否等待数据包被确认或拒绝?

如果不是,那么您的代码会在套接字仍在尝试弄清楚发生了什么时飞到下一行。


你可以在发送后加入Thread.Sleep。无论你等待多久,clientSocket.Connected都会保持为true。 - BowserKingKoopa

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