您可以解释HttpURLConnection连接过程吗?


134

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

  • HTTPURLConnection尝试在哪一点建立与给定URL的连接?
  • 我可以在哪一点上知道可以成功建立连接?
  • 建立连接并发送实际请求的步骤/方法调用是否完成?这是什么方法
  • 你能解释的功能getOutputStreamgetInputStream外行的任期?我注意到当我尝试连接的服务器出现故障时,我会收到Exceptionat getOutputStream。这是否意味着HTTPURLConnection仅在我调用时才开始建立连接getOutputStream?怎么样getInputStream?由于我只能在处获得响应getInputStream,那么这是否意味着我尚未发送任何请求,getOutputStream而只是建立了连接?HttpURLConnection调用时,是否返回服务器请求响应getInputStream
  • 我是否正确地说那openConnection只是创建一个新的连接对象,但还没有建立任何连接?
  • 如何测量读取开销和连接开销?

Answers:


184
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连接何时关闭?


你好 谢谢!!!那确实是一个详细的解释,我非常感谢您的回答。如果我正确理解了您的答案,那么如果尚未建立连接,则getOutputStream和getInputStream都会建立连接。如果我调用getOutputStream,然后在内部调用getInputStream,HTTPURLConnection将不再在getInputStream上建立连接,因为我已经能够在getOutStream上建立连接了?HttpURLConnection将重用我能够在getInputStream中的getOutputStream处建立的任何连接。
阿西(Arci)2012年

续:还是为getOutputStream和getInputStream建立新的单独连接?另外,如果我想获得连接开销,那么放置计时器的适当位置是在getOutputStream之前和之后。如果我想获取读取开销,那么放置计时器的适当位置是在getInputStream之前和之后。
Arci'4

记住javadoc关于getInputStream和getOutputStream:Returns an output stream that writes to this connection.和的说法 Returns an input stream that reads from this open connection.。输出流和输入流与连接分开。
jmort253

8
值得注意的是,看起来HttpURLConnection对象仅在需要时才到达目标URL。在您的示例中,您具有输入和输出流,当然,在打开连接之前,这些流什么也不能做。一个更简单的情况是GET操作,在该操作中,除了初始化连接然后检查响应代码之外,您什么都不做。在这种情况下,直到调用getResponseCode()方法才真正建立连接。否则,这是对连接生命周期的很好​​解释和探索!
Spanky Quigman

1
之前,我对'UrlConnection'实例和基础的Tcp / Ip / SSL连接(两个独立的概念)感到困惑。前者基本上是单个HTTP页面请求的同义词。后者是希望仅在对同一服务器执行多个页面请求时才创建一次的东西。
蒂姆·库珀


1

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

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

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

当您调用getInputStream()或getOutputStream()或getResponseCode()时没有获得异常。

建立连接并发送实际请求的步骤/方法调用是否完成?这是什么方法

不,没有。

您能用外行术语解释getOutputStream和getInputStream的功能吗?

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

我注意到当我尝试连接的服务器关闭时,我在getOutputStream处收到异常。这是否意味着HTTPURLConnection仅在调用getOutputStream时才开始建立连接?getInputStream怎么样?由于我只能在getInputStream上获得响应,那么这是否意味着我没有在getOutputStream上发送任何请求,而只是建立了连接?调用getInputStream时,HttpURLConnection是否返回服务器以请求响应?

往上看。

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

是。

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

连接:花费时间getInoutStream()或getOutputStream()返回,以先调用者为准。阅读:从开始第一次阅读到获得EOS的时间。


1
我认为OP意味着建立连接的哪一点以及在什么时候我们可以了解连接状态。不是端口URL连接到。我猜想这是针对openConnection()和getInoutStream()/ getOutputStream()/ getResponseCode()的,答案在后面。
Aniket Thakur

1

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

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

请参阅: HttpURLConnection实现


0

我通过练习捕获了低级别的数据包交换,发现网络连接仅由诸如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();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.