问题 什么是在.NET中保持活动套接字检查的最佳方法?


我正在寻找一种在.NET中进行保持活动检查的方法。该方案适用于UDP和TCP。

目前在TCP中,我所做的是一方连接,当没有数据要发送时,它每隔X秒发送一次保持活动。

我希望对方检查数据,如果在X秒内收到非数据,则提出事件左右。

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

检查是否有数据和睡眠是不可接受的(因为我可能在睡觉时接收数据时滞后)。

那么最好的方法是什么呢?为什么我在另一方面描述的方法失败了?


3031
2017-10-03 22:37


起源



答案:


如果你的字面意思是“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);
    }

15
2017-10-05 01:52



我想知道你是否甚至读过这个问题:)当然我不是
我很乐意回复那些试图以这种方式帮助你的人。 - Greg Dean
值得澄清的是,这个答案是C#中唯一允许你执行TCP级别Keepalive而不是在代码中手动执行的答案(它是设置并且忘记)并且允许您选择超时(使用SetSocketOption使用启用SocketOptionName.KeepAlive)超时2小时,对于大多数应用来说太长了。更多信息: blog.stephencleary.com/2009/05/... - Derek
我假设这只是在客户端需要。服务器端需要什么? - Sal


答案:


如果你的字面意思是“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);
    }

15
2017-10-05 01:52



我想知道你是否甚至读过这个问题:)当然我不是
我很乐意回复那些试图以这种方式帮助你的人。 - Greg Dean
值得澄清的是,这个答案是C#中唯一允许你执行TCP级别Keepalive而不是在代码中手动执行的答案(它是设置并且忘记)并且允许您选择超时(使用SetSocketOption使用启用SocketOptionName.KeepAlive)超时2小时,对于大多数应用来说太长了。更多信息: blog.stephencleary.com/2009/05/... - Derek
我假设这只是在客户端需要。服务器端需要什么? - Sal


根据MSDN,在Receive调用中超过ReceiveTimeout时抛出SocketException 将不会 关闭插座。您的代码中还有其他内容。

检查捕获的SocketException详细信息 - 也许它毕竟不是超时。也许连接的另一端关闭了套接字。

考虑启用网络跟踪来诊断问题的确切来源:在MSDN上查找“网络跟踪”(无法为您提供链接,因为现在MSDN已关闭)。


1
2017-10-04 12:59



实际上对方仍然认为套接字是打开的(虽然这边没有)。这部分代码并不复杂,我会尝试在较小的测试用例中重现它。


由于您无法使用阻塞(同步)接收,因此必须满足异步处理。幸运的是,使用.NET很容易。查找BeginReceive()和EndReceive()的描述。或者看看这个 文章 要么 这个

至于超时行为,我没有找到对此的确凿描述。由于没有记录,否则你必须假设它是预期的行为。


0
2017-10-03 22:56



是的,忘了提到异步操作,但我想我的问题是有没有人有RecieveTimeout的经验以及为什么它以上述方式失败(因为那时我的代码会比使用RecieveTimeout更容易。