143

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

  • 在哪一点上HTTPURLConnection尝试建立到给定 URL 的连接?
  • 在哪一点上我可以知道我能够成功建立连接?
  • 是否在一步/方法调用中建立连接并发送实际请求?它是什么方法?
  • 你能通俗地解释一下getOutputStreamand的功能getInputStream吗?我注意到当我尝试连接的服务器关闭时,我得到一个Exceptionat getOutputStream。这是否意味着HTTPURLConnection只有在我调用时才会开始建立连接getOutputStream?怎么样getInputStream?既然我只能在 得到响应getInputStream,那么这是否意味着我还没有发送任何请求getOutputStream而只是建立了连接?调用时是否HttpURLConnection返回服务器请求响应getInputStream
  • 我是否正确地说openConnection只是创建了一个新的连接对象但还没有建立任何连接?
  • 如何测量读取开销和连接开销?
4

5 回答 5

196
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基本上打开一个连接流,目的是向服务器写入数据。在上面的代码示例中,“消息”可以是我们发送到服务器的评论,代表在帖子上留下的评论。当您看到 时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) );

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

有关关闭连接的信息(您没有询问),请参阅在 Java 中 URL 连接何时关闭?.

于 2012-04-12T04:09:35.810 回答
18

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

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

于 2013-07-15T23:35:57.690 回答
3

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

在 URL 中指定的端口上(如果有),否则为 HTTP 80 和 HTTPS 443。我相信这是有据可查的。

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

当你打电话getInputStream()getOutputStream()没有getResponseCode()得到例外。

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

没有也没有。

你能通俗地解释一下getOutputStream()and的功能getInputStream()吗?

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

我注意到当我尝试连接的服务器关闭时,我在getOutputStream(). 这是否意味着HTTPURLConnection只有在我调用时才会开始建立连接getOutputStream()?怎么样getInputStream()?既然我只能在 得到响应getInputStream(),那么这是否意味着我还没有发送任何请求getOutputStream()而只是建立了连接?调用时是否HttpURLConnection返回服务器请求响应getInputStream()

往上看。

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

是的。

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

连接:花时间getInputStream()getOutputStream()返回,无论您先打电话。读取:从开始第一次读取到获得 EOS 的时间。

于 2013-07-15T23:45:02.380 回答
1

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

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

请参阅: HttpURLConnection 实现

于 2013-09-04T00:55:48.227 回答
1

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

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

在此处输入图像描述

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

    /* 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();
于 2020-02-22T04:40:59.373 回答