为 SocketOptionLevel.Socket 设置后没有 KeepAlive 请求

No KeepAlive requests after setting it for SocketOptionLevel.Socket

提问人:Sheinar 提问时间:8/11/2023 更新时间:8/11/2023 访问量:49

问:

我有和两个应用程序 第一个打开 TcpClient:

 client = new TcpClient();
 client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
 client.NoDelay = true;
 client.Connect(settings.Hostname, settings.Port);

第二个听:

        var listener = new TcpListener(settings.IPAddress, settings.Port);

        var listenerThread = new Thread(() => Listen(listener, cancellationTokenSource.Token))
        {
            IsBackground = true,
            Priority = ThreadPriority.AboveNormal,
        };

       private void Listen(TcpListener tcpListener, CancellationToken cancellationToken)
       {
        tcpListener.Start();

        // stop listener on cancellation
        cancellationToken.Register(tcpListener.Stop);

        try
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                var client = tcpListener.AcceptTcpClient();
                client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

                // Register client in background
                Task.Run(() => RegisterClientConnection(client), cancellationToken)
                    .LogErrors(SystemLogManager.Current, "Error while registering client connection");
            }
        }
        catch (SocketException)
        {
            // logging
        }
    }

以及读取流的一些逻辑:

using (var clientStream = tcpClient.GetStream())
...
bytesRead = await clientStream.ReadAsync(messageBuffer, messageBufferPointer, tcpClient.ReceiveBufferSize, cancellationToken);

奇怪的部分是,我在 wireshark 中没有看到任何保持活动请求,并且 azure 防火墙在 5 分钟后断开了连接。 为什么保持活动在我的情况下不起作用?

.NetFramework 4.7.2 上的两个应用

C# TCPClient 保持活动状态

评论


答:

0赞 Stephen Cleary 8/11/2023 #1

默认情况下,TCP keepalive 每 2 小时发送一次,因此它们不会在不到 5 分钟的时间内显示。

如果您使用的是 Win 10 (1709) 或更高版本,则可以通过设置 TCP_KEEPIDLETCP_KEEPINTVL 套接字选项来调整此计时;更通用的解决方案 (Windows 2000+) 是将套接字句柄发送到 IOCTL SIO_KEEPALIVE_VALS

但是,请注意,TCP keepalive 是 TCP 规范的可选部分。路由器和软件网络可能会忽略 TCP keepalive。Windows 支持 TCP keepalive,但我不确定您使用的 Azure 组件是否支持 TCP keepalive。

由于您可以控制软件的两端,因此我建议在应用程序协议级别创建 keepalive 消息(如我的博客中所述)。通过发送空消息,这相对容易地完成;例如,如果您使用的是长度前缀消息框架,则只需发送长度前缀 0。使用应用程序级 keepalive 会强制所有中间路由器提供它(因为它是应用程序数据),并且与 TCP 级别的 keepalive 一样容易(如果不是更容易)实现。

0赞 Sheinar 8/11/2023 #2

在我定义保持活动超时后,一切都按预期工作:

插座。IOControl(IOControlCode.KeepAliveValues, inValue, null);