TcpListener:检测客户端断开连接,而不是客户端暂时不发送任何数据

时间:2012-07-26 04:58:28

标签: c# tcplistener

我正在寻找在使用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时删除客户端,它只接受 第一个数据流只是在之后立即丢弃客户端。

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

电话之间没有人为的延迟, 但就计算机时间而言,两次通话之间的时间当然是“巨大的”, 所以总会有一段时间读= = 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
}

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

3 个答案:

答案 0 :(得分:3)

Eren的回答为我解决了这个问题。万一其他人面临同样的问题 这是使用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秒的超时。

答案 1 :(得分:2)

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

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


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

答案 2 :(得分:1)

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

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