2010-08-11 2 views
16

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

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

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

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

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

+0

В одном случае: http://stackoverflow.com/questions/2457538/several-requests-from-one-httpurlconnection?rq=1 в моем случае я получаю слишком много запроса в мой php-файл без какого-либо параметра get/post, где только 1 запрос с параметром get ... Я не знаю, почему, если HttpURLConnection предназначен для отправки одного запроса одновременно, отправка слишком большого количества запросов на сервер ...? – Bhuro

ответ

17

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

Да.

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

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

+1

Я начал искать код src, как только увидел (используя wirehark), что, если я вызвал disconnect(), TCP-соединение все еще использовалось повторно. Существует параллельный хэш-файл для кэширования соединений url, и я вижу, что при создании нового HttpURlConnection, если в пуле есть соединение, он использует его (в конструкторе HttpURLConnection). Но я не вижу, как соединение помещается в кеш-память keepAlive, когда я закрываю потоки (и читаю весь ответ). Я вижу, что соединение возвращается, если запрос является HEAD (вызывается http.finished()), но не для POST. – Cratylus

+0

Вы правы, серверные сокеты объединены в concurrentHashMap, но как только запрос будет готов, сокет будет удален из ClientVector, чтобы новый поток должен был создать новое соединение. Но этот сокет должен возвращаться в пул. Это происходит только в putInKeepAliveCache() ;. Но это не называется 200 OK с телом ответа. Как можно увидеть одно и то же поведение в TCP-соединении с помощью разъединения или нет? – Cratylus

+0

Нет, * Сокеты * объединены. В этом контексте нет ServerSockets. И вы обнаружили, что, возможно, не все из них объединены. – EJP

7

Из Javadoc для HttpURLConnection (курсив мой):

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

+0

Я прочитал это. Мой вопрос заключается в том, как соединение возвращается в буфере для совместного использования другими экземплярами. Я не могу найти код, который возвращает соединение с кешем – Cratylus

+1

Я не уверен, в чем ваша проблема. Вы видите поведение, которое заставляет вас подозревать ошибку в реализации? –

+0

Я начал искать код src, как только увидел (используя wirehark), что, если я вызвал disconnect(), TCP-соединение все еще использовалось повторно. В src я не мог видеть соединение, помещенное обратно в кэш keepAlive, как только я закрыл входной поток первого запроса. Поэтому не было никакой разницы, если бы я вызвал disconnect() или нет. Меня беспокоило, что, возможно, есть какая-то проблема в реализации, если у меня нет чего-то. – Cratylus

4

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

+1

Нет. Я не думаю, что ОС может это сделать. Существует способ принудительного закрытия путем принудительного использования HTTP 1.0 (который не поддерживает постоянные подключения) или с использованием заголовка «Соединение: закрыть», если вы действительно НЕ хотите совместного использования базового TCP-соединения. – StaxMan

+0

@StaxMan: Я знаю о заголовке Connection в HTTP.Мой путаницы было связано с тем, что, хотя я явно закрыл базовый сокет, соединение все еще использовалось повторно. На мой взгляд, поведение, которое я ожидал, было задержкой в ​​2 (макс.) За счет 3-стороннего рукопожатия и HTTP-соединения по новому TCP-соединению. Я этого не видел, и я подумал, что это связано с кэшированием ОС. – Cratylus

+0

Хорошо спасибо за разъяснения. – StaxMan

2

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

  • силы HTTP 1.0 (1.1 введенные постоянные соединения) - это, как указано в строке запроса HTTP
  • Отправлять заголовок «Соединение» со значением «закрыть»; это также закроет закрытие.
+0

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

+0

Хм. Хороший вопрос - я предположил, что есть способ, но я не мог найти его с быстрым поиском. Поэтому я бы использовал второе решение; это лучше в любом случае, и для этого также работает. – StaxMan

4

Как вы "сила использование HTTP1.0" с использованием HttpURLConnection в JDK?

По поддержке секции „Persistent Connections” of the Java 1.5 guide для HTTP1.1 соединений может быть включен или выключен с помощью свойства Java http.keepAlive (по умолчанию это правда). Кроме того, свойство java http.maxConnections указывает максимальное количество (одновременных) соединений для каждого пункта назначения, которое будет поддерживаться в любой момент времени.

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

0

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

+0

Вам не нужно читать поток в EOS или поток ошибок. Тебе просто нужно закрыть. Смотрите свою собственную цитату. Закрытие буферизованного считывателя закрывает поток, поэтому вам тоже этого не нужно. -1 – EJP