C# 为什么NetworkStream.ReadAsync没有超时?
我正在将C# 为什么NetworkStream.ReadAsync没有超时?,c#,.net,tcpclient,C#,.net,Tcpclient,我正在将NetworkStream与TcpClient一起使用 首先,我设置我的tcp客户端: tcp = new TcpClient(AddressFamily.InterNetwork) { NoDelay = true, ReceiveTimeout = 5000}; 我的主要数据接收回路: while (true) { //read available data from the device int numBytesR
NetworkStream
与TcpClient
一起使用
tcp = new TcpClient(AddressFamily.InterNetwork)
{ NoDelay = true, ReceiveTimeout = 5000};
while (true)
{
//read available data from the device
int numBytesRead = await ReadAsync();
Console.WriteLine($"{numBytesRead} bytes read"); //BP2
}
public Task<int> ReadAsync()
{
var stream = tcp.GetStream();
return stream.ReadAsync(InBuffer, 0, InBuffer.Length); //BP1
}
公共任务ReadAsync()
{
var stream=tcp.GetStream();
返回stream.ReadAsync(InBuffer,0,InBuffer.Length);//BP1
}
stream.ReadTimeout
是否从tcp
获取了值5000
如果我经常发送数据,一切都会正常工作。但如果我不发送任何数据,5秒后似乎什么也没有发生,没有超时。我在调试器中看到断点BP1
被命中,但直到我从测试台发送数据,BP2
才被命中。我可以让它等待一分钟或更长时间,但它似乎只是在等待,但在一分钟后收到发送的数据,这似乎是不正确的行为。5秒钟后肯定会发生什么事情(我理解这是个例外)
时间已经晚了,所以我期待着一些基本的东西,但是有人能看到我的错误和解决方法吗
补遗 好的,所以当我为我正在使用的实际.Net版本进行RTFM时(有多少次我被MS默认为.Net Core 3,我确实说过已经晚了),我在
ReadTimeout
的备注部分看到:
此属性仅影响通过调用
读取方法。此属性不影响异步读取
通过调用BeginRead方法执行
我现在还不清楚我是否可以使用现代的等待调用来安全地读取套接字数据,并且有一个特定的超时。除了超时之外,它还在工作,但我不确定给定的ReadAsync
在NetworkStream
中如何没有覆盖。我必须做一些丑陋的黑客还是有一个简单的解决办法
在我的例子中,5000是我能期望的最长时间,在得出有问题之前不接收数据-协议没有ping机制,因此如果没有出现任何问题,我假设连接已断开。因此,考虑一个5000毫秒超时的异步读取将是很好的选择。网络对象的超时值仅适用于同步操作。例如,来自: 此选项仅适用于同步接收呼叫 对于
Socket.ReceiveTimeout
、TcpClient.ReceiveTimeout
和NetworkStream.ReadTimeout
,这些实现最终都会导致调用SetSocketOption(SocketOptionLevel.Socket、SocketOptionName.ReceiveTimeout等)
,而这反过来又会有效地调用本机setsocketopt()
功能。发件人:
SO\u RCVTIMEO
DWORD
设置阻止接收呼叫的超时(以毫秒为单位)。
(强调矿山)
正是底层本机API中的这种限制,才是托管API中存在相同限制的原因。超时值不会应用于网络对象上的异步I/O
您需要自己实现超时,在超时发生时关闭套接字。例如:
async任务ReadAsync(TcpClient客户端,字节[]缓冲区,int索引,int长度,TimeSpan超时)
{
任务结果=client.GetStream().ReadAsync(缓冲区、索引、长度);
等待任务.wheny(结果,任务.延迟(超时));
如果(!result.IsCompleted)
{
client.Close();
}
返回等待结果;
}
关于这个主题的其他变化可以在其他相关问题中找到:你所能做的就是关闭插座。即使对于同步操作,如果发生超时,套接字也不再可用。没有可靠的方法中断读取操作并期望套接字保持一致
当然,您可以选择在关闭套接字之前提示用户。但是,如果要这样做,您将在应用程序体系结构的更高级别上实现超时,这样I/O操作本身就不会意识到超时。可能与@GSerg有关,看起来可能是这样,但建议在回答中使用超时,这就是我正在做的。它确实提到了
ReadAsync
是一个非重写的基本方法,但我在这一点上迷失了方向!这是真的。@jdweng我不知道你在说什么@jdweng:这个问题与NoDelay
属性无关NoDelay
影响Nagel算法是否启用,并且与I/O操作超时无关。因此,我们是说socket receive从根本上说是一种阻塞操作,在较低级别上根本没有异步替代方法,.Net所能做的就是将其包装在线程中?我很好奇Stream.ReadAsync
如何知道阻塞调用何时找到。无论如何,如果这是唯一的真实选择,它有点臭,但不是太可怕。感谢您的精彩解释这其中的一个“问题”是您得到了一个ObjectDisposedException
,我想这可能很容易错过,因为您通常处理IOException
和SocketException
。您可以返回0
,调用者可以决定重新连接,但是您有一个悬空读取操作。。。不确定是否有一个完美的解决方案。@Mr.Boy:“我们是说套接字接收从根本上说是一个阻塞操作,在低级别上根本没有异步替代方案”——不,一点也没有。在操作系统级别,异步操作非常可能(请参阅“重叠I/O”和“I/O完成端口”)。只是在使用异步操作时不支持超时。。。。。。这其中的一个“问题”是您得到了一个ObjectDisposedException”--这根本不应该是一个问题。只需在捕获的异常中包含该类型的异常。你不应该处理异常