Roger Lipscombe
Roger Lipscombe

Reputation: 91845

Where is UdpClient.CancelReceive?

I'm implementing a simple local-network discovery protocol, so I call UdpClient.Send and then UdpClient.BeginReceive. In case more than one response is pending, I call UdpClient.BeginReceive at the end of the callback. Something like this:

UdpClient client = new UdpClient(AddressFamily.InterNetwork);
client.EnableBroadcast = true;
client.Send(request, request.Length, broadcastEndPoint);
client.BeginReceive(Callback, client);

...and then in Callback:

void Callback(IAsyncResult ar)
{
    UdpClient client = (UdpClient)ar.AsyncState;
    IPEndPoint remoteEndPoint = null;
    byte[] response = client.EndReceive(ar, ref remoteEndPoint);

    // Do something with response

    client.BeginReceive(Callback, client);
}

My problem is that my main loop calls client.Close while there's still a receive pending. The receive completes, and then my next call to BeginReceive throws an exception: System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

Why doesn't UdpClient have a CancelReceive method? What can I do instead?

Upvotes: 4

Views: 2885

Answers (2)

Nicolas Riousset
Nicolas Riousset

Reputation: 3609

Using a "isClosing" flag to notify the callback function that the UdpClient is not available anymore is not a proper solution. The callback being executed in a different thread, there's always a chance that the connection is closed after the "isClosing" flag check and before the "BeginReceive" (or "EndReceive") call.

Even though that's not a clean design, Microsoft seems to recommend to just catch the corresponding exceptions to detect that the socket isn't available anymore. That's not documented for "BeginReceive", but that's documented for the similar function "BeginConnect" :

To cancel a pending call to the BeginConnect() method, close the Socket. When the Close() method is called while an asynchronous operation is in progress, the callback provided to the BeginConnect() method is called. A subsequent call to the EndConnect(IAsyncResult) method will throw an ObjectDisposedException to indicate that the operation has been cancelled.

So, sample code would look like :

void Callback(IAsyncResult ar)
{
    try
    {
        UdpClient client = (UdpClient)ar.AsyncState;
        IPEndPoint remoteEndPoint = null;
        byte[] response = client.EndReceive(ar, ref remoteEndPoint);

        // Do something with response

        client.BeginReceive(Callback, client);
    }
    catch (SocketException e)
    {
        // Oups, connection was closed
    }
    catch (ObjectDisposedException e)
    {
        // Oups, client was disposed
    }
}

Upvotes: 3

Dexter
Dexter

Reputation:

Instead or to overcome this exception create bool and set it before issuing the close command, use that bool to check in callback

like this

bool isClosing=false;
void Callback(IAsyncResult ar)
{
    if(isClosing) return;
}

set the bool isClosing before issuing the close command

Upvotes: 6

Related Questions