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

C#中带CancellationToken的异步Socket接收触发连接断开问题求助

Socket异步接收取消后连接断开的问题解决

问题本质

C#的Socket.ReceiveAsync在传入CancellationToken并触发取消时,底层会调用CancelIoEx,这会导致Socket进入错误状态,后续所有对该Socket的操作都会抛出异常,表现为连接断开。而Python的Socket超时机制(如settimeout)只是中断当前的recv调用,不会破坏Socket的内部状态,因此连接可以保留。

解决方案:避免直接将CancellationToken传入ReceiveAsync

改为用Task.WhenAny将接收任务与取消/超时任务绑定,这样取消时不会直接中断Socket的IO操作,而是优雅退出接收循环,保持Socket的可用状态。

修改后的代码示例

try
{
    await Socket!.SendAsync(request, SocketFlags.None, ct);

    if (!expectResponse)
    {
        return null;
    }

    var receivedData = new List<byte>();
    while (true)
    {
        // 创建接收任务,不传入取消令牌
        var receiveBuffer = new byte[256];
        var receiveTask = Socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), SocketFlags.None);
        
        // 等待接收完成或取消令牌触发
        var completedTask = await Task.WhenAny(receiveTask, Task.Delay(Timeout.Infinite, ct));

        if (completedTask == receiveTask)
        {
            // 接收完成,处理数据
            int bytesRead = receiveTask.Result;
            if (bytesRead == 0)
            {
                // 服务器主动关闭连接
                return null;
            }
            receivedData.AddRange(receiveBuffer[..bytesRead]);

            if (ResponseEndsWithTerminationSequence(receivedData.ToArray(), terminationSequence))
            {
                return receivedData.ToArray();
            }
        }
        else
        {
            // 取消令牌触发,退出循环,Socket状态保持正常
            ct.ThrowIfCancellationRequested();
            return null;
        }
    }
}
catch (OperationCanceledException)
{
    return null;
}
catch (Exception ex)
{
    var cts = new CancellationTokenSource(10000);
    _ = Task.Run(() => ReconnectAsync(cts.Token));
    Debug.WriteLine($"Exception occurred during data transmission!\n{ex.Message}");
    return null;
}
finally
{
    stopwatch.Stop();
    Debug.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds}ms");
}

关键改动说明

  • 不直接将ct传入ReceiveAsync,而是通过Task.WhenAny监听取消信号,避免触发Socket的底层取消操作。
  • 接收任务完成后再处理数据,确保Socket的IO操作正常完成或自然退出,不会被强制中断。
  • 取消时仅退出循环,Socket仍处于可用状态,后续可以继续复用连接。

额外注意事项

  • 如果需要实现超时逻辑,可以将Task.Delay(Timeout.Infinite, ct)替换为Task.Delay(yourTimeoutMs, ct),这样既支持取消令牌,也支持超时。
  • 要确保Socket的LingerOption设置合理(比如禁用 linger),避免取消后Socket残留连接资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 08:30:19