如何取消Socket.ReceiveFromAsync()?

How to cancel Socket.ReceiveFromAsync()?

我正在使用 dotnet core 2.2 并具有以下 UDP 侦听器:

var socket = new Socket(ep.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
while (true)
{
    var result = await socket.ReceiveFromAsync(...);
}

现在在某些情况下我想中断 ReceiveFromAsync() 调用。但似乎与 TCP 情况(即 ReceiveAsync())不同,没有 ReceiveFromAsync() 重载接受 CancellationToken

一种选择是将 Task.Delay(-1, ct);Task.WhenAny()/Task.WhenAll() 一起使用。但是我认为这个解决方案会导致内存泄漏,对吗?我的意思是 .ReceiveFromAsync() 呼叫仍然 "exists" 如果中断,只是在后台。也可能导致逻辑中断,因为这样的调用会读取一个 UDP 数据包并在之后丢弃它?还是我的推理不正确?

另一个想法是让后台工作人员从 UDP 套接字读取并对每个数据包进行排队。并且这里不会发生中断。然后我会从队列中读取并中断这个调用。那会起作用,但肯定需要一些努力。我看到的问题是:线程安全和性能。

有没有cleaner/simplier的方法来处理这种情况?

对于 "uncancelable" I/O 请求,Windows 中的标准模式是关闭底层句柄 - 在本例中为套接字。这通常会导致任何异步(或同步,就此而言)操作以错误代码完成。

在您的情况下 - "pausing" UDP 接收器 - 我认为这种方法特别有意义。 UDP套接字无论如何都不代表打开的连接,所以关闭套接字是最好的解决方案。

关于 Task.DelayTask.WhenAny,您的担忧是完全正确的。 Task.Delay + Task.WhenAny 方法只取消操作的 wait,而不是操作本身。具体来说,它不会取消 UDP 接收,并且未取消的 UDP 接收操作可能会得到一个数据包,然后将是 "lost",因为您的应用程序会忽略它。