How to cancel NetworkStream.ReadAsync without closing stream

11,355

Solution 1

You can implement an async wrapper around NetworkStream.Read (or ReadAsync), which also receives a cancellationtoken that you can monitor and honor yourself. Something like this:

Task MyCancelableNetworkStreamReadAsync(NetworkStream stream, CancellationToken ct)
{
...
if(this.stream.CanRead)
{
  do 
  {
    //check ct.IsCancellationRequested and act as needed
    bytesRead = await this.stream.ReadAsync(this.buffer, 0, (int)this.buffer.Length);
  }
  while(myNetworkStream.DataAvailable);
}

Please note that I am only trying to illustrate the idea and you might wnt to consider returning Task<TResult>, as well as whether to have the do{}while loop, any additional processing or cleanup, etc. - all according to your needs.

I would also point your attention to the article by Stephen Toub How do I cancel non-cancelable async operations? and the WithCancellation extension he creates there.

Solution 2

You cannot cancel the ReadAsync since the internal call is unmanaged and uses IOCompletion ports.. Your options are as follows.

  1. Use Socket.Shutdown(). This will return ReadAsync with a socket error of OperationAborted.
  2. Wait for the read to timeout.
  3. Check if data is available before reading from the socket.

Solution 3

I am managing the cancellation by having the Task wait for the cancellation token call between the async call and the await statement like this:

try {

 ....

 Task<int> readTask = input.ReadAsync(buffer, 0, buffer.Length);
 readTask.Wait(myCancellationToken);
 int readBytes= await readTask;

....

}
catch ( OperationCanceledException e )
{
   // handle cancellation
}
Share:
11,355

Related videos on Youtube

Swampie
Author by

Swampie

Updated on June 30, 2022

Comments

  • Swampie
    Swampie almost 2 years

    I am trying to use NetworkStream.ReadAsync() to read data but I cannot find how to cancel the ReadAsync() once called. For background, the NetworkStream is provided to me by a connected BluetoothClient object (from 32Feet.NET Bluetooth Library).

    The current get-it-working code I'm trying is below.

    int bytesRead;
    
    while (this.continueReading)
    {
        bytesRead = await this.stream.ReadAsync(this.buffer, 0, (int)this.buffer.Length);
    
        Console.WriteLine("Received {0} bytes", bytesRead);
    }
    
    Console.WriteLine("Receive loop has ended");
    

    The code works fine receiving data, and will stop looping if the continueReading flag is set to false and data is received, but until data is received it will not proceed past the ReadAsync() line. I cannot see how to abort the call without receiving data.

    I am aware that there is an overload of ReadAsync which provides a CancellationToken, but it appears that as NetworkStream doesn't override the default ReadAsync behaviour, the token is ignored (see NetworkStream.ReadAsync with a cancellation token never cancels).

    I've tried closing the underlying stream, and this causes the waiting ReadAsync call to throw ObjectDisposedException, and the underlying Bluetooth connection gets closed too. Ideally I don't want to completely sever the connection with the device just to stop reading. It doesn't seem a clean way of doing it, and feels unnecessary to tear down the entire stream just to interrupt va ReadAsync() call.

    Any advice?

  • Swampie
    Swampie about 11 years
    I'd forgotten about checking DataAvailable! Thanks for the sample code and for the link to Stephen Toub's article. Both will be helpful for me to achieve that I'm after. Thanks!
  • Swampie
    Swampie about 11 years
    Unfortunately I'm not using Sockets, so I don't believe I can use Socket.Shutdown. I create a BluetoothClient (from 32Feet.NET) and call BluetoothClient.Connect(). Once connected I retrieve the stream via BluetoothClient.GetStream(). As for timeout, the MSDN says the Stream's timeout only applies for synchronous Read() - confirmed by setting ReadTimeout property and ReadAsync() never timed out. The bit about checking for data available from the socket is helpful though, and something I'd completely forgotten about. Thanks
  • James Harmon
    James Harmon about 11 years
    My reading comprehension failed me. I am glad to hear that my un-related answer still helped.
  • unintentionally left blank
    unintentionally left blank over 6 years
    If using TCPClient the TCPClient.Close() method can be used to raise an exception on a hanging readAsync() call.
  • cd491415
    cd491415 about 5 years
    Are you using this with NetworkStream ReadAsync and WriteAsync? Can you provide good working example for both? Thanks
  • Joakim M. H.
    Joakim M. H. over 4 years
    The call to Wait blocks the thread, removing the benefit of using ReadAsync.