问题
Is there any way to cancel a asynchronous read or write task on a SslStream? I have tried providing ReadAsync with a CancellationToken but it doesnt appear to work. When the following code reaches it's timeout (the Task.Delay), it calls cancel on the CancellationTokenSource which should cancel the read task, returns a error to the calling method, and the calling method eventually tries to read again, which raises a "The BeginRead method cannot be called when another write operation is pending" exception.
In my specific application I could work around this by closeing the socket and reconnecting, but there is a high overhead associated with reestablishing the connection so it is less than ideal.
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:
Looking at the doc for SslStream
, it does not support ReadAsync
(it simply uses the fallback synchronous implementation from Stream
). Since SslStream is a decorator Stream, is isn't obvious how to safely recover from a timeout on the underlying Stream, and the only obvious way would be to re-initialize the entire Stream pipeline. However given that the underlying stream might not be seekable, again this might not be idea.
For support of cancellation, the stream would have to override Stream.ReadAsync(Byte[], Int32, Int32, CancellationToken)
. In the documentation, neither NetworkStream
nor SslStream
overrides the overload of ReadAsync
required to consume cancellation (and abstract Stream
couldn't possibly implement generic cancellation). For an example where cancellation IS supported, see FileStream and contrast how the documentation differs.
So for a concrete case, if we were decorating HttpStream
using SslStream
then after a timeout we would want to recover by opening the HttpStream
back at the position where we timed out (using the Range
header). But there is no way to expose that generically using the IO.Stream
class.
Finally you should consider what your failure case should be. Why would ReadAsync
timeout? In the majority of cases I can think of, it should be due to unrecoverable network issues, which would necessitate the Stream
being reinitialized.
Bonus point. Have you considered refactoring out your Timeout behaviour into a decorator Stream? You could then place the timeout decorator onto your underlying stream.
stream = new SslStream(
new TimeoutStream(new FooStream(), Timespan.FromMilliseconds(1000)));
来源:https://stackoverflow.com/questions/24198290/net-4-5-sslstream-cancel-a-asynchronous-read-write-call