【问题标题】:TcpClient SocketException with timeout after 20s no matter whatTcpClient SocketException 无论如何都会在 20 秒后超时
【发布时间】:2016-08-01 14:26:07
【问题描述】:

我想等待来自TcpClient 的客户端的缓慢响应,但无论我如何配置它都会在大约 20 秒后超时。这是我的尝试:

using (var client = new TcpClient { ReceiveTimeout = 9999999, SendTimeout = 9999999 })
{
    await client.ConnectAsync(ip, port);
    using (var stream = client.GetStream())
    {
        // Some quick read/writes happen here via the stream with stream.Write() and stream.Read(), successfully.

        // Now the remote host is calculating something long and will reply if finished. This throws the below exception however instead of waiting for >20s.
        var bytesRead = await stream.ReadAsync(new byte[8], 0, 8);
    }
}

例外是IOException:

无法从传输连接中读取数据:连接 尝试失败,因为连接方没有正确响应 一段时间后,或建立连接失败,因为 连接的主机未能响应。

...其中包含SocketException

连接尝试失败,因为连接方没有 一段时间后正确响应,或建立连接 失败,因为连接的主机没有响应

SocketErrorCodeTimedOut

20 多岁的seems to be an OS default on Windows 但不能通过与TcpClient 交互来从托管代码中覆盖它吗?否则我该如何等待回复?

我也尝试过老式的BeginRead-EndRead 方式,EndRead 也是如此。该问题也不是由 Windows 防火墙或 Defender 引起的。

【问题讨论】:

  • 更新:如果我在 ReadAsync() 失败后再次尝试运行,我会得到“现有连接被远程主机强行关闭”。我怀疑远程主机会导致整个问题,但即使在此之前,我也找不到任何迹象表明它会关闭连接。

标签: c# .net sockets tcpclient networkstream


【解决方案1】:

我想等待客户的缓慢响应

请务必注意,失败的是连接。连接超时仅用于建立连接,它应该总是非常快。事实上,操作系统将代表一个应用程序接受连接,因此您实际上只是在谈论数据包往返。 21 秒应该足够了。

一旦建立连接,您就可以删除ReceiveTimeout/SendTimeout 并使用异步读取来永久等待。

【讨论】:

  • 很有趣,我刚刚阅读了您的博文:blog.stephencleary.com/2009/05/… :-)(正在尝试使用 TCP keep-alive 或自定义“keep-alive”协议)。问题是异步读取失败了。连接成功,虽然为了简洁起见我删除了该代码,但在连接和上述慢速 ReadAsync() 之间也成功发生了一些写入/读取。所以一切正常,直到远程主机再次发送数据需要 20 秒以上。
  • @Piedone:我不想这么说,但你绝对确定这就是你所看到的吗?我问是因为 is 异步套接字读取没有超时(对于 .NET 套接字对象)。 ReceiveTimeout 被异步读取忽略。如果服务器从不发送任何东西,那么客户端应该永远坐在那里等待。
  • 我很确定这就是我所看到的。在一些成功的 stream.Write() 和 stream.Read() 调用之后,如果 ReadAsync() 快速从远程获取数据,那么它可以工作;如果不是,则超时(如果响应更快,例如 1 秒左右,则整个代码可以正常工作)。还假设在超时发生时连接是打开的:FWIW TcpClient.Connected == true。此外,我刚刚注意到我没有在示例中包含 GetStream() 调用,对此进行了澄清。
  • 顺便说一句,Read() 在循环中等待时也会发生同样的情况。
  • @Piedone:听起来您应该将代码减少到重现问题的最少代码。
【解决方案2】:

事实证明,远程主机没有及时响应,因此出现了问题。让我详细说明一下,虽然这将是一个非常适合我的案例的解决方案,但也许它对其他人也有用。

真正的问题不是超时本身,如异常所示,而是在随后的Read() 调用中引发的异常显示:“现有连接被远程主机强行关闭”

远程主机没有故意关闭连接。相反,发生的事情是,当它响应缓慢时,它实际上很忙,以至于它也没有处理任何 TCP 流量。虽然本地主机在等待响应时没有明确发送任何内容,但这仍然是一个问题:本地主机试图为远程主机的先前传输发送 ACK。由于这些无法传递,本地主机确定远程主机“强制关闭”连接。

我从Wireshark 的流量中得到了线索(尝试查看表面下的内容而不是四处猜测总是好的):很明显,当远程主机很忙时,它显示出完全的无线电静默。同时 Wireshark 显示本地主机进行了重传尝试,表明这是问题的根源。

因此该解决方案也无法在本地主机上实现,需要更改远程主机的行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-22
    • 2018-03-11
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    • 2015-04-30
    相关资源
    最近更新 更多