通过超时取消TcpClient ReadAsync操作并在.NET 4.5中捕获此超时事件的正确方法是什么?
TcpClient.ReadTimeout似乎应用于同步只读。
更新:
Tried tro应用这里描述的方法Cancelling an Asynchronous Operation
var buffer = new byte[4096];
CancellationTokenSource cts = new CancellationTokenSource(5000);
int amountRead = await tcpClientStream.ReadAsync(buffer, 0, 4096, cts.Token);
但它永远不会超时取消。有什么不对吗?
答案 0 :(得分:12)
所以我知道这是很长一段时间 但谷歌还是把我赶到了这里 我看到没有标记为答案
对我来说,我这样解决 我做了一个扩展,添加了一个额外超时的方法ReadAsync public static async Task<int> ReadAsync(this NetworkStream stream, byte[] buffer, int offset, int count, int TimeOut)
{
var ReciveCount = 0;
var receiveTask = Task.Run(async () => { ReciveCount = await stream.ReadAsync(buffer, offset, count); });
var isReceived = await Task.WhenAny(receiveTask, Task.Delay(TimeOut)) == receiveTask;
if (!isReceived) return -1;
return ReciveCount;
}
所以如果它返回-1表示读取超时
答案 1 :(得分:3)
编辑:以下要点是我为此所做的解决方法。 https://gist.github.com/svet93/fb96d8fd12bfc9f9f3a8f0267dfbaf68
原件:
我真的很喜欢Khalid Omar的答案,并且我自己做了我认为值得分享的版本:
public static class TcpStreamExtension
{
public static async Task<int> ReadAsyncWithTimeout(this NetworkStream stream, byte[] buffer, int offset, int count)
{
if (stream.CanRead)
{
Task<int> readTask = stream.ReadAsync(buffer, offset, count);
Task delayTask = Task.Delay(stream.ReadTimeout);
Task task = await Task.WhenAny(readTask, delayTask);
if (task == readTask)
return await readTask;
}
return 0;
}
}
它或多或少是相同的东西,除了稍微不同的格式以更易读的方式(对我来说),更重要的是它不使用Task.Run
我不知道为什么它被用在他的示例
编辑:
上面看起来好像乍一看但我发现它会导致一些问题。如果没有数据进入,readAsync调用似乎会泄漏,在我的情况下,它似乎读取稍后的写入,并且数据基本上在内存中不再使用的地方返回。
答案 2 :(得分:1)
将CancellationToken
传递给ReadAsync()
并不会带来太多价值,因为它仅在 it starts reading之前检查令牌的状态:
// If cancellation was requested, bail early with an already completed task.
// Otherwise, return a task that represents the Begin/End methods.
return cancellationToken.IsCancellationRequested
? Task.FromCanceled<int>(cancellationToken)
: BeginEndReadAsync(buffer, offset, count);
通过等待ReadAsync()
或timout任务完成,您可以很容易地自己添加一个timout机制:
byte[] buffer = new byte[128];
TimeSpan timeout = TimeSpan.FromSeconds(30);
var readTask = stream.ReadAsync(buffer, 0, buffer.Length);
var timeoutTask = Task.Delay(timeout);
await Task.WhenAny(readTask, timeoutTask);
if (!readTask.IsCompleted)
{
throw new TimeoutException($"Connection timed out after {timeout}");
}
答案 3 :(得分:0)
你必须自己实现它:它在那个类中不可用。
你应该使用一个定时器,它会在一段时间后终止异步操作,或者在后台线程中使用读同步版本。
你可以找到一个例子:Implementing a timeout with NetworkStream.BeginRead and NetworkStream.EndRead