在.NET中,保持Socket连接的最佳方法是什么?

13

我正在寻找在.NET中进行保持连接检查的方法,情况适用于UDP和TCP。

目前在TCP中,我的做法是一方连接后,在没有要发送的数据时,每隔X秒发送一个心跳包。

我希望另一方检查数据,如果在X秒内没有接收到数据,则触发事件或其他操作。

我尝试的一种方法是执行阻塞式接收,并将套接字的ReceiveTimeout设置为X秒。但问题是,每当超时发生时,套接字的Receive就会抛出一个SocketExeception,并且在这一侧的套接字将关闭,这是正确的行为吗?为什么在超时后套接字会关闭/死亡而不是继续运行?

进行数据检查并休眠的方式不可接受(因为在睡眠期间可能会延迟接收数据)。

那么最佳方法是什么,为什么我在另一侧所描述的方法失败了?

4个回答

20

如果你字面意思上是指“KeepAlive”,请尝试以下操作。

    public static void SetTcpKeepAlive(Socket socket, uint keepaliveTime, uint keepaliveInterval)
    {
        /* the native structure
        struct tcp_keepalive {
        ULONG onoff;
        ULONG keepalivetime;
        ULONG keepaliveinterval;
        };
        */

        // marshal the equivalent of the native structure into a byte array
        uint dummy = 0;
        byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
        BitConverter.GetBytes((uint)(keepaliveTime)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)keepaliveTime).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
        BitConverter.GetBytes((uint)keepaliveInterval).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);

        // write SIO_VALS to Socket IOControl
        socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }

请注意,时间单位为毫秒。


8
我觉得很有趣,你竟然以那种方式回复试图帮助你的人。 - Greg Dean
1
值得澄清的是,这个答案是C#中唯一允许你执行TCP级别保活而不是在代码中手动执行它(它是设置和忘记),并允许你选择超时时间的答案(使用SetSocketOption打开SocketOptionName.KeepAlive会使用2小时的超时时间,对于大多数应用程序来说太长了)。更多信息请参见:http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html - Derek
我假设这只需要在客户端完成。服务器端需要做些什么吗? - Sal

3

如果您有一个不定时写入数据的TCP服务器,并且希望在后台运行保持活动状态:

tcpClient.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 1);
tcpClient.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 2);
tcpClient.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 2);
tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

如果服务器不(通常是自动的)响应TCP保持活动状态,则会导致异步读取引发超时异常。


1
根据MSDN的说法,在Receive调用中超过ReceiveTimeout时抛出的SocketException不会关闭套接字。你的代码里可能还有其他问题。
检查捕获的SocketException详细信息 - 也许这并不是超时。也许连接的另一端关闭了套接字。
考虑启用网络跟踪以诊断问题的确切来源:在MSDN上查找“网络跟踪”(无法提供链接,因为现在MSDN已经关闭)。

实际上,另一端仍然将套接字视为已打开(而此端未打开)。这部分代码并不复杂,我会尝试在一个更小的测试案例中重现它。 - Chris Jester-Young

0

由于您无法使用阻塞(同步)接收,您将不得不采用异步处理。幸运的是,使用.NET很容易实现。请查找BeginReceive()和EndReceive()的描述。或者查看这个文章链接

至于超时行为,我没有找到明确的描述。既然没有其他文档记录,您必须假设这是预期的行为。


1
是的,我忘了提到异步操作,但我想我的问题是:有人有RecieveTimeout的经验,并且知道为什么它会以上述方式失败吗?因为如果可以,那么我的代码将比使用RecieveTimeout更容易。 - Tim Sullivan

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