【问题标题】:TcpListener: Detecting a client disconnect as opposed to a client not sending any data for a whileTcpListener:检测客户端断开连接,而不是客户端一段时间不发送任何数据
【发布时间】:2012-07-24 15:27:38
【问题描述】:

我正在研究如何在使用 TcpListener 时检测“客户端断开连接”。

所有的答案似乎都与这个相似: TcpListener: How can I detect a client disconnect?

基本上,从流中读取,如果 Read() 返回 0,则客户端已断开连接。

但这是假设客户端在发送每个数据流后断开连接。 我们在 TCP 连接/断开连接开销既慢又昂贵的环境中运行。

我们建立一个连接,然后我们发送一些请求。

伪代码:

client.Connect();
client.GetStatus();
client.DoSomething();
client.DoSomethingElse();
client.AndSoOn();
client.Disconnect();

Connect 和 Disconnect() 之间的每次调用都会向服务器发送一个数据流。 服务器知道如何分析和处理流。

如果让 TcpListener 在没有断开连接的情况下循环读取它会读取并处理所有消息,但是在客户端断开连接后,服务器无法知道这一点并且 它永远不会释放客户端并接受新客户端。

var read = client.GetStream().Read(buffer, 0, buffer.Length);

if (read > 0) 
{
   //Process
}

如果我让 TcpListener 在读取 == 0 时删除客户端,它只接受 第一个数据流只在之后立即丢弃客户端。

当然这意味着新客户端可以连接。

通话之间没有人为的延迟, 但就计算机时间而言,两次通话之间的时间当然是“巨大的”, 所以总会有一段时间 read == 0 即使这并不意味着 客户端已经或应该断开连接。

var read = client.GetStream().Read(buffer, 0, buffer.Length);

if (read > 0) 
{
   //Process
}
else
{
   break;   //Always executed as soon as the first stream of data has been received
}

所以我想知道...有没有更好的方法来检测客户端是否已断开连接?

【问题讨论】:

    标签: c# tcplistener


    【解决方案1】:

    艾伦的回答为我解决了这个问题。以防其他人面临同样的问题 这是一些使用 Socket.Receive 方法的“示例”代码:

    private void AcceptClientAndProcess()
    {
        try
        {
            client = server.Accept();
            client.ReceiveTimeout = 20000;
        }
        catch
        {
            return;
        }
    
        while (true)
        {
            byte[] buffer = new byte[client.ReceiveBufferSize];
            int read = 0;
    
            try
            {
                read = client.Receive(buffer);
            }
            catch
            {
                break;
            }
    
            if (read > 0)
            {
                //Handle data
            }
            else
            {
                break;
            }
        }
    
        if (client != null)
            client.Close(5000);
    }
    

    你在某处循环调用 AcceptClientAndProcess()。

    下面一行:

    read = client.Receive(buffer);
    

    将阻塞直到任一

    • 接收到数据,(读取> 0)在这种情况下您可以处理它
    • 连接已正确关闭(读取 = 0)
    • 连接突然关闭(抛出异常)

    最后两种情况中的任何一种都表明客户端不再连接。

    Socket.Accept() 方法周围的 try catch 也是必需的 因为如果客户端连接在连接阶段突然关闭,它可能会失败。

    请注意,确实为读取操作指定了 20 秒的超时时间。

    【讨论】:

      【解决方案2】:

      您可以使用NetworkStream.Socket 属性获取底层套接字,并使用它的Receive 方法进行读取。

      NetworkStream.Read 不同,Socket.Receive 的链接重载将阻塞,直到已读取指定数量的字节,并且仅在远程主机关闭 TCP 连接时才返回零。


      更新:@jrh 的评论是正确的,NetworkStream.Socket 是受保护的属性,在此上下文中无法访问。为了获取客户端Socket,您可以使用TcpListener.AcceptSocket方法返回与新建立的连接对应的Socket对象。

      【讨论】:

      • NetworkStream.Socket 未公开字节 TcpListener.GetStream() 返回的 NetworkStream 实例。但我现在正在研究直接使用 Socket 类
      • 好的,直接使用套接字并使用 Receive 方法就像一个魅力。接受客户端和尝试接收时需要尝试捕获。谢谢!
      • 当.NET 通过Client 属性提供对TcpClient 的套接字的几乎不受限制的访问时,为什么不让您访问NetworkStream.Socket?这只是一个 .NET 设计怪癖,还是存在无法获得NetworkStream 的套接字的情况?此外,您可能需要修改此答案,因为 NetworkStream.Socket 受到保护且无法访问。
      • @jrh 你对受保护的财产是正确的,我已经更新了答案。最有可能的是,NetworkStream 不提供对 Socket 的直接访问,因为它提供了限制读取和写入的能力。请参阅带有 FileAccess 参数的 constructor overloads
      【解决方案3】:

      The documentation for NetworkStream.Read 没有反映这一点,但根据我的经验,如果端口仍然打开并且没有数据可用,“NetworkStream.Read”会阻塞,但如果端口已关闭,则返回 0。

      我遇到了this problem from the other side,因为如果当前没有可用数据,NetworkStream.Read 不会立即返回 0。您必须使用NetworkStream.DataAvailable 来确定@​​987654326@ 现在是否可以读取数据。

      【讨论】:

        猜你喜欢
        • 2010-11-15
        • 2014-04-14
        • 1970-01-01
        • 2012-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多