服务器未识别TCP客户端被强制关闭时的断开连接状态。

10

介绍

我的客户端应用程序的一个实例将与远程服务器建立两个传出TCP连接 - 一个主连接和一个文件传输连接

当客户端应用程序被强制关闭时 - 有时,服务器会没有确认这两个套接字连接已经断开。

服务器将检测到两个连接是否已断开或仅检测到已断开主连接,这是不希望发生的情况。

请注意,问题只出现在多台测试机中的一台上,并且在客户端被强制关闭时文件传输连接正在主动传输数据。

通过分析网络流量 - 我发现操作系统实际上确认了两个RST标志!因此,我倾向于认为问题出在服务器代码中。

代码

使用Socket.BeginReceive方法,我负责检测断开连接的回调函数如下:

private void ReadCallback(IAsyncResult asyncResult) 
{
    try 
    {
        int bytesTransferred = _clientSocket.EndReceive(asyncResult);
        _logger.Debug(GetHashCode() + " Received " + bytesTransferred + " bytes from " + RemoteEndPoint);

        if (bytesTransferred > 0) 
        {
            _clientSocket.BeginReceive(_readMessageBuffer, ReadCallback);
            _logger.Debug(GetHashCode() + " Issued asynchronous read for " + RemoteEndPoint);
        } 
        else 
        {
            _logger.Debug(GetHashCode() + " Client disconnected. Received zero bytes. ");
        }
    } 
    catch (SocketException socketException) 
    {
        _logger.DebugException(GetHashCode() + " Client disconnected. SocketException thrown", socketException);
    }
    catch (ObjectDisposedException objectDisposedException)
    {
        _logger.DebugException(GetHashCode() + " Client disconnected. ObjectDisposedException thrown", objectDisposedException);
    }
    catch (Exception exception) 
    {
        _logger.DebugException(GetHashCode() + " Client disconnected. Exception thrown", exception);
    }
}

为了分离问题,我已将代码削减到仅当每次从网络读取一部分数据时服务器发出异步读取。

请问有人能分享一些见解或猜测,为什么在某些机器上,服务器应用程序不总是能检测到两个套接字的断开连接?


这是当服务器应用程序未能确认两个基础套接字连接已断开时生成的未修改日志。

这是当服务器应用程序成功地确认两个基础套接字连接已经断开时生成的未修改的日志。


1
你的代码看起来很好,我唯一能指出的是当接收到0字节时,if (socketError == SocketError.Success)是不必要的。 - bansi
你说的文件传输连接保持在半开放状态是什么意思?如果你试图在第一个连接断开时关闭第二个连接,可能需要将两者关联起来并手动关闭另一个连接。 - bansi
请发布更多的代码,任何可能相关的内容。当服务器没有注意到断开连接时,您是否看到了RST,还是只是一般观察到了RST?您是否愿意尝试解决方法(考虑超时和保持活动数据包)? - usr
@usr,我真的很感激你,虽然很难相信,但为了隔离问题,服务器应用程序只是像上面描述的那样为每个子套接字发出异步读取。从网络中实际读取的数据被有效地忽略,并且没有其他操作正在进行。 - Caster Troy
@usr 服务器始终会确认 RST。我不想使用保持连接来抽象或隐藏问题。 - Caster Troy
你在哪里看到服务器检测到主连接已关闭?我想有时它会检测到文件连接已关闭。只有在有活动时才能检测到不正常关闭的连接。或者你需要启用TCP保持活动功能。 - Sergey Zyuzin
3个回答

4
我看到的第一件事是在Socket.EndReceive调用周围没有异常处理程序。该调用确实可能抛出异常。
您正在使用应返回SocketError的版本,但文档中没有说明它在所有情况下都这样做。
因为ReadCallback由系统调用,所以您不会有任何异常处理程序向上链,甚至当EndReceive抛出异常时也无法捕获和打印错误。因此,看起来似乎没有什么实际问题,但套接字留下了一半开放状态。
我建议在Socket.EndReceive周围添加异常处理程序,并在发生异常时打印消息或设置断点。我认为这将指出问题所在。您可能只需要在EndReceive收到异常时处置/关闭Socket。
希望有所帮助 - Harold

1
是的,那很有道理。如果客户端变得不可达,就会发生这种情况(例如,如果您拔掉客户端计算机上的网络电缆)。在这种情况下,您需要设置一个定时器来取消异步读取。 - huntharo
更新了问题,试图使问题更清晰,并提供了赏金。也许你有进一步的推测。 - Caster Troy
嗨Caster - 我能想到的唯一一件事是,当你第一次调用BeginReceive(在你发布的代码之外)时,你也没有捕获异常。你检查过这种情况吗?Harold - huntharo
不幸的是问题仍然存在。无论如何还是谢谢。 - Caster Troy

2
计算机网络中的一件事情是:消息在网络上传递。它们之间没有灵魂感应的联系。如果一个端点突然消失了,如果网络断开或计算机失去电源,就不会发出通知另一端说“我不能再跟你交流了”的念力信号。
因此,唯一能够可靠地确定你正在交流的计算机是否仍然存在的方法是尝试去与它通信。当网络未能将数据包传递到计算机时,你才会发现这一点。但当一个计算机从“因为没有要说的话而保持沉默”变成“因为不在了而保持沉默”时,你所看到的只是它保持沉默而已。你无法分辨其中的差异。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接