У меня есть веб-сканер Java. Я заметил, что для небольшого количества серверов, которые я сканирую, у меня остается большое количество УСТАНОВЛЕННЫХ сокетов:
joel@bohr:~/tmp/test$ lsof -p 6760 | grep TCP
java 6760 joel 105u IPv6 96546 0t0 TCP bohr:55602->174.143.223.193:www (ESTABLISHED)
java 6760 joel 109u IPv6 96574 0t0 TCP bohr:55623->174.143.223.193:www (ESTABLISHED)
java 6760 joel 110u IPv6 96622 0t0 TCP bohr:55644->174.143.223.193:www (ESTABLISHED)
java 6760 joel 111u IPv6 96674 0t0 TCP bohr:55665->174.143.223.193:www (ESTABLISHED)
На одном сервере может быть много десятков таких, и я не могу понять, почему они остаются открытыми.
Я использую HttpURLConnection
для установления соединения и чтения данных. HTTP 1.1 и keep-alive
включены (по умолчанию). Насколько я понимаю, базовый сокет tcp для удаленного сервера будет повторно использоваться Java HttpURLConnection
, пока я закрываю поток ввода / ошибок и все данные считываются из потока. Я также понимаю, что если выдается исключение, то, пока поток ввода / ошибок закрыт (если не нулевой), сокет, хотя и не используется повторно, будет закрыт. ( Java-обработка http-keepalive )
Мой сокращенный код выглядит так:
InputStream is = null;
try {
HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(10000);
conn.setRequestProperty("User-Agent", userAgent);
conn.setRequestProperty("Accept", "text/html,text/xml,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
conn.setRequestProperty("Accept-Encoding", "gzip deflate");
conn.setRequestProperty("Accept-Language", "en-gb,en;q=0.5");
conn.connect();
try {
int responseCode = conn.getResponseCode();
is = conn.getInputStream();
} catch (IOException e) {
is = conn.getErrorStream();
if (is != null){
// consume the error stream, http://download.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html
StreamUtils.readStreamToBytes(is, -1 , MAX_LN);
}
throw e;
}
String type = conn.getContentType();
byte[] response = StreamUtils.readStream(is);
// do something with content
} catch (Exception e) {
conn.disconnect(); // don't try to re-use socket - just be done with it.
throw e;
} finally {
if (is != null) {
is.close();
}
}
Я заметил, что для сайта, где это происходит, я получаю много исключений IOException при выполнении запросов GET из-за:
java.net.ProtocolException: Server redirected too many times (20)
Я почти уверен, что справляюсь с этим, правильно закрывая сокет. Неужели это действительно так, или я что-то не так делаю? Может ли это быть результатом неправильного использования keep-alive - и если да, то как это исправить? Я бы предпочел не включать выключение поддержки, чтобы решить проблему.
РЕДАКТИРОВАТЬ : я проверил настройку следующего свойства:
conn.setRequestProperty("Connection", "close"); // supposed to disable keep-alive
При отправке заголовка Connection: close
отключены постоянные tcp-соединения и все сокеты в итоге очищаются. Таким образом, может показаться, что проблема, которую я вижу, действительно связана с keep-alive
и сокетами, которые закрываются неправильно, даже после закрытия входного потока.
EDIT2 - может ли быть так, что один сокет создается при каждом перенаправлении запроса? Там, где эта проблема заметна, запрос перенаправляется 20 раз, прежде чем выдается исключение, указанное выше. Если бы это было так, есть ли способ ограничения количества перенаправлений на URLConnection?