如何在不关闭流的情况下取消NetworkStream.ReadAsync

时间:2013-03-07 14:28:35

标签: c# .net asynchronous async-await networkstream

我正在尝试使用NetworkStream.ReadAsync()来读取数据,但是一旦调用,我找不到如何取消ReadAsync()。对于后台,NetworkStream由连接的BluetoothClient对象(来自32Feet.NET蓝牙库)提供给我。

我正在尝试的当前get-it-working代码如下。

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");

代码可正常接收数据,并且如果continueReading标志设置为false并且接收到数据,则将停止循环,但是在接收到数据之前,它将不会继续通过ReadAsync()行。我无法看到如何在不接收数据的情况下中止呼叫。

我知道ReadAsync存在重载,它提供了CancellationToken,但看起来由于NetworkStream没有覆盖默认的ReadAsync行为,因此忽略该标记(参见NetworkStream.ReadAsync with a cancellation token never cancels)。

我尝试关闭基础流,这会导致等待的ReadAsync调用抛出ObjectDisposedException,底层的蓝牙连接也会关闭。理想情况下,我不想完全切断与设备的连接,只是为了停止阅读。它似乎不是一种干净的方式,并且感觉不必拆除整个流只是为了中断va ReadAsync()调用。

有什么建议吗?

3 个答案:

答案 0 :(得分:8)

您无法取消ReadAsync,因为内部呼叫是非托管的并且使用IOCompletion端口。您的选项如下。

  1. 使用Socket.Shutdown()。这将返回ReadAsync,套接字错误为OperationAborted。
  2. 等待读取超时。
  3. 在从插座读取数据之前检查数据是否可用。

答案 1 :(得分:2)

我正在通过让Task等待异步调用和await语句之间的取消令牌调用来管理取消:

try {

 ....

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

....

}
catch ( OperationCanceledException e )
{
   // handle cancellation
}

答案 2 :(得分:1)

您可以在NetworkStream.Read(或ReadAsync)周围实现异步包装,它还会收到您可以监控并自己尊重的canceltoken。像这样:

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);
}

请注意,我只是想说明这个想法,你可能会考虑返回Task<TResult>,以及是否有do {} while循环,任何其他处理或清理等等 - 所有根据您的需要。

我还要提请你注意Stephen Toub How do I cancel non-cancelable async operations?的文章和他在那里创建的WithCancellation扩展。