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
相关产品推荐
相关产品推荐

