这就是证据。
你知道这段代码出了什么问题吗?
[TestMethod]
public void TestTest()
{
var tcp = new TcpClient() { ReceiveTimeout = 5000, SendTimeout = 20000 };
tcp.Connect(IPAddress.Parse("176.31.100.115"), 25);
bool ok = Read(tcp.GetStream()).Wait(30000);
Assert.IsTrue(ok);
}
async Task Read(NetworkStream stream)
{
using (var cancellationTokenSource = new CancellationTokenSource(5000))
{
int receivedCount;
try
{
var buffer = new byte[1000];
receivedCount = await stream.ReadAsync(buffer, 0, 1000, cancellationTokenSource.Token);
}
catch (TimeoutException e)
{
receivedCount = -1;
}
}
}
发布于 2012-10-15 17:43:36
我终于找到了一个变通办法。使用Task.WaitAny将异步呼叫与延迟任务(Task.Delay)相结合。当io任务之前经过延迟时,关闭流。这将强制任务停止。您应该正确处理io任务上的异步异常。并且您应该为延迟任务和io任务添加一个延续任务。
它也适用于tcp连接。关闭另一个线程中的连接(您可以将其视为延迟任务线程)会强制所有使用/等待此连接的异步任务停止。
-编辑--
@vtortola建议的另一个更干净的解决方案是:使用取消令牌注册对stream.Close的调用:
async ValueTask Read(NetworkStream stream, TimeSpan timeout = default)
{
if(timeout == default(TimeSpan))
timeout = TimeSpan.FromSeconds(5);
using var cts = new CancellationTokenSource(timeout); //C# 8 syntax
using(cts.Token.Register(() => stream.Close()))
{
int receivedCount;
try
{
var buffer = new byte[30000];
receivedCount = await stream.ReadAsync(buffer, 0, 30000, tcs.Token).ConfigureAwait(false);
}
catch (TimeoutException)
{
receivedCount = -1;
}
}
}
发布于 2012-09-15 04:35:56
取消是协作的。NetworkStream.ReadAsync
必须配合才能被取消。它很难做到这一点,因为这可能会使流处于未定义的状态。哪些字节已从Windows TCP堆栈读取,哪些尚未读取?IO是不容易取消的。
Reflector显示NetworkStream
不会覆盖ReadAsync
。这意味着它将获得Stream.ReadAsync
的默认行为,即只丢弃令牌。没有通用的方法可以取消流操作,因此BCL Stream
类甚至不尝试(它不能尝试-没有办法做到这一点)。
您应该在Socket
上设置超时。
发布于 2020-04-09 09:50:00
提供了关于三种不同方法的更多上下文。我的服务监视其他web应用程序的可用性。因此,它需要建立到各种网站的大量连接。它们中的一些崩溃/返回错误/变得无响应。
轴Y-挂起的测试(会话)数。由于部署/重新启动而降至0。
I. (1月25日)在修改服务后,最初的实现使用了带有取消令牌的ReadAsync。这导致了许多测试挂起(对这些网站运行请求表明服务器有时确实不返回内容)。
II. (2月17日)部署了一个更改,以保护与Task.Delay的取消。这完全解决了这个问题。
private async Task<int> StreamReadWithCancellationTokenAsync(Stream stream, byte[] buffer, int count, Task cancellationDelayTask)
{
if (cancellationDelayTask.IsCanceled)
{
throw new TaskCanceledException();
}
// Stream.ReadAsync doesn't honor cancellation token. It only checks it at the beginning. The actual
// operation is not guarded. As a result if remote server never responds and connection never closed
// it will lead to this operation hanging forever.
Task<int> readBytesTask = stream.ReadAsync(
buffer,
0,
count);
await Task.WhenAny(readBytesTask, cancellationDelayTask).ConfigureAwait(false);
// Check whether cancellation task is cancelled (or completed).
if (cancellationDelayTask.IsCanceled || cancellationDelayTask.IsCompleted)
{
throw new TaskCanceledException();
}
// Means that main task completed. We use Result directly.
// If the main task failed the following line will throw an exception and
// we'll catch it above.
int readBytes = readBytesTask.Result;
return readBytes;
}
III (3月3日)之后,StackOverflow实现了基于超时的流关闭:
using (timeoutToken.Register(() => stream.Close()))
{
// Stream.ReadAsync doesn't honor cancellation token. It only checks it at the beginning. The actual
// operation is not guarded. As a result if a remote server never responds and connection never closed
// it will lead to this operation hanging forever.
// ReSharper disable once MethodSupportsCancellation
readBytes = await targetStream.ReadAsync(
buffer,
0,
Math.Min(responseBodyLimitInBytes - totalReadBytes, buffer.Length)).ConfigureAwait(false);
}
这种实现带来了挂起(与最初的方法不同):
已恢复到Task.Delay解决方案。
https://stackoverflow.com/questions/12421989
复制相似问题