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

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

  • HTTPURLConnection在哪一点上尝试建立到给定 URL 的连接?
  • 在哪一点上我可以知道我能够成功地建立一个连接?
  • 建立连接和发送实际请求是在一个步骤/方法调用中完成的吗? 它是什么方法?
  • 你能用通俗的语言解释一下 getOutputStreamgetInputStream的功能吗?我注意到,当我试图连接到的服务器关闭时,我会得到 getOutputStream处的 Exception。这是否意味着 HTTPURLConnection只有在我调用 getOutputStream时才会开始建立连接?那 getInputStream呢?因为我只能在 getInputStream处获得响应,那么这是否意味着我还没有在 getOutputStream处发送任何请求,而只是建立了一个连接?当我调用 getInputStream时,HttpURLConnection是否回到服务器请求响应?
  • 我是否可以说 openConnection只是创建了一个新的连接对象,但是还没有建立任何连接?
  • 如何测量读开销和连接开销?
119422 次浏览
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) {
// ...
}

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

来自 GetOutputStream:

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

基本上,我认为你们已经很好地理解了它是如何工作的,所以让我用外行的术语重申一下。getOutputStream基本上打开一个 联系流,目的是将数据写入服务器。在上面的代码示例中,“ message”可以是我们发送给服务器的一条注释,它表示在帖子中留下的一条注释。当您看到 getOutputStream时,您正在打开 联系流以进行写操作,但是在调用 writer.write("message=" + message);之前您实际上不会写任何数据。

来自 GetInputStream ():

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

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

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

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

用外行人的话来说,发出 getInputStream请求就相当于打电话到你朋友家说: “嘿,我可以过来借一下那副虎钳吗?”你的朋友握手的方式是说: “当然!来拿吧”。然后,在这一点上,连接建立,你走到你的朋友的房子,敲门,要求虎钳,并走回你的房子。

使用类似的例子为 getOutputStream将涉及到打电话给你的朋友,并说: “嘿,我有我欠你的钱,我可以寄给你吗?”?你的朋友需要钱,你把钱藏了这么长时间,他生病了,他说: “当然,过来吧,你这个小气鬼。”。所以你走到你朋友家,把钱“邮寄”给他。然后他把你赶出去,你走回家。

现在,继续使用外行人的例子,让我们看看一些例外情况。如果你打电话给你的朋友,他不在家,这可能是一个500错误。如果因为你的朋友厌倦了你一直借钱,你打电话给他,却收到了一条断开的电话号码信息,那就是404页没有找到。如果你的手机因为你没有付账单而关机,这可能是一个 IOException 异常。(注意: 本节可能不是100% 正确。它的目的是给你一个大概的概念,什么正在发生在门外汉的术语。)

问题五:

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

openConnection创建一个新的连接对象:

每次通过调用此 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 连接何时关闭?

Tim Bray 提供了一个简明的步骤,说明 openConnection ()并不建立实际的连接。实际上,在调用 getInputStream ()或 getOutputStream ()等方法之前,不会建立实际的 HTTP 连接。

Http://www.tbray.org/ongoing/when/201x/2012/01/17/httpurlconnection

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

在 URL 中命名的端口(如果有的话)上,否则 HTTP 为80,HTTPS 为443。我相信这是有记录的。

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

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

建立连接和发送实际请求是在一个步骤/方法调用中完成的吗? 它是什么方法?

没有也没有。

你能用通俗的语言解释一下 getOutputStream()getInputStream()的功能吗?

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

我注意到,当我试图连接到的服务器关闭时,在 getOutputStream()处会出现一个异常。这是否意味着 HTTPURLConnection只有在我调用 getOutputStream()时才会开始建立连接?那 getInputStream()呢?因为我只能在 getInputStream()处获得响应,那么这是否意味着我还没有在 getOutputStream()处发送任何请求,而只是建立了一个连接?当我调用 getInputStream()时,HttpURLConnection是否回到服务器请求响应?

看上面。

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

是的。

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

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

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

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

参见: HttpURLConnection 实现

我完成了捕获低级数据包交换的练习,发现只有诸如 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();