C# 使用TcpListener取消NetworkStream.ReadAsync
考虑以下简化示例(准备在LinqPad中滚动,需要提升帐户): 如果我将telnet客户端连接到C# 使用TcpListener取消NetworkStream.ReadAsync,c#,stream,async-await,tcplistener,cancellation,C#,Stream,Async Await,Tcplistener,Cancellation,考虑以下简化示例(准备在LinqPad中滚动,需要提升帐户): 如果我将telnet客户端连接到localhost:6666,然后无所事事地坐了5秒钟,为什么我会看到“令牌已取消”,但从未看到“boom”(或“finished”) 此网络流是否不考虑取消 我可以通过组合使用Task.Delay()和Task.wheny来解决这个问题,但我更愿意让它按预期工作 相反,以下取消示例: async void Go(CancellationToken ct) { using(var cts=ne
localhost:6666
,然后无所事事地坐了5秒钟,为什么我会看到“令牌已取消”,但从未看到“boom”(或“finished”)
此网络流是否不考虑取消
我可以通过组合使用Task.Delay()
和Task.wheny
来解决这个问题,但我更愿意让它按预期工作
相反,以下取消示例:
async void Go(CancellationToken ct)
{
using(var cts=new CancellationTokenSource(TimeSpan.FromSeconds(5)))
{
try
{
await Task.Delay(TimeSpan.FromSeconds(10),cts.Token)
.ConfigureAwait(false);
}
catch(TaskCanceledException)
{
Console.WriteLine("boom");
}
}
}
按预期打印“boom”。发生了什么事?否,
NetworkStream
不支持取消
不幸的是,底层Win32 API并不总是支持每次操作取消。传统上,您可以取消某个特定句柄的所有I/O,但这是相当新的。大多数.NET BCL都是针对XP API(或更早版本)编写的,其中不包括CancelIoEx
Stream
通过“伪造”对取消(以及异步I/O)的支持(即使实现不支持)来加剧这个问题。对取消的“假”支持只是立即检查令牌,然后启动无法取消的常规异步读取。这就是您在NetworkStream
中看到的情况
对于套接字(以及大多数Win32类型),如果要中止通信,传统方法是关闭句柄。这会导致所有当前操作(读取和写入)失败。从技术上讲,这违反了BCL线程安全性,但它确实有效
cts.Token.Register(() => client.Close());
...
catch (ObjectDisposedException)
另一方面,如果您希望检测半开放场景(您的一方正在读取,但另一方已失去连接),那么最好的解决方案是定期发送数据。我会更详细地描述这一点
cts.Token.Register(() => client.Close());
...
catch (ObjectDisposedException)