我正在寻找在使用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
}
所以我想知道......有没有更好的方法来检测客户端是否已断开连接?
答案 0 :(得分:3)
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);
将阻止
最后两种情况中的任何一种都表明客户端已不再连接。
还需要围绕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
是否可以立即读取数据。