HttpURLConnection 实现

发布于 2024-09-14 02:35:58 字数 1551 浏览 14 评论 0原文

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

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 的源代码,其实现确实保留了到相同目的地的连接的 keepalive 缓存。我的问题是,在发送第一个请求后,我无法看到连接如何放回缓存中。断开连接会关闭连接,如果没有断开连接,我仍然看不到连接如何放回缓存中。我看到缓存有一个 run 方法来遍历所有空闲连接(我不确定它是如何调用的),但我找不到如何将连接放回缓存中。唯一发生这种情况的地方是 httpClient 的完成方法中,但是带有响应的 POST 不会调用此方法。 有人能帮我解决这个问题吗?

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

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

但我不确定这是哪个处理程序。正如我所见,sun.net.www.protocol.http.Handler 没有进行任何缓存 谢谢!

I have read that HttpURLConnection supports persistent connections, so that a connection can be reused for multiple requests. I tried it and the only way to send a second POST was by calling openConnection for a second time. Otherwise I got a IllegalStateException("Already connected");
I used the following:

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

The second request is send over the same TCP connection (verified it with wireshark) but I can not understand why (although this is what I want) since I have called disconnect.
I checked the source code for the HttpURLConnection and the implementation does keep a keepalive cache of connections to the same destinations. My problem is that I can not see how the connection is placed back in the cache after I have send the first request. The disconnect closes the connection and without the disconnect, still I can not see how the connection is placed back in the cache. I saw that the cache has a run method to go through over all idle connections (I am not sure how it is called), but I can not find how the connection is placed back in the cache. The only place that seems to happen is in the finished method of httpClient but this is not called for a POST with a response.
Can anyone help me on this?

EDIT
My interest is, what is the proper handling of an HttpUrlConnection object for tcp connection reuse. Should input/output stream be closed followed by a url.openConnection(); each time to send the new request (avoiding disconnect())? If yes, I can not see how the connection is being reused when I call url.openConnection() for the second time, since the connection has been removed from the cache for the first request and can not find how it is returned back.
Is it possible that the connection is not returned back to the keepalive cache (bug?), but the OS has not released the tcp connection yet and on new connection, the OS returns the buffered connection (not yet released) or something similar?
EDIT2
The only related i found was from JDK_KeepAlive

...when the application calls close()
on the InputStream returned by
URLConnection.getInputStream(), the
JDK's HTTP protocol handler will try
to clean up the connection and if
successful, put the connection into a
connection cache for reuse by future
HTTP requests.

But I am not sure which handler is this. sun.net.www.protocol.http.Handler does not do any caching as I saw
Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

滥情空心 2024-09-21 02:35:58

是否应该关闭输入/输出流
接下来是 url.openConnection();
每次发送新请求
(避免断开连接())?

是的。

如果是,我看不到连接情况
当我打电话时重复使用
第二个是 url.openConnection()
时间,自从连接以来
第一次从缓存中删除
请求但找不到具体情况
回来了。

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

Should input/output stream be closed
followed by a url.openConnection();
each time to send the new request
(avoiding disconnect())?

Yes.

If yes, I can not see how the connection is being
reused when I call
url.openConnection() for the second
time, since the connection has been
removed from the cache for the first
request and can not find how it is
returned back.

You are confusing the HttpURLConnection with the underlying Socket and its underlying TCP connection. They aren't the same. The HttpURLConnection instances are GC'd, the underlying Socket is pooled, unless you call disconnect().

花落人断肠 2024-09-21 02:35:58

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

每个 HttpURLConnection 实例都是
用于发出单个请求,但是
底层网络连接到
HTTP服务器可能是透明的
由其他实例共享。调用
InputStream 上的 close() 方法或
HttpURLConnection 的输出流
请求后可能会释放网络
与此相关的资源
实例,但对任何实例都没有影响
共享持久连接。呼唤
connect() 方法可能关闭
底层套接字如果是持久的
否则连接处于空闲状态
时间。

From the javadoc for HttpURLConnection (my emphasis):

Each HttpURLConnection instance is
used to make a single request but the
underlying network connection to the
HTTP server may be transparently
shared by other instances.
Calling the
close() methods on the InputStream or
OutputStream of an HttpURLConnection
after a request may free network
resources associated with this
instance but has no effect on any
shared persistent connection. Calling
the disconnect() method may close the
underlying socket if a persistent
connection is otherwise idle at that
time.

温柔嚣张 2024-09-21 02:35:58

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

I found that the connection is indeed cached when the InputStream is closed. Once the inputStream has been closed the underlying connection is buffered. The HttpURLConnection object is unusable for further requests though, since the object is considered still "connected", i.e. its boolean connected is set to true and is not cleared once the connection is placed back in the buffer. So each time a new HttpUrlConnection should be instantiated for a new POST, but the underlying TCP connection will be reused, if it has not timed out.
So EJP answer's was the correct description. May be the behavior I saw, (reuse of the TCP connection) despite explicitly calling disconnect() was due to caching done by the OS? I do not know. I hope someone who knows can explain.
Thanks.

烟火散人牵绊 2024-09-21 02:35:58

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

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

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

How do you "force use of HTTP1.0" using the HttpUrlConnection of JDK?

According to the section „Persistent Connections” of the Java 1.5 guide support for HTTP1.1 connections can be turned off or on using the java property http.keepAlive (default is true). Furthermore, the java property http.maxConnections indicates the maximum number of (concurrent) connections per destination to be kept alive at any given time.

Therefore, a "force use of HTTP1.0" could be applied for the whole application at once by setting the java property http.keepAlive to false.

短叹 2024-09-21 02:35:58

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

  • 强制使用 HTTP 1.0(1.1 引入了持久连接)- - 这由 http 请求行
  • 发送值为“close”的“Connection”标头指示;这也将强制关闭。

Hmmh. I may be missing something here (since this is an old question), but as far as I know, there are 2 well-known ways to force closing of the underlying TCP connection:

  • Force use of HTTP 1.0 (1.1 introduced persistent connections) -- this as indicated by the http request line
  • Send 'Connection' header with value 'close'; this will force closing as well.
‖放下 2024-09-21 02:35:58

放弃流将导致 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

Abandoning streams will cause idle TCP connections. The response stream should be read completely. Another thing I overlooked initially, and have seen overlooked in most answers on this topic is forgetting to deal with the error stream in case of exceptions. Code similar to this fixed one of my apps that wasn't releasing resources properly:

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();
    }
}

The buffered reader isn't strictly necessary, I chose it because my use case required reading one line at a time.

See also: http://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文