6

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

所有的答案似乎都与这个相似: TcpListener:如何检测客户端断开连接?

基本上,从流中读取,如果 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 在 read == 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
}

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

4

3 回答 3

3

艾伦的回答为我解决了这个问题。如果其他人面临同样的问题,这里有一些使用 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 秒的超时。

于 2012-07-26T07:01:59.000 回答
2

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

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


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

于 2012-07-26T05:15:53.133 回答
1

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

我从另一边遇到了这个问题,如果当前没有数据可用,NetworkStream.Read它不会立即返回 0。您必须使用NetworkStream.DataAvailable以确定是否NetworkStream.Read可以立即读取数据。

于 2013-11-04T14:42:10.990 回答