17

我读过 HttpURLConnection 支持持久连接,因此一个连接可以重复用于多个请求。我试过了,发送第二个 POST 的唯一方法是第二次调用 openConnection。否则我得到一个 IllegalStateException("Already connected"); 我使用了以下内容:

try{
URL url = new URL("http://someconection.com");
}
catch(Exception e){}
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//set output, input etc
//send POST
//Receive response
//Read whole response
//close input stream
con.disconnect();//have also tested commenting this out
con = (HttpURLConnection) url.openConnection();
//Send new POST

第二个请求是通过同一个 TCP 连接发送的(使用wireshark 验证),但我不明白为什么(尽管这是我想要的),因为我已经调用了断开连接。我检查了 HttpURLConnection 的源代码,并且该实现确实保留了到相同目的地的连接的保活缓存。我的问题是,在我发送第一个请求后,我看不到连接是如何放回缓存中的。断开连接关闭连接并且没有断开连接,我仍然看不到连接是如何放回缓存中的。我看到缓存有一个运行方法来遍历所有空闲连接(我不确定它是如何调用的),但我找不到连接是如何放回缓存中的。似乎发生的唯一地方是在 httpClient 的完成方法中,但这不是为带有响应的 POST 调用的。谁可以帮我这个事?

编辑 我的兴趣是,正确处理 HttpUrlConnection 对象以重用 tcp 连接的方法是什么。应该关闭输入/输出流,然后是 url.openConnection(); 每次发送新请求(避免断开())?如果是的话,当我第二次调用 url.openConnection() 时,我看不到连接是如何被重用的,因为第一次请求的连接已经从缓存中删除并且找不到它是如何返回的。连接是否有可能没有返回到keepalive缓存(错误?),但操作系统尚未释放tcp连接并且在新连接上,操作系统返回缓冲连接(尚未释放)或类似的东西? EDIT2 我发现的唯一相关来自JDK_KeepAlive

...当应用程序对 URLConnection.getInputStream() 返回的 InputStream 调用 close() 时,JDK 的 HTTP 协议处理程序将尝试清理连接,如果成功,则将连接放入连接缓存以供将来的 HTTP 请求重用.

但我不确定这是哪个处理程序。sun.net.www.protocol.http.Handler 没有做任何缓存,因为我看到了谢谢!

4

6 回答 6

17

应该关闭输入/输出流,然后是 url.openConnection(); 每次发送新请求(避免断开())?

是的。

如果是的话,当我第二次调用 url.openConnection() 时,我看不到连接是如何被重用的,因为第一次请求的连接已经从缓存中删除并且找不到它是如何返回的。

您将HttpURLConnection与底层Socket及其底层 TCP 连接混淆了。他们不一样。实例是 GC的HttpURLConnection,底层Socket是池化的,除非你调用disconnect().

于 2010-08-12T01:41:42.717 回答
7

来自 HttpURLConnection 的 javadoc(我的重点):

每个 HttpURLConnection 实例用于发出单个请求,但到 HTTP 服务器的底层网络连接可能会被其他实例透明地共享。在请求之后对 HttpURLConnection 的 InputStream 或 OutputStream 调用 close() 方法可能会释放与此实例关联的网络资源,但不会影响任何共享的持久连接。如果持续连接当时处于空闲状态,则调用 disconnect() 方法可能会关闭底层套接字。

于 2010-08-11T18:23:42.850 回答
4

我发现当 InputStream 关​​闭时连接确实被缓存了。一旦 inputStream 被关闭,底层连接就会被缓冲。但是,HttpURLConnection 对象无法用于进一步的请求,因为该对象仍被视为“已连接”,即其布尔连接设置为 true,并且一旦将连接放回缓冲区中就不会被清除。所以每次新的 HttpUrlConnection 都应该为一个新的 POST 实例化,但是底层的 TCP 连接会被重用,如果它没有超时的话。所以 EJP 的答案是正确的描述。可能是我看到的行为,(重用 TCP 连接)尽管显式调用 disconnect() 是由于操作系统完成的缓存?我不知道。希望知道的人解释一下。谢谢。

于 2010-08-12T12:09:46.337 回答
4

如何使用JDK的HttpUrlConnection“强制使用HTTP1.0”?

根据Java 1.5 指南的“持久连接”部分,可以使用 java 属性关闭或打开对 HTTP1.1 连接的支持http.keepAlive(默认为 true)。此外,java 属性http.maxConnections指示每个目标在任何给定时间保持活动的最大(并发)连接数。

http.keepAlive因此,通过将 java 属性设置为 false ,可以一次对整个应用程序应用“强制使用 HTTP1.0” 。

于 2011-05-13T13:52:53.897 回答
2

嗯。我可能在这里遗漏了一些东西(因为这是一个老问题),但据我所知,有两种众所周知的方法可以强制关闭底层 TCP 连接:

  • 强制使用 HTTP 1.0(1.1 引入了持久连接)——如 http 请求行所示
  • 发送值为 'close' 的 'Connection' 标头;这也将强制关闭。
于 2010-10-20T16:01:48.150 回答
0

放弃流将导致空闲的 TCP 连接。应完整读取响应流。我最初忽略的另一件事,并且在有关该主题的大多数答案中都被忽略了,那就是在发生异常时忘记处理错误流。与此类似的代码修复了我的一个未正确释放资源的应用程序:

HttpURLConnection connection = (HttpURLConnection)new URL(uri).openConnection();
InputStream stream = null;
BufferedReader reader = null;
try {
        stream = connection.getInputStream();
        reader = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));

        // do work on part of the input stream

} catch (IOException e) {

    // read the error stream
    InputStream es = connection.getErrorStream();
    if (es != null) {
        BufferedReader esReader = null;
        esReader = new BufferedReader(new InputStreamReader(es, Charset.forName("UTF-8")));
        while (esReader.ready() && esReader.readLine() != null) {
        }
        if (esReader != null)
            esReader.close();
    }

    // do something with the IOException
} finally {

    // finish reading the input stream if it was not read completely in the try block, then close
    if (reader != null) {
        while (reader.readLine() != null) {
        }
        reader.close();
    }

    // Not sure if this is necessary, closing the buffered reader may close the input stream?
    if (stream != null) {
        stream.close();
    }

    // disconnect
    if (connection != null) {
        connection.disconnect();
    }
}

缓冲阅读器并不是绝对必要的,我选择它是因为我的用例需要一次阅读一行。

另请参阅:http ://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html

于 2012-11-13T23:38:16.250 回答