HttpURLConnection getInputStream:总是在 180 秒后超时

Posted

技术标签:

【中文标题】HttpURLConnection getInputStream:总是在 180 秒后超时【英文标题】:HttpURLConnection getInputStream: timeout always after 180 seconds 【发布时间】:2015-12-03 17:09:45 【问题描述】:

我正在尝试以这种方式在连接速度非常慢的情况下下载文件:

    java.net.URL url = new URL("https://X.X.X.X:8443/path/2f6b88cf2b70ee933197edfc9627a9bc/");
    HttpURLConnection connection = (HttpURLConnection)  url.openConnection();
    connection.setRequestMethod("GET");
    connection.setDoOutput(true);
    connection.setConnectTimeout(240 * 1000);
    connection.setReadTimeout(240 * 1000);
    long start = System.currentTimeMillis();
    Files.copy(connection.getInputStream(), new File("test.zip").toPath());
    System.out.println("Time: "+((System.currentTimeMillis() - start) / 1000) + " sec.");

我注意到由于某些原因(本机 Windows 套接字超时?)它总是在下载 180 秒后中断,没有任何例外。

在 setConnectTimeout(...) 或 setReadTimeout(...) 中设置的超时没有帮助。

我尝试使用 wget 下载该文件:

wget https://X.X.X.X:8443/path/2f6b88cf2b70ee933197edfc9627a9bc/ --no-check-certificate
--2015-09-07 14:36:12--  https://X.X.X.X:8443/path/2f6b88cf2b70ee933197edfc9627a9bc/
Connecting to X.X.X.X:8443... connected.
WARNING: The certificate of ‘X.X.X.X’ is not trusted.
WARNING: The certificate of ‘X.X.X.X’ hasn't got a known issuer.
The certificate's owner does not match hostname ‘X.X.X.X’
HTTP request sent, awaiting response... 302 Found
Location: https://X.X.X.X:8443/files/test.zip [following]
--2015-09-07 14:36:16--  https://X.X.X.X:8443/files/test.zip
Reusing existing connection to X.X.X.X:8443.
HTTP request sent, awaiting response... 200 OK
Length: 321917584 (307M) [application/zip]
Saving to: ‘test.zip’

test.zip                                  100%[====================================================================================>] 307.00M   253KB/s   in 19m 50ss

完整的文件在 20 分钟后成功保存在磁盘上。

HttpURLConnection 有什么问题?

编辑: 我尝试通过http协议从其他服务器下载测试文件,一切正常。这似乎是服务器或协议特定的问题。但是为什么 wget 设法下载整个文件?

Edit2:按照你的建议我也试过了:

移除connection.setDoOutput(true); 使用直接链接避免重定向 302 用自定义实现替换 Files.copy 方法

很遗憾,以上都没有帮助。

编辑3: 我注意到该文件也可以通过不安全的 http 协议在同一台服务器上使用。所以我只更改了代码中的 URL,在 120 秒后我得到了:

Exception in thread "main" java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:196)
    at java.net.SocketInputStream.read(SocketInputStream.java:122)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
    at sun.net.www.MeteredStream.read(MeteredStream.java:134)
    at java.io.FilterInputStream.read(FilterInputStream.java:133)
    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3066)
    at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3060)
    at java.nio.file.Files.copy(Files.java:2735)
    at java.nio.file.Files.copy(Files.java:2854) 

【问题讨论】:

我对 readTimeout 的理解是等待数据可用的时间量。无论如何,它们以毫秒为单位,而您已经给了 240,000 - 4 分钟。 我尝试将超时时间都减少到 10s 或增加到 10m,但它总是在 180s 后中断。 您能发布观察到的异常的完整堆栈跟踪吗? @tomek.ceszke 从 wget 输出 - 有一个 302 重定向。您可以尝试使用重定向的 URL,而不是使用当前 URL,看看会发生什么? @tomek.ceszke 您正在尝试的这段代码是更大程序的一部分还是独立程序的一部分? Files.copy 返回复制的字节数。你能打印那个值吗? 【参考方案1】:
connection.setRequestMethod("GET");

您打算在这里执行 HTTP GET。

connection.setDoOutput(true);

在这里您将其更改为 PUT。

Files.copy(connection.getInputStream(), new File("test.zip").toPath());

在这里,您无需编写任何内容即可获得输入流。服务器仍在等待您从不发送的 POST 数据,因此它从不发送响应,因此您正在超时。

丢失setDoOutput(true); 行。

【讨论】:

感谢您的回答,但没有帮助。 你必须展示你做了什么。将其编辑到您的问题中。【参考方案2】:

我终于在这里找到了解决方案It is not possible to download large files at Jetty server(再次感谢 ***)。问题出在服务器端。

我们使用的 Jetty 9.2 有一个错误,它会中断在慢速连接上提供大文件 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=472621)。似乎并不总是抛出异常。

尽管传输停止或连接重置,Wget 和浏览器还是能够以某种方式完成下载。不幸的是,我的 Java 应用程序更敏感...

将捆绑的 Jetty 升级到最新的稳定版本 9.3.3 修复了所有下载问题。

【讨论】:

以上是关于HttpURLConnection getInputStream:总是在 180 秒后超时的主要内容,如果未能解决你的问题,请参考以下文章

Android之HttpURLConnection

HttpURLConnection.getInputStream 很慢

同步高并发的HTTP请求(HttpURLConnection)?

java成神之——HttpURLConnection访问api

httpURLConnection:post 参数可以有多长?

HttpURLConnection 不解压缩 Gzip