Java HttpURLConnection 实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3460990/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 00:58:44  来源:igfitidea点击:

HttpURLConnection implementation

network-programmingjavajdk1.6httpurlconnection

提问by Cratylus

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:

我已经读过 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

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?

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

EDITMy 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? EDIT2The only related i found was from JDK_KeepAlive

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

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

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

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

采纳答案by user207421

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

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

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.

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

You are confusing the HttpURLConnectionwith the underlying Socketand itsunderlying TCP connection. They aren't the same. The HttpURLConnectioninstances are GC'd, the underlying Socketis pooled, unless you call disconnect().

你是混淆HttpURLConnection与底层Socket它的TCP连接。他们不一样。该HttpURLConnection实例是GC'd,底层Socket是汇集,除非你打电话disconnect().

回答by Jim Garrison

From the javadoc for HttpURLConnection (my emphasis):

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

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 mayclose the underlying socket if a persistent connection is otherwise idle at that time.

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

回答by Cratylus

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.

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

回答by StaxMan

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 连接:

  • 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.
  • 强制使用 HTTP 1.0(1.1 引入了持久连接)——这由 http 请求行指示
  • 发送值为 'close' 的 'Connection' 标头;这也将强制关闭。

回答by deadhorse

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

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

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

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

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

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

回答by Crutis

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:

放弃流将导致空闲 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();
    }
}

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

另见:http: //docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html