Внедрение HttpURLConnection

Я читал, что HttpURLConnection поддерживает постоянные соединения, так что соединение может быть повторно использовано для нескольких запросов. Я попробовал это, и единственный способ отправить второй POST - это вызов openConnection во второй раз. В противном случае я получил исключение IllegalStateException ( "Уже подключено" ); Я использовал следующее:

try{
URL url = new URL("http://someconection.com");
}
catch(Exception e){}
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//set output, input etc
//send POST
//Receive response
//Read whole response
//close input stream
con.disconnect();//have also tested commenting this out
con = (HttpURLConnection) url.openConnection();
//Send new POST

Второй запрос отправляется по тому же TCP-соединению (проверено с помощью wirehark), но я не могу понять, почему (хотя это то, что я хочу), так как я вызвал disconnect. Я проверил исходный код для HttpURLConnection, и реализация сохраняет кэш-память keepalive подключений к тем же адресам. Моя проблема в том, что я не вижу, как соединение помещается обратно в кеш после того, как я пришлю первый запрос. Отключение закрывает соединение и без разъединения, но я не вижу, как соединение помещается обратно в кеш. Я видел, что в кеше есть метод запуска для прохождения через все незанятые соединения (я не уверен, как это называется), но я не могу найти, как соединение помещается обратно в кеш. Единственное место, которое, похоже, происходит в готовом методе httpClient, но это не вызвано POST с ответом. Может ли кто-нибудь помочь мне в этом?

ИЗМЕНИТЬ Я заинтересован в том, что правильная обработка объекта HttpUrlConnection для повторного использования соединения tcp. Если поток ввода/вывода должен быть закрыт, а затем url.openConnection(); каждый раз, чтобы отправить новый запрос (избегая отключения())? Если да, я не вижу, как соединение повторно используется, когда я вызываю url.openConnection() во второй раз, поскольку соединение было удалено из кеша для первого запроса и не может найти способ его возврата. Возможно ли, что соединение не возвращается обратно в кэш keepalive (ошибка?), Но ОС еще не освободила соединение tcp и при новом подключении, ОС возвращает буферное соединение (еще не выпущенное) или что-то подобное? EDIT2 Единственное, что я нашел, - это JDK_KeepAlive

... когда приложение вызывает close() на InputStream, возвращаемом URLConnection.getInputStream(), Обработчик протокола JDK HTTP попытается для очистки соединения, и если успешно, установите соединение в кэш подключений для повторного использования в будущем HTTP-запросы.

Но я не уверен, какой это обработчик. sun.net.www.protocol.http.Handler не делает никакого кэширования, поскольку я видел Спасибо!

Ответ 1

Если поток ввода/вывода будет закрыт а затем url.openConnection(); каждый раз, чтобы отправить новый запрос (избегая отключения())?

Да.

Если да, я не вижу, как это соединение повторно использовать, когда я звоню url.openConnection() для второго времени, поскольку соединение было удалено из кеша для первого запросить и не можете найти, как это вернулся.

Вы путаете HttpURLConnection с базовым Socket и его базовым TCP-соединением. Они не то же самое. Примерами HttpURLConnection являются GC'd, базовый Socket объединяется, если вы не вызываете disconnect().

Ответ 2

Из javadoc для HttpURLConnection (мой акцент):

Каждый экземпляр HttpURLConnection используется для создания одного запроса, но базовое сетевое подключение к HTTP-сервер может быть прозрачным разделяемых другими экземплярами. Вызов close() на InputStream или OutputStream HttpURLConnection после запроса может быть свободная сеть ресурсы, связанные с этим экземпляр, но не влияет на совместное постоянное соединение. призвание метод disconnect() может закрыть базовый сокет, если постоянный в противном случае соединение время.

Ответ 3

Я обнаружил, что соединение действительно кэшируется, когда InputStream закрыт. Как только входной поток был закрыт, базовое соединение буферизуется. Объект HttpURLConnection непригоден для дополнительных запросов, хотя, поскольку объект считается еще "подключенным", то есть его логическое соединение установлено в true и не очищается после того, как соединение помещено обратно в буфер. Поэтому каждый раз, когда новый HttpUrlConnection должен быть создан для нового POST, но базовое TCP-соединение будет использоваться повторно, если оно не было отключено. Поэтому ответ EJP был правильным описанием. Может быть, поведение, которое я видел, (повторное использование TCP-соединения), несмотря на явно вызывающий disconnect(), было связано с кэшированием, выполняемым ОС? Я не знаю. Надеюсь, кто-то, кто знает, может объяснить. Спасибо.

Ответ 4

Как вы "принудительно используете HTTP1.0", используя HttpUrlConnection JDK?

В соответствии с разделом "Persistent Connections" руководства Java 1.5 поддержка HTTP1.1 соединений может быть отключена или включена с помощью java Свойство http.keepAlive (по умолчанию - true). Кроме того, свойство java http.maxConnections указывает максимальное количество (одновременных) соединений для каждого адресата, которое будет поддерживаться в любой момент времени.

Следовательно, "принудительное использование HTTP1.0" может быть применено для всего приложения сразу, установив для свойства java http.keepAlive значение false.

Ответ 5

Hmmh. Возможно, мне что-то не хватает (так как это старый вопрос), но, насколько мне известно, есть два известных способа принудительного закрытия базового TCP-соединения:

  • Принудительное использование HTTP 1.0 (1.1 введенные постоянные соединения) - это, как указано в строке запроса http
  • Отправлять заголовок 'Connection' со значением 'close'; это также закроет закрытие.

Ответ 6

Отказ от потоков приведет к простоям TCP-соединений. Поток ответа должен быть прочитан полностью. Еще одна вещь, которую я забыл изначально, и в большинстве ответов на эту тему упускал из виду, забывает иметь дело с потоком ошибок в случае исключений. Код, аналогичный этому исправленному одному из моих приложений, который не освобождал ресурсы должным образом:

HttpURLConnection connection = (HttpURLConnection)new URL(uri).openConnection();
InputStream stream = null;
BufferedReader reader = null;
try {
        stream = connection.getInputStream();
        reader = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));

        // do work on part of the input stream

} catch (IOException e) {

    // read the error stream
    InputStream es = connection.getErrorStream();
    if (es != null) {
        BufferedReader esReader = null;
        esReader = new BufferedReader(new InputStreamReader(es, Charset.forName("UTF-8")));
        while (esReader.ready() && esReader.readLine() != null) {
        }
        if (esReader != null)
            esReader.close();
    }

    // do something with the IOException
} finally {

    // finish reading the input stream if it was not read completely in the try block, then close
    if (reader != null) {
        while (reader.readLine() != null) {
        }
        reader.close();
    }

    // Not sure if this is necessary, closing the buffered reader may close the input stream?
    if (stream != null) {
        stream.close();
    }

    // disconnect
    if (connection != null) {
        connection.disconnect();
    }
}

Буферизованный читатель не является строго необходимым, я выбрал его, потому что в моем случае использования требовалось чтение по одной строке за раз.

Смотрите также: http://docs.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html