Безопасное использование HttpURLConnection
При использовании HttpURLConnection нужно ли закрывать InputStream, если мы не "получаем" и не используем его?
то есть. это безопасно?
HttpURLConnection conn = (HttpURLConnection) uri.getURI().toURL().openConnection();
conn.connect();
// check for content type I don't care about
if (conn.getContentType.equals("image/gif") return;
// get stream and read from it
InputStream is = conn.getInputStream();
try {
// read from is
} finally {
is.close();
}
Во-вторых, безопасно ли закрыть InputStream , прежде чем все его содержимое будет полностью прочитано?
Есть ли риск оставить базовый сокет в состоянии ESTABLISHED или даже CLOSE_WAIT?
Ответы
Ответ 1
безопасно закрыть InputStream до того, как все это было прочитайте
Вам необходимо прочитать все данные во входном потоке перед тем, как закрыть его, чтобы кэшировалось базовое TCP-соединение. Я прочитал, что это не должно требоваться в последней Java, но всегда было поручено прочитать весь ответ для повторного использования соединения.
Отметьте это сообщение: keep-alive в java6
Ответ 2
Согласно http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html и исходному коду OpenJDK.
(Когда keepAlive == true
)
Если клиент вызвал HttpURLConnection.getInputSteam(). close()
HttpURLConnection.getInputSteam(). close()
, последующий вызов HttpURLConnection. disconnect()
HttpURLConnection. disconnect()
НЕ закроет Socket
. т.е. Socket
используется повторно (кэшируется)
Если клиент не вызывает close()
, вызовет disconnect()
закроет InputStream
и закроет Socket
.
Таким образом, чтобы повторно использовать Socket
, просто вызовите InputStream. close()
InputStream. close()
. Не звоните HttpURLConnection. disconnect()
HttpURLConnection. disconnect()
.
Ответ 3
Ниже приведена информация о кеше keep-alive. Вся эта информация относится к Java 6, но, вероятно, также верна для многих предыдущих и более поздних версий.
Из того, что я могу сказать, код сводится к следующему:
- Если удаленный сервер отправляет заголовок "Keep-Alive" со значением "timeout", которое может быть проанализировано как положительное целое число, это количество секунд используется для таймаута.
- Если удаленный сервер отправляет заголовок "Keep-Alive", но у него нет значения "timeout", которое может быть проанализировано как положительное целое число и "usingProxy", это правда, тогда таймаут составляет 60 секунд.
- Во всех остальных случаях тайм-аут составляет 5 секунд.
Эта логика разделяется между двумя местами: вокруг строки 725 sun.net.www.http.HttpClient (в методе "parseHTTPHeader" ) и вокруг строки 120 солнце. net.www.http.KeepAliveCache (в методе "put" ).
Итак, есть два способа управления периодом ожидания:
- Управляйте удаленным сервером и настраивайте его для отправки заголовка Keep-Alive с соответствующим полем тайм-аута
- Измените исходный код JDK и создайте свой собственный.
Можно было бы подумать, что можно было бы изменить кажущийся произвольный пятисекундный дефолт без перекомпиляции внутренних классов JDK, но это не так. A bug был подан в 2005 году, запрашивая эту возможность, но Sun отказалась предоставить его.
Ответ 4
Если вы действительно хотите убедиться, что соединение близко, вы должны позвонить conn.disconnect()
.
Открытые подключения, которые вы наблюдали, связаны с тем, что соединение HTTP 1.1 поддерживает функцию продолжения (также известную как HTTP Persistent Connections).
Если сервер поддерживает HTTP 1.1 и не отправляет Connection: close
в заголовок ответа, Java не сразу закрывает подстилающее TCP-соединение при закрытии входного потока. Вместо этого он держит его открытым и пытается повторно использовать его для следующего HTTP-запроса на тот же сервер.
Если вы вообще не хотите этого поведения, вы можете установить для системного свойства http.keepAlive
значение false:
System.setProperty("http.keepAlive","false");
Ответ 5
Вы также должны закрыть поток ошибок в случае сбоя HTTP-запроса (что угодно, кроме 200):
try {
...
}
catch (IOException e) {
connection.getErrorStream().close();
}
Если вы этого не сделаете, все запросы, которые не возвращают 200 (например, тайм-аут), будут пропускать один сокет.
Ответ 6
При использовании HttpURLConnection нужно ли закрывать InputStream, если мы не "получаем" и не используем его?
Да, он всегда должен быть закрыт.
то есть. это безопасно?
Не 100%, вы рискуете получить NPE. Безопаснее:
InputStream is = null;
try {
is = conn.getInputStream()
// read from is
} finally {
if (is != null) {
is.close();
}
}