Откуда берется время ожидания сокета 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 по умолчанию...

https://social.technet.microsoft.com/Forums/windows/en-US/9e7f59dd-6469-4ade-91ca-ceb5bcaf2675/windows-7-tcp-parameter-tcpmaxconnectretransmissions-and-tcpinitialrtt?forum=w7itpronetworking

Исходя из ссылки и дальнейшего прочтения, Windows по умолчанию будет делать 3 попытки и удваивать время ожидания с каждой попыткой, начиная с 3 с. Таким образом, вы получите 3 с + 6 с + 12 с = 21 с.

Другие вопросы по тегам