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

客户端断开后WCF连接处于CLOSE_WAIT状态,如何在C#中捕获?

解决WCF服务客户端超时断开后服务器任务无法终止的问题

问题根源

WCF默认通道机制不会主动检测TCP层面的CLOSE_WAIT状态,客户端超时断开后,服务器端通道仍维持Opened状态,导致已启动的长时间任务无法中断,无效连接持续占用资源。常规的通道事件监听或状态检查无法直接捕获该场景——只有当服务器尝试向客户端发送数据时,才会发现连接已断开。

解决方案

1. 异步任务+取消令牌,结合通道事件监听

在长时间处理任务中嵌入取消逻辑,通过通道的Closed/Faulted事件触发任务终止,同时定期检查取消令牌状态。

public class SaverService : ISaverPort
{
    public async Task<object> SaveData(object request)
    {
        using var cts = new CancellationTokenSource();
        var channel = OperationContext.Current.Channel;
        
        // 绑定通道关闭/故障事件,触发取消令牌
        void ChannelStateChanged(object sender, EventArgs e)
        {
            cts.Cancel();
            channel.Closed -= ChannelStateChanged;
            channel.Faulted -= ChannelStateChanged;
        }
        channel.Closed += ChannelStateChanged;
        channel.Faulted += ChannelStateChanged;

        try
        {
            // 模拟长时间处理任务,每秒检查一次取消状态
            await Task.Run(async () =>
            {
                for (int i = 0; i < 600; i++)
                {
                    cts.Token.ThrowIfCancellationRequested();
                    // 执行分段处理逻辑
                    await Task.Delay(1000, cts.Token);
                }
            }, cts.Token);

            return new { Status = "Success", Message = "处理完成" };
        }
        catch (OperationCanceledException)
        {
            // 清理资源,终止未完成任务
            return new { Status = "Cancelled", Message = "客户端已断开连接,任务终止" };
        }
        finally
        {
            // 解绑事件,避免内存泄漏
            channel.Closed -= ChannelStateChanged;
            channel.Faulted -= ChannelStateChanged;
        }
    }

    public object GetData(object request)
    {
        // 实现GetData业务逻辑
        return null;
    }
}

2. 自定义通道监听器,直接检测TCP连接状态

通过反射获取底层Socket,定期检查TCP连接状态,当检测到CLOSE_WAIT时主动终止通道和任务。

public class CustomReplyChannel : IReplyChannel
{
    private readonly IReplyChannel _innerChannel;
    private Socket _socket;

    public CustomReplyChannel(IReplyChannel innerChannel)
    {
        _innerChannel = innerChannel;
        // 获取底层Socket(仅适用于基于TCP的绑定,如wsHttpBinding/netTcpBinding)
        var socketProp = innerChannel.GetProperty<ISocketInfo>();
        _socket = socketProp?.Socket;
        if (_socket != null)
        {
            _ = MonitorSocketAsync();
        }
    }

    private async Task MonitorSocketAsync()
    {
        while (_socket.Connected)
        {
            await Task.Delay(2000);
            // 通过反射获取TCP状态
            var tcpState = (TcpState)_socket.GetType()
                .GetProperty("TcpState", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(_socket);
            
            if (tcpState == TcpState.CloseWait)
            {
                _innerChannel.Abort(); // 主动终止通道,触发服务端事件
                break;
            }
        }
    }

    // 实现IReplyChannel接口的所有方法,全部委托给_innerChannel
    public Uri LocalAddress => _innerChannel.LocalAddress;
    public T GetProperty<T>() where T : class => _innerChannel.GetProperty<T>();
    public void Abort() => _innerChannel.Abort();
    // ... 其余接口方法省略,直接转发到_innerChannel
}

// 配套的自定义通道监听器和绑定元素代码需集成到WCF服务配置中

3. 启用可靠会话,让WCF自动检测客户端断开

修改绑定配置,启用可靠会话并设置非活动超时,WCF会定期检查客户端连接状态,超时后自动关闭通道。

<wsHttpBinding>
  <binding name="SaverBinding" 
           closeTimeout="00:10:00" 
           openTimeout="00:10:00" 
           sendTimeout="00:10:00" 
           receiveTimeout="00:10:00" 
           maxReceivedMessageSize="2147483647" 
           maxBufferPoolSize="2147483647">
    <readerQuotas maxDepth="2147483647" 
                  maxStringContentLength="2147483647" 
                  maxArrayLength="2147483647" 
                  maxBytesPerRead="2147483647" 
                  maxNameTableCharCount="2147483647"/>
    <reliableSession enabled="true" inactivityTimeout="00:10:00" />
    <security mode="Message">
      <message clientCredentialType="UserName"/>
    </security>
  </binding>
</wsHttpBinding>

注意事项

  • 方案1是最通用的实现,不依赖底层细节,但要求任务能拆分为可中断的分段逻辑。
  • 方案2依赖.NET底层Socket的私有属性,不同版本可能存在兼容性问题,需谨慎使用。
  • 方案3需要客户端同步配置可靠会话,否则无法生效;同时inactivityTimeout需与客户端超时时间保持一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 13:45:48