TcpListener:检测客户端断开连接,而不是客户端暂时不发送任何数据
我正在寻找在使用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在读取== 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 }
所以我想知道…有没有更好的方法来检测客户端是否已断开连接?
您可以使用NetworkStream.Socket
属性获取底层套接字,并使用它的Receive方法进行读取。
与NetworkStream.Read
不同, Socket.Receive
的链接重载将一直阻塞,直到读取了指定的字节数,并且只有在远程主机关闭TCP连接时才会返回零。
更新: @ jrh的注释是正确的, NetworkStream.Socket
是受保护的属性,在此上下文中无法访问。 为了获取客户端Socket
,您可以使用TcpListener.AcceptSocket方法,该方法返回与新建立的连接对应的Socket
对象。
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)
- 连接突然关闭(抛出exception)
最后两种情况中的任何一种都表明客户端已不再连接。
还需要围绕Socket.Accept()方法的try catch,因为如果客户端连接在连接阶段突然关闭,它可能会失败。
请注意,确实为读取操作指定了20秒的超时。
NetworkStream.Read
的文档没有反映这一点,但根据我的经验,如果端口仍处于打开状态并且没有数据可用,则“NetworkStream.Read”将阻塞,但如果端口已关闭,则返回0。
我从另一端遇到了这个问题 ,在那个NetworkStream.Read
中,如果当前没有可用的数据,则不会立即返回0。 您必须使用NetworkStream.DataAvailable
来确定NetworkStream.Read
可以立即读取数据。