HttpURLConnection实现

时间:2010-08-11 17:12:14

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

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

  

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

但我不确定这是哪个处理程序。 sun.net.www.protocol.http.Handler没有像我看到的那样进行任何缓存 谢谢!

6 个答案:

答案 0 :(得分:17)

  

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

  

如果是,我看不出连接是怎样的   我打电话时重复使用   url.openConnection()为第二个   时间,因为连接已经   从第一个缓存中删除   请求,但无法找到它是怎么回事   回来了。

您将HttpURLConnection与基础Socket底层TCP连接混淆。他们不一样。除非您致电HttpURLConnection

,否则Socket个实例为GC,基础disconnect().已合并

答案 1 :(得分:7)

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

  

每个HttpURLConnection实例都是   用于制作单个请求但是   底层网络连接到   HTTP服务器可能是透明的   由其他实例共享。致电   InputStream上的close()方法或   HttpURLConnection的OutputStream   请求后可以免费上网   与此相关的资源   实例但对任何事都没有影响   共享持久连接。调用   disconnect()方法可能关闭   底层套接字如果是持久的   否则连接是空闲的   时间。

答案 2 :(得分:4)

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

答案 3 :(得分:4)

  

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

根据„Persistent Connections” of the Java 1.5 guide部分,可以使用java属性http.keepAlive关闭或打开HTTP1.1连接(默认为true)。此外,java属性http.maxConnections表示在任何给定时间保持活动状态的每个目标的最大(并发)连接数。

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

答案 4 :(得分:2)

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

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

答案 5 :(得分: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