Reputation: 91845
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
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
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