为啥 TcpListener 会泄漏 ESTABLISHED 连接?
Posted
技术标签:
【中文标题】为啥 TcpListener 会泄漏 ESTABLISHED 连接?【英文标题】:Why would TcpListener be leaking ESTABLISHED connections?为什么 TcpListener 会泄漏 ESTABLISHED 连接? 【发布时间】:2012-03-01 11:58:46 【问题描述】:我有一个应用程序正在侦听来自大约 30 辆汽车的调制解调器的消息。我使用 TcpListener 来实现如下所示的服务器代码(省略了错误处理):
...
listener.Start()
...
void
BeginAcceptTcpClient()
if(listener.Server.IsBound)
listener.BeginAcceptTcpClient(TcpClientAccepted, null);
void
TcpClientAccepted(IAsyncResult ar)
var buffer = new byte[bufferSize];
BeginAcceptTcpClient();
using(var client = EndAcceptTcpClient(ar))
using(var stream = client.GetStream())
var count = 0;
while((count = stream.Read(buffer, total, bufferSize - total)) > 0)
total += count;
DoSomething(buffer)
我正确收到消息,我的问题在于断开连接。每 12 小时调制解调器重置并获得一个新的 IP 地址,但服务器继续保持旧连接处于活动状态(它们在 tcpview 中标记为 ESTABLISHED)。有没有办法为旧连接设置超时?我认为通过关闭 TcpClient 关闭了 TCP 连接(这就是我本地测试中发生的情况),我做错了什么?
【问题讨论】:
【参考方案1】:我实际上对代码示例有点困惑 - 问题表明这些问题的开放时间相当长,但代码对于非常短的突发事件更为典型;对于长时间运行的连接,我希望在这里看到其中一个异步 API,而不是同步 API。
无痕迹地死掉的套接字非常常见,尤其是在与许多都需要发现关闭的中间设备一起分发时。尤其是无线网络有时会尝试人为地保持套接字处于活动状态,因为短暂失去无线连接是很常见的,因为设备不希望每次都杀死每个连接。
因此,在连接上实现某种心跳是很常见的,这样您就可以跟踪谁仍然真正活着。
举个例子 - 我这里有一个 websocket 服务器,理论上 处理正常关闭(通过指示关闭的特定序列)和非正常套接字关闭(意外终止连接) - 但是在过去一个小时左右我看到的 19k 连接中,有 70 个连接都没有命中。因此,相反,我会针对(缓慢的)心跳跟踪活动,如果它们在太长时间后没有响应,则将其杀死。
重新超时;你可以试试ReceiveTimeout
,但这只会在你通常不期望流量有很大差距的情况下对你有所帮助。
【讨论】:
我使用 Async api 进行连接接受,但使用同步 api 读取消息,我每小时只有一两次消息。我对调制解调器与服务器的连接方式没有太多控制(硬件提供商控制着这些,并且无法很好地解释它们的行为)。接受的 TcpClient 的 Dispose() 不会关闭与客户端的连接吗?我的问题不是客户死在我身上,我的问题是当他们更改 IP 时他们不会死! @Pablo 你能澄清一下:一旦客户端连接 - 它会立即关闭发送一些数据吗?还是开放一段时间? 客户端连接,发送一些数据,然后...关闭?这是问题的一部分,我不知道。我已经重置了连接(被优雅地接受,并且使用了接收到的部分数据),但连接似乎始终保持在 ESTABLISHED 状态,即使在我手动重置调制解调器之后(这给了它一个新的 IP 地址)。 @Pablo 当然;但如前所述 - 在意外关闭时,有时无法检测到断开的连接;因此,除非您有接收/读取超时,否则Read
将永远阻塞,并且永远不会关闭。这正是我试图描述的。如果您期望数据是突发的,则接收/读取超时是解决此问题的最简单方法;如果连接是长期存在的,则必须使用心跳。所以:打开读取超时,比如说 5 秒。
啊哈!所以你是说,我在接收因重置而卡住时被阻止,但我没有收到有关该问题的通知!我会测试它一个报告回来,谢谢!!!以上是关于为啥 TcpListener 会泄漏 ESTABLISHED 连接?的主要内容,如果未能解决你的问题,请参考以下文章