1

有什么方法可以取消 SslStream 上的异步读写任务?我尝试为 ReadAsync 提供 CancellationToken,但它似乎不起作用。当下面的代码达到它的超时时间(Task.Delay)时,它会在 CancellationTokenSource 上调用取消,它应该取消读取任务,向调用方法返回一个错误,调用方法最终会再次尝试读取,这会引发“The当另一个写入操作处于挂起状态时,无法调用 BeginRead 方法”异常。

在我的特定应用程序中,我可以通过关闭套接字并重新连接来解决此问题,但是与重新建立连接相关的开销很高,因此不太理想。

    private async Task<int> ReadAsync(byte[] buffer, int offset, int count, DateTime timeout)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        if (socket.Poll(Convert.ToInt32(timeout.RemainingTimeout().TotalMilliseconds) * 1000, SelectMode.SelectRead) == true)
        {
            Task<int> readTask = stream.ReadAsync(buffer, offset, count, cancellationTokenSource.Token);

            if (await Task.WhenAny(readTask, Task.Delay(timeout.RemainingTimeout())) == readTask)
                return readTask.Result;
            else
                cancellationTokenSource.Cancel();
        }
        return -1;
    }
4

1 回答 1

1

查看文档SslStream,它不支持ReadAsync(它只是使用来自 的后备同步实现Stream)。由于 SslStream 是一个装饰器 Stream,如何安全地从底层 Stream 的超时中恢复并不明显,唯一明显的方法是重新初始化整个 Stream 管道。但是,考虑到底层流可能不可搜索,这可能也不是主意。

为了支持取消,流必须覆盖Stream.ReadAsync(Byte[], Int32, Int32, CancellationToken). 在文档中,既没有NetworkStream也没有覆盖需要消费取消SslStream的重载(并且不可能实现通用取消)。有关支持取消的示例,请参阅FileStream并对比文档的不同之处。ReadAsyncabstract Stream

因此,对于一个具体的案例,如果我们在超时后HttpStream使用SslStreamthen 进行装饰,我们希望通过HttpStream在我们超时的位置打开背部来恢复(使用Range标题)。但是没有办法使用IO.Stream类来公开它。

最后,您应该考虑您的失败案例应该是什么。为什么会ReadAsync超时?在我能想到的大多数情况下,这应该是由于不可恢复的网络问题,这将需要Stream重新初始化。

加分。您是否考虑过将您的超时行为重构为装饰器流?然后,您可以将超时装饰器放置到您的底层流上。

stream = new SslStream(
            new TimeoutStream(new FooStream(), Timespan.FromMilliseconds(1000)));
于 2014-06-13T05:47:24.007 回答