你能解释一下HttpURLConnection连接过程吗?

时间:2012-04-12 03:22:50

标签: java inputstream httpurlconnection outputstream urlconnection

我使用HTTPURLConnection连接到网络服务。我知道如何使用HTTPURLConnection,但我想了解它是如何工作的。基本上,我想知道以下内容:

  • HTTPURLConnection尝试建立与给定网址的连接在哪一点?
  • 我在哪一点上可以知道我能够成功建立连接?
  • 是否正在建立连接并在一步/方法调用中发送实际请求?它是什么方法?
  • 你能解释一下外行人getOutputStreamgetInputStream的功能吗?我注意到,当我尝试连接的服务器关闭时,我会在Exception获得getOutputStream。这是否意味着HTTPURLConnection仅在我调用getOutputStream时才开始建立连接? getInputStream怎么样?由于我只能在getInputStream获得响应,那么这是否意味着我还没有在getOutputStream发送任何请求,而只是建立连接?当我调用HttpURLConnection时,getInputStream会回到服务器请求响应吗?
  • 我是否正确地说openConnection只创建一个新的连接对象但尚未建立任何连接?
  • 如何衡量读取开销并连接开销?

5 个答案:

答案 0 :(得分:176)

String message = URLEncoder.encode("my message", "UTF-8");

try {
    // instantiate the URL object with the target URL of the resource to
    // request
    URL url = new URL("http://www.example.com/comment");

    // instantiate the HttpURLConnection with the URL object - A new
    // connection is opened every time by calling the openConnection
    // method of the protocol handler for this URL.
    // 1. This is the point where the connection is opened.
    HttpURLConnection connection = (HttpURLConnection) url
            .openConnection();
    // set connection output to true
    connection.setDoOutput(true);
    // instead of a GET, we're going to send using method="POST"
    connection.setRequestMethod("POST");

    // instantiate OutputStreamWriter using the output stream, returned
    // from getOutputStream, that writes to this connection.
    // 2. This is the point where you'll know if the connection was
    // successfully established. If an I/O error occurs while creating
    // the output stream, you'll see an IOException.
    OutputStreamWriter writer = new OutputStreamWriter(
            connection.getOutputStream());

    // write data to the connection. This is data that you are sending
    // to the server
    // 3. No. Sending the data is conducted here. We established the
    // connection with getOutputStream
    writer.write("message=" + message);

    // Closes this output stream and releases any system resources
    // associated with this stream. At this point, we've sent all the
    // data. Only the outputStream is closed at this point, not the
    // actual connection
    writer.close();
    // if there is a response code AND that response code is 200 OK, do
    // stuff in the first if block
    if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
        // OK

        // otherwise, if any other status code is returned, or no status
        // code is returned, do stuff in the else block
    } else {
        // Server returned HTTP error code.
    }
} catch (MalformedURLException e) {
    // ...
} catch (IOException e) {
    // ...
}

在上面的示例HTTP POST中,您的问题的前3个答案在每个方法旁边列为内联注释。

来自getOutputStream

  

返回写入此连接的输出流。

基本上,我认为你对这是如何运作有很好的理解,所以让我以外行的方式重申一下。 getOutputStream基本上打开连接流,目的是将数据写入服务器。在上面的代码示例中,“message”可以是我们发送到服务器的注释,该注释表示帖子上留下的评论。当您看到getOutputStream时,您正在打开连接流进行写入,但在致电writer.write("message=" + message);之前,您实际上并未写入任何数据。

来自getInputStream()

  

返回从此打开的连接读取的输入流。如果读取超时在数据可用于读取之前到期,则从返回的输入流读取时可能抛出SocketTimeoutException。

getInputStream反其道而行之。与getOutputStream一样,它也会打开连接流,但目的是从服务器读取数据,而不是写入数据。如果连接或流打开失败,您会看到SocketTimeoutException

  

getInputStream怎么样?因为我只能在getInputStream上获得响应,那么这是否意味着我还没有在getOutputStream发送任何请求,而只是建立连接?

请记住,发送请求和发送数据是两种不同的操作。当您调用 getOutputStream或getInputStream url.openConnection()时,您向服务器发送请求以建立连接。发生握手,服务器向您发回确认连接已建立的确认。然后,在那个时间点,您准备发送或接收数据。因此,您不需要调用getOutputStream来建立连接打开一个流,除非您发出请求的目的是发送数据。

通俗地说,提出getInputStream请求相当于打电话给你朋友的房子说“嘿,如果我过来借那副副把手,还可以吗?”和你的朋友说:“当然!来吧,得到它”。然后,在那时,连接完成,你走到朋友的家里,敲门,请副把手,然后走回你家。

使用getOutputStream的类似示例会涉及给你的朋友打电话并说“嘿,我有欠你的钱,我可以寄给你吗”?你的朋友,你需要金钱和生病才能保存这么长时间,他说:“当然,请你过来这个便宜的私生子”。所以你走到你朋友的家里并把钱“邮寄”给他。然后他把你踢出去然后走回你家。

现在,继续这个外行的例子,让我们看看一些例外情况。如果你打电话给你的朋友并且他不在家,那可能是500错误。如果您打电话并收到断开连接的号码信息,因为您的朋友已经厌倦了您一直借钱,那就是找不到404页面。如果您的手机因为没有支付账单而死亡,那可能是IOException。 (注意:此部分可能不是100%正确。它旨在让您大致了解外行人所发生的事情。)

问题#5:

是的,你是正确的,openConnection只是创建一个新的连接对象,但没有建立它。当您调用getInputStream或getOutputStream时建立连接。

openConnection创建一个新的连接对象。来自URL.openConnection javadocs

  

每次通过调用此URL的协议处理程序的openConnection方法打开一个新连接。

当您调用openConnection时建立连接,并在实例化时调用InputStream,OutputStream或两者。

问题#6

为了测量开销,我通常会在整个连接块周围包含一些非常简单的时序代码,如下所示:

long start = System.currentTimeMillis();
log.info("Time so far = " + new Long(System.currentTimeMillis() - start) );

// run the above example code here
log.info("Total time to send/receive data = " + new Long(System.currentTimeMillis() - start) );

我确信有更多高级方法可以测量请求时间和开销,但这通常足以满足我的需求。

有关关闭连接的信息,请参阅In Java when does a URL connection close?

答案 1 :(得分:14)

Tim Bray一步一步地说明了openConnection()没有建立实际的连接。相反,在调用getInputStream()或getOutputStream()等方法之前,不会建立实际的HTTP连接。

http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection

答案 2 :(得分:1)

  

HTTPURLConnection在哪一点尝试建立与给定URL的连接?

在URL中指定的端口(如果有),否则为80表示HTTP,443表示HTTPS。我相信这是记录在案的。

  

我可以在哪一点知道我能够成功建立连接?

调用getInputStream()或getOutputStream()或getResponseCode()而不会出现异常。

  

是否建立连接并在一步/方法调用中发送实际请求?它是什么方法?

不,没有。

  

你能解释一下外行人的getOutputStream和getInputStream的功能吗?

如果需要,它们中的任何一个首先连接,然后返回所需的流。

  

我注意到当我尝试连接的服务器关闭时,我在getOutputStream上得到一个Exception。这是否意味着HTTPURLConnection只会在调用getOutputStream时开始建立连接? getInputStream怎么样?因为我只能在getInputStream中获得响应,那么它是否意味着我还没有在getOutputStream发送任何请求但只是建立连接?当我调用getInputStream时,HttpURLConnection是否返回服务器请求响应?

见上文。

  

我是否正确地说openConnection只是创建一个新的连接对象但是还没有建立任何连接?

  

如何衡量读取开销和连接开销?

连接:花时间getInoutStream()或getOutputStream()返回,无论你先调用哪一个。阅读:从开始第一次阅读到获得EOS的时间。

答案 3 :(得分:1)

我通过练习捕获了低级别的数据包交换,发现网络连接仅由诸如getInputStream,getOutputStream,getResponseCode,getResponseMessage等操作触发。

这是我尝试编写一个小程序以将文件上传到Dropbox时捕获的数据包交换。

enter image description here

下面是我的玩具程序和注释

    /* Create a connection LOCAL object,
     * the openConnection() function DOES NOT initiate
     * any packet exchange with the remote server.
     * 
     * The configurations only setup the LOCAL
     * connection object properties.
     */
    HttpURLConnection connection = (HttpURLConnection) dst.openConnection();
    connection.setDoOutput(true);
    connection.setRequestMethod("POST");
    ...//headers setup
    byte[] testContent = {0x32, 0x32};

    /**
     * This triggers packet exchange with the remote
     * server to create a link. But writing/flushing
     * to a output stream does not send out any data.
     * 
     * Payload are buffered locally.
     */
    try (BufferedOutputStream outputStream = new BufferedOutputStream(connection.getOutputStream())) {
        outputStream.write(testContent);
        outputStream.flush();
    }

    /**
     * Trigger payload sending to the server.
     * Client get ALL responses (including response code,
     * message, and content payload) 
     */
    int responseCode = connection.getResponseCode();
    System.out.println(responseCode);

    /* Here no further exchange happens with remote server, since
     * the input stream content has already been buffered
     * in previous step
     */
    try (InputStream is = connection.getInputStream()) {
        Scanner scanner = new Scanner(is);
        StringBuilder stringBuilder = new StringBuilder();
        while (scanner.hasNextLine()) {
        stringBuilder.append(scanner.nextLine()).append(System.lineSeparator());
        }
    }

    /**
     * Trigger the disconnection from the server.
     */
    String responsemsg = connection.getResponseMessage();
    System.out.println(responsemsg);
    connection.disconnect();

答案 4 :(得分:0)

HTTPURLConnection在哪一点尝试建立与给定URL的连接?

值得澄清的是,有'UrlConnection'实例,然后是基础 Tcp / Ip / SSL套接字连接,2个不同的概念。 “UrlConnection”或“HttpUrlConnection”实例与单个HTTP页面请求同义,并在您调用url.openConnection()时创建。但是如果你从一个'url'实例做多个url.openConnection(),那么如果你很幸运,他们会重复使用相同的Tcp / Ip套接字和SSL握手的东西......如果你是好的话对同一台服务器进行大量的页面请求,尤其是如果你使用SSL,建立套接字的开销非常高。

请参阅: HttpURLConnection implementation