Sam Hazleton
Sam Hazleton

Reputation: 462

How can I safely terminate UdpClient.receive() in case of timeout?

I want to implement a timeout in my UDP Multicast receiver using VB.Net. Basically, if I don't receive any packets in 10 seconds I want to stop listening. I can very easily use a Timer with an interval of 10000 to know when I need to time out, but the question is how do I stop the receive function? If I use the Receive() function (the one that blocks), I could simply stop it with a Thread.Abort() call. Everything I have read, however, has said that this is not a safe practice. If I use the asynchronous BeginReceive() function, I don't know how to terminate it before it finishes normally because EndReceive() will throw an exception if it isn't called with an IASyncResult that is returned from BeginReceive().

The answers to this question led me to investigate the CancelAsync() method. But, the answer to this question made me nervous.

If I use the blocking receive, I will not be able to continuously poll the CancellationPending property unless I call Receive() in its own thread from within the DoWork handler. But that would mean it would continue to run after the cancel takes effect right? If I use BeginReceive(), I am worried that CancelAsync() wil get "eaten" by the DoWork handler and I will end up with the same problem.

Plus, this snippet from the BackgroundWorker documentation is less than reassuring...

Be aware that your code in the DoWork event handler may finish its work as a cancellation request is being made, and your polling loop may miss CancellationPending being set to true. In this case, the Cancelled flag of System.ComponentModel.RunWorkerCompletedEventArgs in your RunWorkerCompleted event handler will not be set to true, even though a cancellation request was made.

One alternative I thought of was having the UdpClient that is sending the packets be in charge of timing out, and then have it send some kind of cancellation signal packet to indicate that the receiver(s) should stop listening. The problem with this is that given the nature of UDP, there is no guarantee that said packet will arrive, or be picked up in the correct order.

Is there a way to safely terminate a UDP receive procedure before it finishes?

Upvotes: 1

Views: 1663

Answers (2)

Drowning_Ophelia
Drowning_Ophelia

Reputation: 31

I've run in to a similar situation where I open several connections (Udp, Serial, etc.) with remote devices and need to switch among them in a "listener" thread that uses the blocking UdpClient.Receive() call. Calling Thread.Abort() caused crashes, switching the connection instance (the UdpClient) without first exiting the thread didn't work either, because the thread was hung on the UdpClient.Receive() and a simple flag in a while loop never exited.

What did finally work was to close the connection in the main application thread, this would cause UdpClient.Receive() to throw an exception that could be caught and dealt with. The application creates instances of UdpClient that represent the various connections and starts a "listener" thread that can be terminated by setting a global flag and closing the current UdpClient instance. I used VB.NET and it looked something like this:

Dim mListening as Boolean    'application flag for exiting Listener thread 
Dim mReceiver as UdpClient   'a connection instance 
...
Private Sub Listener()
    While mListening
        Try
            Dim reply = mReceiver.Receive()
        Catch ex As Exception
            'execution goes here when mReceiver.Close() called
        End Try
    End While
End Sub

The app sets mListening and starts the Listener thread. When the app needs to "unblock" mReceiver, it calls mReceiver.Close() and handles it accordingly. I've used this scheme without any problems. I'm using VS 2019 and .NET v4.7

Upvotes: 2

Jazz_Narang
Jazz_Narang

Reputation: 60

I have ran into the same issue with UdpClient and I am not sure what the safe solution is/if a "safe" solution exists. However, I came across a function that a user posted for a different question which tracks and terminates a code block that exceeds a certain time span and I just wrap my call to UdpClient.receive() in it. If you would like to give it a try, the function looks like this:

private static bool TrackFunction(TimeSpan timeSpan, Action codeBlock)
{
    try
    {
        Task task = Task.Factory.StartNew(() => codeBlock());
        task.Wait(timeSpan);
        return task.IsCompleted;
    }
    catch (AggregateException ae)
    {
        throw ae.InnerExceptions[0];
    }
}

And you would simply wrap it around your code like such:

       bool timeTracker = TrackFunction(TimeSpan.FromSeconds(10), () =>
    {
        Byte[] received = myUdpClient.Receive(ref myIPEndPoint);

    }

Again, there may be a better solution, but this is just what I have used.

Upvotes: 2

Related Questions