Откуда берется время ожидания сокета 21000 мс?
Эта проблема
Приложение, которое я поддерживаю, продолжает получать время ожидания сокета примерно после 21000 мс, несмотря на то, что я явно установил более длительное время ожидания. Это, казалось бы, волшебное значение 21000 мс появилось в нескольких других SO-вопросах и ответах, и я пытаюсь точно выяснить, откуда оно исходит.
Вот суть моего кода:
HttpURLConnection connection = null;
try {
URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(45000);
connection.setReadTimeout(90000);
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
// code omitted
}
} catch (Exception e) {
// code omitted
}
finally {
if (connection != null) {
connection.disconnect();
}
}
Перехват всех исключений в одном блоке, по общему признанию, не идеален, но это унаследованный код, и я не хочу с ним связываться. Я знаю, что это ловит SocketTimeoutException
через 21000 мс, поскольку в нем регистрируется простое имя класса исключения.
Улики
Я нашел вопрос, где аскер получал ConnectTimeout
через 21000 мс, несмотря на явную настройку 40000 мс. Это интригует, несмотря на то, что класс исключений отличается.
Я также нашел плохо объясненный ответ, в котором утверждается, что сторона сервера отвечает за тайм-аут 21000 мс.
Моя догадка
Я не думаю, что какое-либо действие или бездействие сервера может привести к превышению времени ожидания сокета на клиенте. Но, возможно, стеки TCP в Windows и Android имеют общего предка или, по крайней мере, используют аналогичную логику повторных попыток соединения.
Может ли быть так, что Android устанавливает максимальное время ожидания соединения 21000 мс и устанавливает более длительное время ожидания в HttpURLConnection
бесполезно? Или этот тайм-аут может быть вызван какой-либо машиной Windows на пути между мобильным устройством и сервером? У некоторых версий Android кидают SocketTimeoutException
где другие бросают ConnectException
?
3 ответа
Я написал грубое тестовое приложение, основанное на коде в моем вопросе, которое имитирует тайм-аут соединения, пытаясь соединиться с не маршрутизируемым адресом, как предлагается в этом ответе. На моем Moto G (Android 4.4.2) он выбрасывает SocketTimeoutException
примерно через 45 секунд, как и ожидалось. Любопытно, что если я не установил явное время ожидания подключения, вместо этого ConnectException
примерно через одну минуту.
Я собираюсь написать немного более сложное тестовое приложение и отправить его клиенту, чтобы попытаться определить, устанавливает ли само устройство тайм-аут на 21 с, или виновником может быть какой-либо маршрутизатор в их мобильной сети. Я обновлю этот ответ с результатами.
Результат: похоже, что это ошибка ОС, которая влияет на Samsung SPH-P100 (Galaxy Tab 1) от Sprint. У меня нет доступа к Tab 1 от любого другого оператора, так что в этом можно обвинить Samsung или Sprint. Похоже, что это не влияет на Android 2.x, потому что у меня ZTE X501 под управлением 2.3.6, который позволяет мне устанавливать более длительные тайм-ауты.
Согласно RFC 1122 (ТРАНСПОРТНЫЙ СЛОЙ - TCP), раздел 4.2.3.1 ("Расчет времени ожидания повторной передачи"):
"Реализация также ДОЛЖНА включать экспоненциальный откат для последовательных значений RTO для того же сегмента".
Таким образом, ответ xpa1492 звучит правдоподобно (несмотря на его характер Windows); Реализация стека TCP либо следует этому RFC, либо подвергается панорамированию за невозможность сделать это.
Кстати, RFC 1122 указывает 3 секунды в качестве начального тайм-аута, явно делая ответ xpa1492 (3 + 6 + 12 = 21) звучащим как ответ на вашу загадку.
И да, стек Android TCP имеет общего предка со стеком Windows TCP; оба они были созданы с использованием RFC 1122 в качестве руководства ( "[Стек Linux Linux является] реализацией протокола TCP, определенного в RFC 793, RFC 1122 и RFC 2001 с расширениями NewReno и SACK").
Я подозреваю, что ваша проблема связана с радиопомехами, поэтому вы можете попробовать включить F-RTO, поскольку вы можете многократно нажимать на "магическое число" из-за среды, в которой вы тестируете.
Кажется, это конфигурация Windows по умолчанию...
Исходя из ссылки и дальнейшего прочтения, Windows по умолчанию будет делать 3 попытки и удваивать время ожидания с каждой попыткой, начиная с 3 с. Таким образом, вы получите 3 с + 6 с + 12 с = 21 с.