How do you 'cancel' a UdpClient::BeginReceive?
Asked Answered
F

3

6

I have a thread which sits around waiting for UDP messages from multiple interfaces using UdpClient::BeginReceive and a callback which calls UdpClient::EndReceive to pick up the data and pass it on.

If after 5 seconds I don't get anything, I return from the function which calls UdpClient::BeginReceive so that the process can be cancelled and to issue another broadcast which would trigger external clients to send in UDP responses. If we're not cancelling, I call the UdpClient::BeginReceive function again to check for new data.

If the client hasn't received any data in time, is there a way to cancel that async request without calling EndReceive which blocks indefinitely? I get the impression that leaving hundreds of async triggers running would be a bad idea.

Fireside answered 19/8, 2013 at 9:0 Comment(0)
D
17

You need to jerk the floor mat, call the UdpClient.Close() method.

That will complete the BeginReceive() call and your callback method will run. When you call EndReceive(), the class lets you know the floor mat is gone and will throw an ObjectDisposedException. So be prepared to handle this exception, wrap the EndReceive() call with try/catch so you catch the ODE and just exit the callback method.

Do note that this tends to get .NET programmers a bit upset, using exceptions for flow control is in general strongly discouraged. You however have to call EndInvoke(), even if you already know that this going to throw an exception. Failure to do so will cause a resource leak that can last a while.

Duplessis answered 19/8, 2013 at 13:8 Comment(1)
That was pretty much the conclusion I'd reached since posting the question. My implementation isn't the cleanest right now. Really I think I ought to be leaving one listener per interface running and only closing using Close() as you've suggested when things are needing to be disposed of properly. Thanks for the confirmation.Fireside
E
1

For the canceling BeginRecieve you can use Close method, but in base realization it will invoke ObjectDisposedException. I fixed it with creating custom BeginRecieve which is modernization of base realization:

public Task<UdpReceiveResult> ReceiveAsync(UdpClient client, CancellationToken breakToken)
    => breakToken.IsCancellationRequested
        ? Task<UdpReceiveResult>.Run(() => new UdpReceiveResult())
        : Task<UdpReceiveResult>.Factory.FromAsync(
            (callback, state) => client.BeginReceive(callback, state),
            (ar) =>
                {
                    if (breakToken.IsCancellationRequested)
                        return new UdpReceiveResult();

                    IPEndPoint remoteEP = null;
                    var buffer = client.EndReceive(ar, ref remoteEP);
                    return new UdpReceiveResult(buffer, remoteEP);
                },
            null);

More read here: UdpClient.ReceiveAsync correct early termination.

Also will be helpfull this: Is it safe to call BeginXXX without calling EndXXX if the instance is already disposed.

Edmonds answered 9/12, 2016 at 14:9 Comment(0)
O
0
      // Set ReadMsg = true while using UdpClient
      // Set ReagMsg = false before Dispose or Close no Exception Error
      private static bool ReadMsg;
      private static void ReceiveCallback(IAsyncResult ar)
      {

        UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
        IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
        try
        {
            if ( ReadMsg )
            {
                Byte[] receiveBytes = u.EndReceive(ar, ref e);
                string sddpMessage = Encoding.ASCII.GetString(receiveBytes);

            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"{ex.Message}");
        }
    }
Oniskey answered 12/4, 2020 at 7:10 Comment(1)
Welcome to SO. Please add more information what this code does and why it solves the problem.Halftruth

© 2022 - 2024 — McMap. All rights reserved.