14

我正在尝试使用 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() 行。我看不到如何在不接收数据的情况下中止呼叫。

我知道存在提供 CancellationToken 的 ReadAsync 过载,但似乎由于 NetworkStream 没有覆盖默认的 ReadAsync 行为,因此忽略了令牌(请参阅NetworkStream.ReadAsync with a cancelation token never cancels )。

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

有什么建议吗?

4

3 回答 3

9

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

  1. 使用 Socket.Shutdown()。这将返回带有 OperationAborted 套接字错误的 ReadAsync。
  2. 等待读取超时。
  3. 在从套接字读取之前检查数据是否可用。
于 2013-03-07T21:34:18.383 回答
4

您可以围绕 NetworkStream.Read(或 ReadAsync)实现异步包装器,该包装器还接收您可以监控和尊重自己的取消令牌。像这样的东西:

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 的文章如何取消不可取消的异步操作?以及他在那里创建的 WithCancellation 扩展。

于 2013-03-07T16:35:55.933 回答
2

我通过让任务等待异步调用和等待语句之间的取消令牌调用来管理取消,如下所示:

try {

 ....

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

....

}
catch ( OperationCanceledException e )
{
   // handle cancellation
}
于 2016-08-11T10:30:40.720 回答