【问题标题】:How to tell when a Socket has been disconnected如何判断 Socket 何时断开连接
【发布时间】:2011-02-03 01:55:21
【问题描述】:

在客户端,我需要知道我的套接字连接何时/是否断开。但是 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

【问题讨论】:

标签: c# .net sockets


【解决方案1】:

在做了一些测试之后,Socket.Connected 的文档似乎是错误的,或者至少具有误导性。 clientSocket.Connected 只会在调用 clientSocket.close() 后变为 false。我认为这是对原始 C Berkeley 套接字 API 及其术语的回归。一个套接字在它有一个与之相关联的本地地址时被绑定,而一个套接字在它有一个与之相关联的远程地址时被连接。即使远程端关闭了连接,本地套接字仍然有关联,所以它仍然是“连接的”。

但是,这是一种确实有效的方法:

!(socket.Poll(0, SelectMode.SelectRead) && socket.Available == 0)

它依赖于这样一个事实,即即使没有可用数据,关闭的连接也会被标记为可读。

如果要检测网络电缆损坏或计算机突然关闭等情况,情况会稍微复杂一些。在这些情况下,您的计算机永远不会收到表明套接字已关闭的数据包。它需要通过发送数据包并注意到没有响应返回来检测远程端已经消失。作为协议的一部分,您可以在应用程序级别执行此操作,也可以使用 TCP KeepAlive 选项。从 .NET 使用 TCP Keep Alive 并不是特别容易。您可能最好在您的协议中构建一个保持活动机制(或者,您可以针对“如何在 .NET 中启用 TCP 保持活动并设置保持活动间隔?”提出一个单独的问题)。

【讨论】:

  • socket.Poll(0, SelectMode.SelectRead) 即使在服务器端连接关闭后,我也总是返回 true。
  • msdn 文档还说,Poll 方法无法检测某些条件“例如网络电缆损坏,或者远程主机被不正常地关闭”。我只是想知道服务器端的socket有没有,我不在乎为什么没有。
  • @BrowserKingKoopa:哎呀!我最初发布的代码测试了套接字 disconnected。我已经修好了。当远程端断开连接时,Poll() 将返回 true 但 socket.Available 将为 0。我还添加了一段关于处理损坏的网线的段落。
【解决方案2】:

只需照常写入您的套接字即可。当您的数据无法传递的异常断开连接时,您会知道。

如果你没有什么可写的......那谁在乎它是否断开连接?它现在可能已断开连接,但在您需要它之前回来 - 为什么要拆掉它,然后循环重新连接,直到链接修复......尤其是当您无论如何都无话可说时?

如果它困扰您,请在您的协议中实现保持活动状态。然后你每隔 30 秒左右就会有话要说。

【讨论】:

  • +1 如果服务器发送了所有数据并且它没有保持活动状态,它应该关闭套接字并且客户端应该正确处理它。如果从客户端接收到它想要的所有数据,它也会关闭连接,服务器将处理它。您应该只在 s.Receive/s.Send 上捕获异常以了解某人何时断开连接,Poll 也会在断开连接的套接字上抛出异常
  • 套接字可以双向工作,主要问题是当您使用套接字推送但套接字是 d/c 时您不知道它,因此您没有数据,因为读取网络流提供数据但没有错误。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    我建议剥离高级语言的内容并探索低级 IO 发生的情况。

    我探索过的最低限度是在编写 isectd 时(在 sourceforge 上找到)。使用 select() 系统调用,已关闭套接字的描述符变为可读取状态,当 isectd 尝试执行 recv() 时,可以确认套接字的断开状态。

    作为一种解决方案,我建议不要编写自己的套接字 IO 并使用其他人的中间件。那里有很多优秀的候选人。不要忘记考虑简单的排队服务。

    PS。我会提供上述所有内容的 URL,但我的声誉 (1) 不允许。

    【讨论】:

      【解决方案5】:

      clientSocket.Send() 方法是否等待数据包被确认/确认?

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

      【讨论】:

      • 您可以在发送后放置一个 Thread.Sleep。无论您等待多久,clientSocket.Connected 都保持真实。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-01
      • 1970-01-01
      • 2014-08-22
      • 2013-12-31
      • 2019-03-13
      • 2020-09-20
      相关资源
      最近更新 更多