Можете ли вы объяснить процесс подключения HttpURLConnection?

Я использую HTTPURLConnection для подключения к веб-службе. Я знаю, как использовать HTTPURLConnection, но я хочу понять, как это работает. В принципе, я хочу знать следующее:

  • В какой момент HTTPURLConnection попытайтесь установить соединение с данным URL?
  • В какой момент я могу знать, что мне удалось успешно установить соединение?
  • Установили соединение и отправили фактический запрос, сделанный при вызове одного шага/метода? Какой метод?
  • Можете ли вы объяснить функцию getOutputStream и getInputStream в неспециалистом? Я замечаю, что когда сервер, к которому я пытаюсь подключиться, не работает, я получаю Exception в getOutputStream. Означает ли это, что HTTPURLConnection начнет устанавливать соединение только при вызове getOutputStream? Как насчет getInputStream? Поскольку я могу получить ответ только в getInputStream, значит, я еще не отправил запрос в getOutputStream, но просто установил соединение? Do HTTPURLConnection вернуться на сервер, чтобы запросить ответ, когда я вызываю getInputStream?
  • Можно ли сказать, что openConnection просто создает новый объект соединения, но пока не устанавливает соединение?
  • Как я могу измерить накладные расходы на чтение и соединить служебные данные?

Ответ 1

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) {
    // ...
}

Первые 3 ответа на ваши вопросы перечислены в виде встроенных комментариев, помимо каждого метода, в примере HTTP POST выше.

От getOutputStream:

Возвращает выходной поток, который записывается в это соединение.

В принципе, я думаю, вы хорошо понимаете, как это работает, поэтому позвольте мне просто повторить в непрофессиональных условиях. getOutputStream в основном открывает поток , с целью записи данных на сервер. В приведенном выше примере кода "сообщение" может быть комментарием, который мы отправляем на сервер, который представляет комментарий, оставленный в сообщении. Когда вы видите getOutputStream, вы открываете поток для записи, но вы фактически не записываете никаких данных, пока не назовете writer.write("message=" + message);.

От getInputStream():

Возвращает поток ввода, который читается из этого открытого соединения. Исключение SocketTimeoutException может быть выбрано при чтении из возвращаемого потока ввода, если истечет время ожидания чтения до того, как данные доступны для чтения.

getInputStream делает обратное. Как и getOutputStream, он также открывает поток connection, но целью является считывание данных с сервера, а не запись на него. Если соединение или открытие потока не удастся, вы увидите SocketTimeoutException.

Как получить getInputStream? Поскольку я могу получить ответ только в getInputStream, значит, я еще не отправил запрос в getOutputStream, но просто установил соединение?

Имейте в виду, что отправка запроса и отправка данных - это две разные операции. Когда вы вызываете getOutputStream или getInputStream url.openConnection(), вы отправляете запрос на сервер для установления соединения. Существует рукопожатие, которое происходит там, где сервер отправляет вам подтверждение, что соединение установлено. Именно в этот момент вы готовы отправить или получить данные. Таким образом, вам не нужно вызывать getOutputStream для установить соединение, чтобы открыть поток, если только ваша цель для отправки запроса - отправить данные.

В условиях неспециалиста запрос getInputStream является эквивалентом звонка в дом вашего друга, чтобы сказать: "Эй, хорошо, если я приеду и возьму эту пару тисков?" и ваш друг устанавливает рукопожатие, говоря: "Конечно, приходите и получите". Затем, в этот момент, соединение сделано, вы ходите в свой дом друзей, стучите в дверь, просите тиски и возвращаетесь в свой дом.

Использование аналогичного примера для getOutputStream предполагает привлечение вашего друга и высказывание "Эй, у меня есть те деньги, которые я вам должен, могу ли я отправить вам"? Твой друг, нуждающийся в деньгах и больных, которые ты так долго хранил, говорит: "Конечно, перейди к тебе, дешевый ублюдок". Итак, вы ходите в дом своего друга и "ПОЧТАЙТЕ" деньги ему. Затем он вышвырнет вас, и вы вернетесь в свой дом.

Теперь, продолжая пример непрофессионала, давайте посмотрим на некоторые Исключения. Если вы позвонили своему другу и его не было дома, это может быть ошибка 500. Если вы позвонили и получили сообщение с отключенным номером, потому что ваш друг устал от того, что вы все время заимствуете деньги, что 404 страницы не найдены. Если ваш телефон мертв, потому что вы не оплатили счет, это может быть исключение IOException. (ПРИМЕЧАНИЕ. Этот раздел может быть не на 100% правильным. Он предназначен для того, чтобы дать вам общее представление о том, что происходит в условиях непрофессионала.)

Вопрос № 5:

Да, вы правы, что openConnection просто создает новый объект подключения, но не устанавливает его. Соединение устанавливается при вызове getInputStream или getOutputStream.

openConnection создает новый объект подключения. Из URL.openConnection javadocs:

Новое соединение открывается каждый раз, вызывая метод openConnection обработчика протокола для этого URL.

Соединение устанавливается при вызове 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 закрывается?.

Ответ 2

Тим Брей представил краткий шаг за шагом, заявив, что openConnection() не устанавливает фактическое соединение. Скорее, фактическое HTTP-соединение не установлено до тех пор, пока вы не вызовете методы, такие как getInputStream() или getOutputStream().

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

Ответ 3

В какой момент HTTPURLConnection пытается установить соединение с данным URL?

В порт, указанный в URL-адресе, если он есть, иначе 80 для HTTP и 443 для HTTPS. Я считаю, что это документировано.

В какой момент я могу знать, что мне удалось успешно установить соединение?

Когда вы вызываете getInputStream() или getOutputStream() или getResponseCode() без получения исключения.

Установили соединение и отправили фактический запрос, сделанный при вызове одного шага/метода? Какой метод?

Нет и нет.

Можете ли вы объяснить функцию getOutputStream и getInputStream в непрофессиональном плане?

Любой из них сначала подключается, если необходимо, затем возвращает требуемый поток.

Я замечаю, что когда сервер, к которому я пытаюсь подключиться, отключается, я получаю исключение в getOutputStream. Означает ли это, что HTTPURLConnection начнет устанавливать соединение только при вызове getOutputStream? Как насчет getInputStream? Поскольку я могу получить ответ только в getInputStream, значит, я еще не отправил запрос в getOutputStream, но просто установил соединение? HttpURLConnection вернется на сервер, чтобы запросить ответ, когда я вызываю getInputStream?

См. выше.

Можно ли сказать, что openConnection просто создает новый объект соединения, но пока не устанавливает соединение?

Да.

Как я могу измерить накладные расходы на чтение и соединить служебные данные?

Подключитесь: возьмите время getInoutStream() или getOutputStream(), чтобы вернуться, в зависимости от того, что вы назовете первым. Читайте: время от начала первого чтения до получения EOS.

Ответ 4

В какой момент HTTPURLConnection пытается установить соединение с данным URL?

Стоит прояснить, есть ли экземпляр "UrlConnection", а затем базовое соединение сокетов Tcp/Ip/SSL, 2 разных понятия. Экземпляр "UrlConnection" или "HttpUrlConnection" является синонимом одного запроса на HTTP-страницу и создается при вызове url.openConnection(). Но если вы делаете несколько url.openConnection() из одного экземпляра "url", то, если вам повезет, они будут повторно использовать один и тот же сокет Tcp/Ip и файлы для связи SSL... это хорошо, если вы делаете много запросов страниц на тот же сервер, особенно хорошо, если вы используете SSL, где накладные расходы на установку сокета очень высоки.

См: HttpURLConnection реализация