gpt4 book ai didi

c# - 为什么 NetworkStream.ReadAsync 没有超时?

转载 作者:行者123 更新时间:2023-12-05 01:36:22 36 4
gpt4 key购买 nike

我正在使用 NetworkStreamTcpClient

  1. 首先我设置我的 tcp 客户端:

        tcp = new TcpClient(AddressFamily.InterNetwork)
    { NoDelay = true, ReceiveTimeout = 5000};
  2. 我的主要数据接收循环:

        while (true)
    {
    //read available data from the device
    int numBytesRead = await ReadAsync();
    Console.WriteLine($"{numBytesRead} bytes read"); //BP2
    }
  3. 以及实际的TCP数据读取:

    public Task<int> ReadAsync()
    {
    var stream = tcp.GetStream();
    return stream.ReadAsync(InBuffer, 0, InBuffer.Length); //BP1
    }

我将其连接到测试平台,让我可以手动发送数据包。通过设置断点和调试,我检查了 stream.ReadTimeouttcp 中获取值 5000。

如果我频繁发送数据,一切都会按预期进行。 但是如果我不发送任何数据,5 秒后似乎什么也没有发生,没有超时。我看到断点 BP1 在调试器中被击中,但在我从测试平台发送数据之前,BP2 没有被击中。我可以让它等待一分钟或更长时间,它似乎只是在等待,但在一分钟后收到发送的数据,这似乎是不正确的行为。 5 秒后 某事 肯定会发生(据我所知是一个异常(exception))?

已经很晚了,所以我期待一些非常基本的东西,但有人能看出我的错误是什么和解决方案吗?


附录

好的,所以当我对我正在使用的实际 .Net 版本进行 RTFM 时(有多少次我被默认为 .Net Core 3 的 MS 发现了,我确实说过它已经晚了)我在评论部分看到了读取超时:

This property affects only synchronous reads performed by calling the Read method. This property does not affect asynchronous reads performed by calling the BeginRead method.

我现在不清楚我是否可以完全使用现代可等待调用来安全地读取套接字数据,并特别设置超时。除了超时外它都在工作,但我不确定给定的 ReadAsync 如何在 NetworkStream 中没有覆盖。我必须做一些丑陋的 hack 还是有一个简单的解决方案?

在我的例子中,5000 是我在断定存在问题之前预计不会收到数据的最长时间 - 该协议(protocol)没有 ping 机制,所以如果没有出现,我假设连接已断开。因此,认为具有 5000 毫秒超时的异步读取会很好而且整洁。

最佳答案

网络对象的超时值仅适用于同步 操作。例如,来自 the documentation :

This option applies to synchronous Receive calls only.

对于 Socket.ReceiveTimeoutTcpClient.ReceiveTimeoutNetworkStream.ReadTimeout,所有实现最终都会调用 SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ...) 这反过来有效地调用 native setsockopt() 函数。来自 that documentation :

SO_RCVTIMEO DWORD Sets the timeout, in milliseconds, for blocking receive calls.

(强调我的)

底层 native API 中的这种限制是托管 API 中存在相同限制的原因。超时值不适用于网络对象上的异步 I/O。

您需要自己实现超时,方法是在超时发生时关闭套接字。例如:

async Task<int> ReadAsync(TcpClient client, byte[] buffer, int index, int length, TimeSpan timeout)
{
Task<int> result = client.GetStream().ReadAsync(buffer, index, length);

await Task.WhenAny(result, Task.Delay(timeout));

if (!result.IsCompleted)
{
client.Close();
}

return await result;
}

关于这个主题的其他变体可以在其他相关问题中找到:
NetworkStream.ReadAsync with a cancellation token never cancels
Cancel C# 4.5 TcpClient ReadAsync by timeout

关闭套接字实际上是您所能做的全部。即使对于同步操作,如果发生超时,套接字将不再可用。没有可靠的方法来中断读取操作并期望套接字保持一致。

当然,您可以选择在关闭套接字之前提示用户。但是,如果您要这样做,您将在应用程序架构的更高级别实现超时,这样 I/O 操作本身就根本不知道超时。

关于c# - 为什么 NetworkStream.ReadAsync 没有超时?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62161695/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com