8

I'm using UDPClient like below

dim c = New UDPClient(port)
client.CLient.ReceiveTimeout = 1
await client.ReceiveAsync()

However the await does not terminate or throw even though I have set a timeout. Is this normal behaviour?

bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217

4 Answers4

7

Yes. The asynchronous methods on Socket do not implement the timeouts. If you need timeouts on asynchronous operations, you have to create them yourself (e.g., using Task.Delay and Task.WhenAny).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
7

It is explicitly mentioned in the MSDN Library article for Socket.ReceiveTimeout:

Gets or sets a value that specifies the amount of time after which a synchronous Receive call will time out.

Emphasis added. You are doing the opposite of a synchronous receive when you use ReceiveAsync(). The workaround is to use a System.Timers.Timer that you start before the call and stop afterwards. Close the socket in the Elapsed event handler so the ReceiveAsync() method terminates with an ObjectDisposed exception.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Good idea. are there any thread safety issues closing the socket like this whilst another thread is waiting on it? – bradgonesurfing Sep 28 '12 at 15:35
  • I cannot possibly answer that without any decent info. Getting multiple threads to read from a socket asynchronously is however never a problem, it is impossible. – Hans Passant Sep 28 '12 at 15:41
4

I had this issue recently and this is how I solved it:

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
    }
}
gbieging
  • 320
  • 1
  • 13
  • 3
    Note, that if the timeout is hit the read is still running and will eventually drop the data it receives. You can only use this technique if you shut down the whole socket in case of a timeout. – usr Dec 06 '17 at 17:00
  • @usr Won't the socket be closed once the udp client falls out of scope since it is in a using clause? – GrixM Mar 30 '22 at 08:35
1

For what it's worth, this is how I do it (also with the possible combination of a cancellation token):

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;
}

The trick is to wait for the UDP receive task in a Task.WaitAny call.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298