.NET 4.5 SslStream - 取消异步读/写调用?

时间:2014-06-13 05:28:08

标签: c# .net asynchronous sslstream

有没有办法取消SslStream上的异步读写任务?我尝试使用CancellationToken提供ReadAsync但它似乎不起作用。当下面的代码达到超时(Task.Delay)时,它会在CancellationTokenSource上调用cancel,应该取消读取任务,向调用方法返回错误,调用方法最终再次尝试读取,这引发了一个"当另一个写入操作未决时,无法调用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;
    }

1 个答案:

答案 0 :(得分:1)

查看SslStream的文档,它不支持ReadAsync(它只使用Stream的回退同步实现)。由于SslStream是一个装饰器Stream,如何从底层Stream上的超时安全恢复并不明显,唯一明显的方法是重新初始化整个Stream管道。然而,鉴于潜在的流可能不可寻找,这可能不是一个想法。

为了支持取消,流必须覆盖Stream.ReadAsync(Byte[], Int32, Int32, CancellationToken)。在文档中,NetworkStreamSslStream都不会覆盖消费取消所需的ReadAsync重载(并且abstract Stream无法实现通用取消)。有关支持取消的示例,请参阅FileStream并对比文档的不同之处。

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

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

奖励点。您是否考虑过将Timeout行为重构为装饰器Stream?然后,您可以将超时装饰器放置在基础流上。

stream = new SslStream(
            new TimeoutStream(new FooStream(), Timespan.FromMilliseconds(1000)));