【问题标题】:how network stream is detecting disconnection网络流如何检测断开连接
【发布时间】:2016-09-03 10:03:21
【问题描述】:

我最近开始学习套接字编程并编写了一个简单的异步 tcp 服务器,它可以很好地从单个客户端发送接收。这是它的简化代码:

//accpet loop

while (true)
{
   var client = await listener.AcceptTcpClientAsync();
   new AppClient(client).Start();
} 

 // and here is the start method in AppClient class
 public async void Start()
 {
        /// TcpClient is a class property which is in scope of this method
       using (var stream = TcpClient.GetStream())
       {
           string message = string.Empty;
           byte[] buffer = new byte[1000];
           int bytesRead = 0;
           while (true)
           {
                try
                {
                    bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
                }         
                catch (Exception ex)
                {
                        MessageBox.Show(ex.Message);
                        break;
                 }

                 // decode simple text message
                 message += Encoding.UTF8.GetString(buffer, 0, bytesRead);


           }
       }
  }

它以单线程方式处理数千个连接,但 让我感到不安的是,无论我如何破坏客户端(杀死进程和......),ReadAsync 方法都会立即抛出一个异常,这基本上与我的想法相冲突。 (据我所知,在 tcp 中检测断开的套接字应该不是那么容易)

我是不是做错了什么?

【问题讨论】:

  • 你真的不应该这样做catch (Exception ex) - 这是一种反模式。
  • 谢谢,我会记住的。
  • 对于他正在做的事情,捕捉异常绝对没有错

标签: c# sockets asynchronous tcp network-programming


【解决方案1】:

您应该将.ReadAsync() 与cancellationToken 一起使用。然后更改您的循环以检查令牌值。当您想杀死客户端时,您会发出令牌信号。

同时检查你的变量bytesRead。如果它以0 返回,则连接刚刚关闭。

【讨论】:

  • 非常感谢您的取消提示。但是 bytesRead 永远不会为零,带有 await 的 ReadAsync 方法会暂停该方法,直到有东西要读取。
  • 如果服务器端断开连接会返回0。
【解决方案2】:

让我心烦意乱的是,无论我如何破坏客户 (杀死进程和......) ReadAsync 方法立即抛出一个 异常

欢迎来到网络(套接字)编程。您必须让自己对这些异常感到满意,因为在这些类型的应用程序中遇到异常是很常见的。此行为是设计使然。

您的应用程序应该以最终用户获得良好消息而不是异常消息的方式开发。

...ReadAsync 方法立即抛出异常 基本上和我的想法相冲突。 (据我阅读检测 tcp 中的断开连接的套接字应该不是那么容易)

原因是:

TcpClient.Connected Property 获取客户端套接字在最后一次 I/O 操作时的连接状态。当它返回 false 时,客户端套接字要么从未连接,要么不再连接。

因为 Connected 属性仅反映最近操作时的连接状态,您应该尝试发送或接收消息以确定当前状态。消息发送失败后,该属性不再返回true。

请注意,此行为是设计使然。您无法可靠地测试连接状态,因为在测试和发送/接收之间的时间内,连接可能已经丢失。您的代码应假定套接字已连接,并优雅地处理失败的传输。

【讨论】:

  • 感谢您的回复,在那个无限循环中有 await stream.ReadAsync() 可以吗? (我这样做是为了继续监听来自客户端的新数据)
  • @SHM - 是的,没关系。如果客户端要发送多次事先不知道的数据,那么是的,您必须继续等待和接收数据。一旦客户端关闭套接字,您的循环就会中断,从而引发异常。
  • @SHM - 另外,请耐心地重新阅读我的答案的最后两段。这些是非常有用的词。祝您在网络编程方面的经验好运。
  • 是的,我读到了,但是优雅地关闭 = 发送关闭数据包。现在,如果客户端没有机会通过 tcp 发送关闭数据包,ReadAsync 如何抛出“强制关闭异常”?
  • @SHM - 如果客户端没有机会通过 tcp 发送关闭数据包,ReadAsync 如何抛出“强制关闭异常”,这是什么意思?为您引用 MSDN 来源所说的话:The Close method marks the instance as disposed and requests that the associated Socket close the TCP connection. Based on the LingerState property, the TCP connection may stay open for some time after the Close method is called when data remains to be sent. There is no notification provided when the underlying connection has completed closing.
【解决方案3】:

它不是“检测”断开连接,它会抛出异常,因为.ReadAsync 不断尝试获取是否有可读取的数据,是否将其写入缓冲区,如果数据长度 == 缓冲区长度然后结束函数。所有这些步骤(可能还有更多)涉及Stream,它绑定到socket。然后,如果套接字已关闭(通过代码或手动),则流将关闭,如果您尝试对已关闭的流执行任何操作,则会引发异常。

【讨论】:

  • Readasync 只检查一次我在等待它,一旦数据在套接字上可用,其余的方法 id 就会执行。
猜你喜欢
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 2018-04-13
  • 2012-11-03
相关资源
最近更新 更多