HttpURLConnection 实现
我读到 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
是的。
您将
HttpURLConnection
与底层Socket
及其底层 TCP 连接混淆了。他们不一样。HttpURLConnection
实例会被 GC,底层的Socket
会被池化,除非您调用disconnect()。
Yes.
You are confusing the
HttpURLConnection
with the underlyingSocket
and its underlying TCP connection. They aren't the same. TheHttpURLConnection
instances are GC'd, the underlyingSocket
is pooled, unless you calldisconnect().
来自 HttpURLConnection 的 javadoc (我的重点):
From the javadoc for HttpURLConnection (my emphasis):
我发现当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.
根据Java的“持久连接”部分1.5 指南 对 HTTP1.1 连接的支持可以使用 java 属性
http.keepAlive
(默认为 true)关闭或打开。此外,java 属性 http.maxConnections 指示在任何给定时间每个目标保持活动的最大(并发)连接数。因此,通过将 java 属性
http.keepAlive
设置为 false,可以立即对整个应用程序应用“强制使用 HTTP1.0”。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 propertyhttp.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.嗯。我可能在这里遗漏了一些东西(因为这是一个老问题),但据我所知,有两种众所周知的方法可以强制关闭底层 TCP 连接:
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:
放弃流将导致 TCP 连接空闲。应完整读取响应流。我最初忽略的另一件事是,在出现异常时忘记处理错误流,并且在该主题的大多数答案中都看到了忽略。与此类似的代码修复了我的一个未正确释放资源的应用程序:
缓冲读取器并不是绝对必要的,我选择它是因为我的用例需要一次读取一行。
另请参阅: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:
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