Socket.SendAsync没有检测到死TCP连接
我遇到了Socket.SendAsync方法未检测到死TCP连接的问题。在我的客户端/服务器应用程序中,服务器定期向连接的客户端发送心跳。Socket.SendAsync没有检测到死TCP连接
我遇到的问题是,即使客户端可能已经死亡,SendAsync方法的回调指示“SocketError.Success”,并且Socket.Connected属性为true,即使客户端不再是“活”。所以,对于服务器来说,看起来心跳数据已正确发送并且客户端仍然活着。
我每次都看到这个问题,客户端PC要么进入睡眠/休眠状态,要么当客户端在VMWare实例中运行并且该实例被挂起时。我没有看到这个问题,当客户端关闭应用程序,从任务管理器杀死它,等
internal void InternalSendAsync(ByteDataChunk chunk)
{
asyncSendArgs.SetBuffer(chunk.Buffer, 0, chunk.Offset);
asyncSendArgs.UserToken = chunk;
Socket.SendAsync(asyncSendArgs);
}
private void SendCompleted(object sender, SocketAsyncEventArgs args)
{
if (args.SocketError != SocketError.Success || !Socket.Connected)
{
InternalDisconnect(args.SocketError);
return;
}
// all is good & do some other stuff
}
任何人有任何想法是怎么回事,为什么SendCompleted方法不返回SocketError即使客户端已经很长时间了(我已经让服务器运行了几个小时,并且死亡套接字从未被检测到)?
感谢,
汤姆
从MSDN:
注意的 成功完成SendAsync方法并不表示 数据已成功交付 。
海事组织,关于网络最棘手的部分之一是你不能确定客户端得到的数据。如果你正在实施心跳系统,你应该让客户端回应心跳,证明它仍然活着。
当您暂停一个进程或休眠计算机时,我认为如果关闭正在运行的计算机上的套接字,就不会关闭它。
心跳是否实际发送?我的怀疑将是Naggle algorithm。拉出wireshark并检查线路上的流量。您可以用SocketOptionName.NoDelay
禁用Nagle。 From MSDN:
BeginSend
方法的成功完成意味着底层系统有足够的空间来缓存网络发送的数据。 如果您的应用程序立即将每个字节发送到远程主机很重要,则可以使用SetSocketOption
启用SocketOptionName.NoDelay
。有关网络效率缓冲的更多信息,请参阅MSDN中的Nagle算法。
Nagle不应该有甚至在Nagle打开的情况下,数据将在固定的时间间隔后发送(通常为200-500ms) – TJF 2010-08-19 14:59:27
不知道.net异步层是否会自行缓冲。 – 2010-08-21 15:08:02
忽略Socket.Connected
属性;它几乎没用。在您的示例代码中,如果或者Socket.Connected
为真或者没有错误代码,则认为一切正常。我要做的第一件事是删除Socket.Connected
部分。
我建议始终保持突出的异步读取以及定期发送的心跳。如果套接字不再连接,则读取或写入都会导致错误。
发送必须超时若干次,并具有指数回退。因此,检测对方何时消失需要一段时间(在程序退出的情况下,操作系统会立即回应连接不再可用)。尽管如此,它不应该在几小时之内。最多几分钟(假设网络连接速度较慢)。我的套接字在一秒钟内定期检测掉连接。
这正是我正在做的事情,而问题主要集中在如果客户端机器进入睡眠状态时写入不会导致错误的原因。我可以通过SendAsync方法向该套接字写入数个小时,并且如果客户端被挂起但它确实会抛出错误(例如,客户端死亡 – TJF 2010-08-19 14:58:15
你的代码假设如果'Socket.Connected'为真,那么连接仍然有效。那是错的。删除支票的“Socket.Connected”部分(仅留下支票的错误部分),然后查看是否有效。 – 2010-08-19 15:07:53
你是对的,我在这里发布代码时犯了一个错误,因为我的生产代码正在做一些其他的事情,当我缩短它并张贴在这里时我打错了这个。生产代码是|| !Socket.Connected – TJF 2010-08-19 15:17:51
您是否使用过Wireshark或类似软件来查看网络上发生了什么?有人会认为,如果客户端上的TCP子系统没有确认数据包,那么应该有套接字错误。也许客户端保持打开端口并确认数据包。如果是这样,那么你可能想尝试在客户端解决这个问题,或者做尼古拉所说的。
在捕获中,我看到一个PSH,ACK,然后是3个重传,之后没有任何内容。 Imho,我应该在套接字上收到超时异常,因为没有收到ACK,但我不知道? – TJF 2010-08-20 17:19:23
对 - 这是通常的做法。如果客户端在秒内未对心跳做出响应,则认为它已经死机并断开连接。 –
caf
2010-08-19 01:25:43
我同意这将是一种方法来做到这一点,但我从来没有见过这个问题与同步发送,因为我总是收到发送错误后,即使主机进入睡眠/休眠 – TJF 2010-08-19 15:00:48