如何使UdpClient.ReceiveAsync()可取消? [英] How to to make UdpClient.ReceiveAsync() cancelable?
问题描述
我有一个方法的接口INetwork
:
Task<bool> SendAsync(string messageToSend, CancellationToken ct)
接口的一种实现具有如下代码:
One implementation of the interface has code like this:
public async Task<bool> SendAsync(string messageToSend, CancellationToken ct)
{
var udpClient = new UdpClient();
var data = Encoding.UTF8.GetBytes (messageToSend);
var sentBytes = await udpClient.SendAsync(data);
return sentBytes == data.Length;
}
不幸的是,UdpClient
类的SendAsync()
不接受CancellationToken
.
Unfortunately, SendAsync()
of the UdpClient
class does not accept a CancellationToken
.
所以我开始将其更改为:
So I started changing it to:
public Task<bool> SendAsync(string messageToSend, CancellationToken ct)
{
var udpClient = new UdpClient();
var data = Encoding.UTF8.GetBytes (messageToSend);
var sendTask = udpClient.SendAsync(data);
sendTask.Wait(ct);
if(sendTask.Status == RanToCompletion)
{
return sendTask.Result == data.Length;
}
}
显然,这是行不通的,因为没有返回任何Task
.但是,如果我返回任务,则签名不再匹配. SendAsync()
返回一个Task<int>
,但是我需要一个Task<bool>
.
Obviously this won't work because there is no Task
being returned. However if I return the Task, the signatures don't match anymore. SendAsync()
returns a Task<int>
, but I need a Task<bool>
.
现在我很困惑. :-) 该如何解决?
And now I'm confused. :-) How to resolve this?
推荐答案
我知道这有点晚了,但是最近我不得不使UdpClient ReceiveAsync/SendAsync可取消.
I know this is a little late, but I just recently had to make a UdpClient ReceiveAsync/SendAsync cancellable.
您的第一个代码块正在发送而没有取消(您的标题说顺便说一句...").
Your first code block is sending without a cancel (your title says receive by the way...).
您的第二个代码块绝对不是这样做的方法.您正在调用* Async,然后是Task.Wait,它将阻塞直到调用完成.这使调用有效地同步,并且调用* Async版本毫无意义.最好的解决方案是按如下方式使用Async:
Your second code block is defintely not the way to do it. You are calling *Async, and then Task.Wait, which blocks until the call is complete. This makes the call effectively synchronous and there's no point in calling the *Async version. The best solution is to use Async as follows:
...
var sendTask = udpClient.SendAsync(data);
var tcs = new TaskCompletionSource<bool>();
using( ct.Register( s => tcs.TrySetResult(true), null) )
{
if( sendTask != await Task.WhenAny( task, tcs.Task) )
// ct.Cancel() called
else
// sendTask completed first, so .Result will not block
}
...
在UdpClient上没有内置的取消方法(没有一个函数接受CancellationToken
),但是您可以利用Task.WhenAny
等待多个任务的功能.这将在完成的第一个任务中返回(这也是使用Task.Delay()
来实现超时的一种简便方法).然后,我们只需要创建一个在取消CancellationToken
时完成的任务,我们可以通过创建TaskCompletionSource
并使用CancellationToken
的回调对其进行设置来完成此任务.
There's no built-in way to cancel on UdpClient (none of the functions accept a CancellationToken
), but you can take advantage of the ability to await multiple tasks with Task.WhenAny
. This will return with the first task that completes (this is also an easy way to use Task.Delay()
to implement timeouts). We then just need to create a Task that will complete when the CancellationToken
is canceled, which we can do by creating a TaskCompletionSource
and setting it with the CancellationToken
's callback.
一旦取消,我们就可以关闭套接字以实际上取消"底层的读/写.
Once canceled, we can close the socket to actually "cancel" the underlying read/write.
最初的想法来自另一个SO处理文件句柄的答案,但它也适用于套接字.我通常将其包裹在扩展方法中,如下所示:
The original idea for this came from another SO answer dealing with file handles, but it works with sockets too. I generally wrap it up in an extension method like so:
public static class AsyncExtensions
{
public static async Task<T> WithCancellation<T>( this Task<T> task, CancellationToken cancellationToken )
{
var tcs = new TaskCompletionSource<bool>();
using( cancellationToken.Register( s => ( (TaskCompletionSource<bool>)s ).TrySetResult( true ), tcs ) )
{
if( task != await Task.WhenAny( task, tcs.Task ) )
{
throw new OperationCanceledException( cancellationToken );
}
}
return task.Result;
}
}
然后像这样使用它:
try
{
var data = await client.ReceiveAsync().WithCancellation(cts.Token);
await client.SendAsync(data.Buffer, data.Buffer.Length, toep).WithCancellation(cts.Token);
}
catch(OperationCanceledException)
{
client.Close();
}
这篇关于如何使UdpClient.ReceiveAsync()可取消?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!