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

15

我正在尝试使用NetworkStream.ReadAsync()来读取数据,但我无法找到如何在调用后取消ReadAsync()的方法。需要说明的是,NetworkStream由一个已连接的BluetoothClient对象(来自32Feet.NET蓝牙库)提供。

我正在尝试的当前代码如下。

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个回答

9

由于内部调用是不受管理的并使用IOCompletion端口,因此您无法取消ReadAsync。 您有以下选项:

  1. 使用Socket.Shutdown()。 这将返回带有操作中止的套接字错误的ReadAsync。
  2. 等待读取超时。
  3. 在从套接字读取之前检查数据是否可用。

很遗憾,我没有使用Sockets,所以我不认为我可以使用Socket.Shutdown。我创建了一个BluetoothClient(来自32Feet.NET),并调用BluetoothClient.Connect()。一旦连接成功,我通过BluetoothClient.GetStream()检索流。至于超时,MSDN表示流的超时仅适用于同步读取(通过设置ReadTimeout属性进行确认,而ReadAsync()从未超时)。关于检查套接字中是否有可用数据的部分是有帮助的,这是我完全忘记了的。谢谢。 - Swampie
我的阅读理解能力有所不足。很高兴听到我的无关答案仍然有所帮助。 - James Harmon
如果使用TCPClient,则可以使用TCPClient.Close()方法在挂起的readAsync()调用上引发异常。 - unintentionally left blank

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 的文章 How do I cancel non-cancelable async operations? 以及他在那里创建的 WithCancellation 扩展。

我忘记检查DataAvailable了!感谢您提供的示例代码和Stephen Toub文章的链接。这两者对我实现目标都非常有帮助。谢谢! - Swampie

2

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

try {

 ....

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

....

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

你是否正在使用NetworkStream的ReadAsync和WriteAsync?你能提供这两个方法的好的工作示例吗?谢谢。 - cd491415
2
调用 Wait 会阻塞线程,使使用 ReadAsync 的好处失效。 - Joakim M. H.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接