BeginReceive / BeginRead timeouts

11,991

Solution 1

It's the only way to do it, because when you're using an asynchronous operation, the thread that initiated the operation is off doing something else. The timeout is available with the synchronous version because the execution thread is blocked until the Read operation completes.

If you would have to use a background thread to cancel the operation, though, there wouldn't be much point to continuing to use the asynchronous Begin/End methods. If you're going to spin off a background thread, just perform a synchronous Read operation from the background thread, and then you can use the ReceiveTimeout.

Solution 2

Wait on ManualResetEvent with some timeout value to signal when your task is finished. If it times out before it is signaled, then you know that asynchronous operation never completed.

private ManualResetEvent receiveDone = new ManualResetEvent(false);

receiveDone.Reset();
socket.BeginReceive(...);
if(!receiveDone.WaitOne(new TimeSpan(0, 0, 0, 30))) //wait for 30 sec.
    throw new SocketException((int)SocketError.TimedOut);

Inside BeginReceive callback, use

private void ReceiveCallBack(IAsyncResult ar)
{
    /** Use ar to check if receive is correct and complete */
    receiveDone.Set();
}
Share:
11,991
Barg
Author by

Barg

Updated on June 06, 2022

Comments

  • Barg
    Barg almost 2 years

    I'm using a NetworkStream & TcpClient to asynchronously receive data using BeginRead. I need to apply a time-out to this operation, such that after a specified amount of time the read will be aborted.

    As far as I'm able to tell, this isn't supported on NetworkStream or TcpClient - there is a ReceiveTimeout property, but this appears to only apply to the synchronous equivalent - 'Read'.

    Even the underlying Socket class doesn't appear to support timeouts in its BeginReceive method.

    I've searched on this issue and the only suggested resolution I've seen is to set up another background thread to cancel the operation if it doesn't complete within the timeout period. This seems like a horrible hack. Surely there is a better way?

  • eselk
    eselk almost 12 years
    Joel points us in the right direction about not using a background thread to cancel, but incase it isn't obvious, what a lot of people do is have 1 thread (maybe from the worker poll) periodically clean up dead connections. This is required to free up socket handles/etc, and especially important if you ever get anything like a denial of service attack -- intentional or not.
  • Anthony
    Anthony over 10 years
    Downvoting because this is exactly what I (naively) did, and it doesn't work. Every call to BeginReceive that isn't matched by a call to EndReceive will leak kernel resources. Graph the process activity in something like Perfmon and watch the nonpaged pool slowly leak. If this lasts long enough (in my case, hours) you will eventually get an exception with error code WSAENOBUFS; "An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full."
  • vivek.m
    vivek.m over 10 years
    I have not verified it but can we not fix your issue by calling socket.EndReceive(..) before throwing exception? The IAsyncResult that you need for calling 'EndReceive` is returned by BeginReceive. Upvoting your comment. Thanks for the update.
  • RoeeK
    RoeeK over 9 years
    so... it's not that asynchronous operation after all. i mean, what's the point of using BeginReceive it you block the thread until timeout? just use synchronous operations with socket timeouts instead..
  • hangar
    hangar over 9 years
    The other option is System.Threading.Timer, which doesn't take up a thread.
  • Kieran Devlin
    Kieran Devlin over 3 years
    @hangar There's 4 implementations of Timer, System.Threading.Timer does take up a thread, its just run on the thread pool so you don't have to manually manage the resources necessary to have the timer function. docs.microsoft.com/en-us/dotnet/api/…