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连接发送的(通过wiresharkvalidation)但我无法理解为什么(虽然这是我想要的)因为我已经调用了disconnect。 我检查了HttpURLConnection的源代码,并且实现确实保持了对相同目标的连接的keepalive缓存。 我的问题是,在发送第一个请求后,我无法看到连接如何放回缓存中。 断开连接关闭连接,没有断开连接,仍然无法看到连接如何放回缓存。 我看到缓存有一个run方法来遍历所有空闲连接(我不确定它是如何被调用的),但是我找不到连接如何放回缓存中。 似乎唯一发生的地方是httpClient的完成方法,但是没有调用具有响应的POST。 谁可以帮我这个事?

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

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

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

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

是。

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

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

从用于HttpURLConnection的javadoc(我的重点):

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

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

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

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

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

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

  • 强制使用HTTP 1.0(1.1引入持久连接) – 这由http请求行指示
  • 发送’Connection’标题,其值为’close’; 这也将迫使关闭。

放弃流将导致空闲TCP连接。 应完全读取响应流。 我最初忽略的另一件事,在这个主题的大多数答案中都被忽略了,就是忘记在exception的情况下处理错误流。 代码类似于我修复的一个未正确发布资源的应用程序:

 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