问题
我维护的应用程序在大约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
吗?
答案 0 :(得分:6)
根据RFC 1122(TRANSPORT LAYER - TCP),第4.2.3.1节(“重传超时计算”):
“实现还必须包括同一段的连续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,因为您可能会因为您正在测试的环境而反复点击“幻数”。
答案 1 :(得分:2)
好像是Windows默认配置......
根据链接和一些进一步阅读,Windows将默认执行3次重试,并在每次尝试时加倍超时,开始时间为3秒。所以你最终得到3秒+ 6秒+ 12秒= 21秒超时。
答案 2 :(得分:1)
我根据我的问题中的代码编写了一个原始测试应用程序,它通过尝试连接到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的中兴X501,它允许我设置更长的超时。