【问题标题】:Where does the socket timeout of 21000 ms come from?21000 毫秒的套接字超时从何而来?
【发布时间】:2015-01-09 20:41:59
【问题描述】:

问题

我维护的一个应用程序在大约 21000 毫秒后不断收到套接字超时,尽管我已经明确设置了更长的超时时间。这个看似神奇的 21000 毫秒值已经出现在其他一些 SO 问题和答案中,我正试图弄清楚它的确切来源。

这是我的代码的精髓:

HttpURLConnection connection = null;
try {
URL url = new URL(urlString);
    connection = (HttpURLConnection) url.openConnection();
    connection.setConnectTimeout(45000);
    connection.setReadTimeout(90000);
    int responseCode = connection.getResponseCode();
    if (responseCode == 200) {
        // code omitted
    }       
} catch (Exception e) {
    // code omitted
}
finally {
    if (connection != null) {
        connection.disconnect();
    }
}

在一个块中捕获所有异常无疑是不理想的,但它是继承的代码,我不愿意弄乱它。我知道它在 21000 毫秒后捕获了 SocketTimeoutException,因为它记录了异常类的简单名称。

线索

我发现一个提问者得到ConnectTimeout after 21000 ms 的问题,尽管明确将其设置为 40000 毫秒。尽管异常类不同,但这很有趣。

我还发现了一个解释不清的答案,声称the server side is responsible for the 21000 ms timeout

我的预感

我认为服务器的任何动作或不作为都不会导致客户端上的套接字超时时间短于预期。但也许 Windows 和 Android 中的 TCP 堆栈共享一个共同的祖先,或者至少使用类似的连接重试逻辑。

会不会是 Android 将最大连接超时时间设置为 21000 毫秒,而在 HttpURLConnection 中设置更长的超时时间是徒劳的?或者移动设备和服务器之间路径上的某些 Windows 机器是否会触发此超时?是否某些 Android 版本会抛出 SocketTimeoutException 而其他版本会抛出 ConnectException

【问题讨论】:

  • “解释不清的答案”肯定是错误的。声称服务器端平台在执行出站连接时的行为会影响其他平台在出站连接到服务器平台时的行为是没有任何意义的。
  • 但是来自客户端和服务器之间路径上某处路由器的出站连接呢?我对网络错误和故障如何传播回客户端的理解有点模糊。

标签: android sockets httpurlconnection


【解决方案1】:

根据RFC 1122 (TRANSPORT LAYER -- TCP), section 4.2.3.1 ("Retransmission Timeout Calculation"):

“实现还必须包括同一段的连续 RTO 值的指数退避”。

所以 xpa1492 的答案听起来是合理的(尽管它是 Windows 特有的); TCP 堆栈的实现要么遵循此 RFC,要么因未能这样做而被淘汰。

顺便说一句,RFC 1122 明确指定 3 秒作为初始超时,这使得 xpa1492 的 (3 + 6 + 12 = 21) 答案听起来像是你的谜题的答案。

是的,Android TCP 堆栈与 Windows TCP 堆栈共享一个共同祖先;它们都是使用 RFC 1122 作为指南 ("[The Linux TCP stack is] an implementation of the TCP protocol defined in RFC 793, RFC 1122 and RFC 2001 with the NewReno and SACK extensions") 创建的。

我怀疑您的问题与无线电干扰有关,因此您可能想尝试启用F-RTO,因为您可能会因为您正在测试的环境而反复点击“幻数”。

【讨论】:

  • 您是否建议Android设备本身将最大连接超时时间设置为21秒?或者设备和它所连接的服务器之间的某些路由器可能会施加该限制?
  • Kevin,是的,它是由抽象层和/或本地设备上的 TCP 堆栈强加的;为什么他们没有记录下来,我不知道。
  • 不幸的是,虽然这个答案看起来应该是正确的,但从经验上看却不是。我倾向于无法覆盖默认超时是设备特定的行为。现在押注该恶意设备是否是另一个 [脏话删除] 三星...
  • @KevinKrumwiede,听起来设备特定的可能性是最合理的;这些设备制造商根本不在乎,并且可能认为一致的 API 支持并不重要,当然,除非某个运营商希望它用于他们的专有应用程序之一。
  • @KevinKrumwiede,您可能会发现问题是设备特定和运营商特定的组合,例如 Verizon 上的 Galaxy S4。
【解决方案2】:

好像是Windows默认配置...

https://social.technet.microsoft.com/Forums/windows/en-US/9e7f59dd-6469-4ade-91ca-ceb5bcaf2675/windows-7-tcp-parameter-tcpmaxconnectretransmissions-and-tcpinitialrtt?forum=w7itpronetworking

根据链接和一些进一步的阅读,Windows 默认情况下会重试 3 次,每次尝试的超时时间加倍,从 3 秒开始。所以你最终得到 3sec + 6sec + 12sec = 21sec 超时。

【讨论】:

    【解决方案3】:

    我根据我的问题中的代码编写了一个粗略的测试应用程序,它通过尝试连接到this answer 中建议的不可路由地址来模拟连接超时。在我的 Moto G (Android 4.4.2) 上,它按预期在大约 45 秒内抛出SocketTimeoutException。奇怪的是,如果我没有明确设置连接超时,它会在大约一分钟后抛出ConnectException

    我将编写一个稍微复杂的测试应用程序并将其发送给客户,以尝试确定设备本身是否设置了 21 秒超时,或者他们的移动网络上的某些路由器是否可能是罪魁祸首。我会用结果更新这个答案。

    结果:这似乎是一个影响 Sprint 的三星 SPH-P100(Galaxy Tab 1)的操作系统错误。我无法访问任何其他运营商的 Tab 1,所以这可能归咎于三星或 Sprint。它似乎一般不会影响 Android 2.x,因为我有一个运行 2.3.6 的 ZTE X501,它允许我设置更长的超时时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多