HttpURLConnection реализация - PullRequest
       34

HttpURLConnection реализация

17 голосов
/ 11 августа 2010

Я прочитал, что 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-соединению (проверено с помощью wireshark), но я не могу понять, почему (хотя это то, что я хочу), так как я назвал отключение. Я проверил исходный код для HttpURLConnection, и реализация поддерживает постоянный кеш подключений к тем же адресатам. Моя проблема в том, что я не могу видеть, как соединение помещается обратно в кеш после отправки первого запроса. Разъединение закрывает соединение и без разъединения все равно не вижу, как соединение снова помещается в кеш. Я видел, что в кеше есть метод run для прохождения всех незанятых соединений (я не уверен, как он вызывается), но я не могу найти, как соединение помещается обратно в кеш. Единственное место, которое, по-видимому, происходит, это готовый метод httpClient, но он не вызывается для POST с ответом. Может ли кто-нибудь помочь мне в этом?

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

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

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

Ответы [ 6 ]

17 голосов
/ 12 августа 2010

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

Да.

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

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

7 голосов
/ 11 августа 2010

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

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

4 голосов
/ 13 мая 2011

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

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

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

4 голосов
/ 12 августа 2010

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

2 голосов
/ 20 октября 2010

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

Отправка заголовка 'Connection' со значением 'close';это также приведет к закрытию.
0 голосов
/ 14 ноября 2012

Отказ от потоков приведет к простоям 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

...