我正在使用 UDPClient,如下所示
dim c = New UDPClient(port)
client.CLient.ReceiveTimeout = 1
await client.ReceiveAsync()
但是,await 不会终止或抛出异常 虽然我设置了超时。这是正常的吗 行为?
MSDN Library 文章中明确提到了 Socket.ReceiveTimeout:
获取或设置一个值,该值指定同步接收调用超时之前的时间量。
添加了强调。使用 ReceiveAsync() 时,您正在执行与同步接收相反的操作。解决方法是使用 System.Timers.Timer,在调用之前启动并在调用之后停止。关闭 Elapsed 事件处理程序中的套接字,以便 ReceiveAsync() 方法终止并出现 ObjectDispose 异常。
是的。
Socket
上的异步方法不实现超时。如果您需要异步操作超时,则必须自己创建它们(例如,使用 Task.Delay
和 Task.WhenAny
)。
我最近遇到了这个问题,这就是我解决它的方法:
async Task Listen(IPEndPoint ep, int timeout)
{
using (var udp = new UdpClient(ep))
{
var result = await Task.Run(() =>
{
var task = udp.ReceiveAsync();
task.Wait(timeout);
if (task.IsCompleted)
{ return task.Result; }
throw new TimeoutException();
});
Receive(result); // use the result
}
}
对于它的价值,这就是我的做法(也可以使用取消令牌的组合):
public static async Task<byte[]> SendReceiveUdpAsync(IPEndPoint endPoint, byte[] packet, int timeout, CancellationToken cancellationToken)
{
using var client = new UdpClient(endPoint.AddressFamily);
await client.SendAsync(packet, endPoint, cancellationToken).ConfigureAwait(false);
var task = client.ReceiveAsync(cancellationToken);
var index = Task.WaitAny(new [] { task.AsTask() }, timeout, cancellationToken);
if (index < 0)
return null;
return task.Result.Buffer;
}
技巧是在 Task.WaitAny 调用中等待 UDP 接收任务。
你也可以给
ReceiveAsync
函数一个CancellationToken
,让这个token在超时后过期。
private async Task ReceiveDataWithoutTokenAsync(int timeoutInMs)
{
using (CancellationTokenSource cancellationTokenSource = new())
{
cancellationTokenSource.CancelAfter(timeoutInMs);
await UdpClient.ReceiveAsync(cancellationTokenSource.Token);
}
}
或者,如果您已有令牌,则可以链接外部令牌和超时令牌。
private async Task ReceiveDataWithExternalTokenAsync(int timeoutInMs, CancellationToken externalToken)
{
using (CancellationTokenSource internalCancellationTokenSource = new())
{
using (CancellationTokenSource linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(internalCancellationTokenSource.Token, externalToken))
{
internalCancellationTokenSource.CancelAfter(timeoutInMs);
await UdpClient.ReceiveAsync(linkedTokenSource.Token);
}
}
}