You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework 4.7.2下Socket KeepAlive无效,Azure防火墙断连问题

TCP KeepAlive不生效问题排查

我有两个基于.NET Framework 4.7.2的应用程序:

  • 第一个作为TCP客户端,代码如下:
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中看不到任何保活请求,且连接会在5分钟后被Azure防火墙断开。请问该场景中KeepAlive为何不生效?


原因及解决方法

1. Windows默认TCP KeepAlive参数触发延迟过长

仅设置SocketOptionName.KeepAlive = true只是开启了KeepAlive开关,但Windows系统默认的TCP KeepAlive参数为:

  • 无数据交互后2小时发送第一个探测包
  • 探测间隔1秒
  • 重试5次

Azure防火墙闲置超时为5分钟,远短于默认的2小时触发时间,导致保活包尚未发送就被防火墙断开连接。

必须通过SIO_KEEPALIVE_VALS IO控制码手动配置具体参数,示例代码如下:

客户端配置:

client = new TcpClient();
client.NoDelay = true;

const int SIO_KEEPALIVE_VALS = -1744830460; // 对应Winsock IOCTL码
var keepAliveBytes = new byte[12];
BitConverter.GetBytes((uint)1).CopyTo(keepAliveBytes, 0);    // 启用KeepAlive
BitConverter.GetBytes((uint)30000).CopyTo(keepAliveBytes, 4); // 5分钟后发送首个探测包(毫秒)
BitConverter.GetBytes((uint)10000).CopyTo(keepAliveBytes, 8); // 探测间隔10秒(毫秒)

client.Client.IOControl(SIO_KEEPALIVE_VALS, keepAliveBytes, null);
client.Connect(settings.Hostname, settings.Port);

服务端配置:

var client = tcpListener.AcceptTcpClient();
// 先开启KeepAlive开关,再配置参数
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

const int SIO_KEEPALIVE_VALS = -1744830460;
var keepAliveBytes = new byte[12];
BitConverter.GetBytes((uint)1).CopyTo(keepAliveBytes, 0);
BitConverter.GetBytes((uint)30000).CopyTo(keepAliveBytes, 4);
BitConverter.GetBytes((uint)10000).CopyTo(keepAliveBytes, 8);

client.Client.IOControl(SIO_KEEPALIVE_VALS, keepAliveBytes, null);

2. 应用层异步读取不影响TCP层保活机制

ReadAsync处于等待状态时,TCP栈会独立于应用层发送保活探测包,因此核心问题仍在于KeepAlive参数未正确配置,而非异步操作导致。

3. 确认Azure网络策略未拦截保活包

TCP保活探测包为TCP ACK包,使用原连接端口,Azure防火墙默认不会拦截此类数据包。若仍有疑问,可通过Wireshark在客户端/服务端本地捕获,确认保活包是否已发送,再排查网络层面问题。


内容的提问来源于stack exchange,提问作者Sheinar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 23:45:20