Android (Java) HttpURLConnection отключить повторную попытку в режиме ожидания чтения
Поэтому я использую Google Volley
для HTTP-запроса, который в основном использует Java
HttpURLConnection
.
Согласно моим тестам, проблема заключается в этом:
Когда время ожидания "чтения" на HttpURLConnection
достигает, перед закрытием соединения выполняется молчащий повтор, а соответствующее исключение выбрано (SocketTimeoutException
).
Примечание, что:
- Я заметил эту ошибку при использовании запроса HTTP POST
.
- Тайм-аут "чтения" отличается от тайм-аута "connect".
- Если тайм-аут "read" (установленный вызовом connection.setReadTimeout(int)
) НЕ установлен (0) или установлен на большее значение, чем connection.setConnectTimeout(int)
, эта ошибка не возникает.
- Этот вопрос обсуждался, например, здесь, но я не нашел удовлетворительного решения.
- Несколько сопутствующую проблему можно найти здесь, но я не уверен, что это релевантно (не так ли?)
Дополнительные сведения
Мое приложение используется для оплаты денег, поэтому не требуется повторная попытка запроса (да, я знаю, что он может обрабатываться сервером, я хочу, чтобы мой клиент был "правильным" ) в любом случае).
Когда установлен тайм-аут "read" , в случае установления соединения с сервером, но сервер ожидает /sleeps/delayays -response, время "timeout" перед ответом (таким образом, возникает исключение "read" , а не "connect" ') другой (тихий) запрос отправляется непосредственно перед тем, как это исключение будет поднято, в результате чего будут получены 2 похожих запроса, что неприемлемо.
Какое решение я ищу?
Ну, это тот, который будет хорошо решить эту проблему/ошибку, так же, как исправлено объяснение здесь (но я снова считаю, что это не имеет значения в этом случай).
Кроме того, я хотел бы сохранить исходный поток как есть, что означает не принуждение соединения к закрытию или что-то в этом роде.
То, что я собираюсь сделать на данный момент, устанавливает тайм-аут "read" в два раза "тайм-аут соединения" (они начинают подсчитывать в то же время), чтобы убедиться, что исключение "connection" возникает в первую очередь. Я также попытаюсь решить эту проблему на стороне сервера. Проблема в том, что этот тайм-аут "read" существует по какой-то причине, и моя текущая реализация практически просто игнорирует его и обрабатывает только таймауты "соединения".
ИЗМЕНИТЬ
Библиотека Volley
RetryPolicy
не повлияла на эту проблему, так как это повторение молчания.
Я выглядел как можно глубже в библиотеке. Журналы/точки останова повсюду, отменены вызовы повторной попытки. Это, как я знаю, это проблема 99.99% a HttpURLConnection
.
Ответы
Ответ 1
Это плохое решение было принято разработчиком в 2006 году.
Вот хорошая цитата от кого-то, которая объясняет всю ситуацию с точки зрения java:
"Как вы, наверное, догадались, что теперь это ошибка ( http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6382788). Не механизм повтора, конечно, просто дерьмо Ошибка в том, что это также происходит для POST (который по умолчанию не является идемпотентным для HTTP RFC). Но не беспокойтесь, Билл давно исправил эту ошибку. Билл исправил ее, введя переключатель. Билл узнал о отсталых Билл решил, что лучше оставить переключатель 'on' по умолчанию, потому что это сделает его совместимым с ошибкой. Билл улыбается. Он уже может видеть лица удивленных разработчиков по всему миру. как Билл?" Источник
Ну, предлагаемое решение:
System.setProperty("sun.net.http.retryPost", "false")
Но мы не можем сделать это на android! Единственным оставшимся решением будет следующее:
httpURLConnection.setChunkedStreamingMode(0);
Который, кажется, работает, но не очень эффективен с точки зрения запроса времени.
EDIT:
Я не мог использовать эту реализацию, поэтому я искал альтернативную библиотеку.
Я узнал, что реализация HttpUrlConnection использует OkHttp с Android 4.4. Поскольку OkHttp - это open source, я мог бы искать, есть ли у них проблемы с молчаливыми попытками. И у них были проблемы с ним и исправлено в апреле 2016 года. CommonsWare (настоящий специалист по Android) объясняет, что каждый производитель может решить, какую реализацию они могут использовать. Вследствие этого должно быть много устройств, которые делают молчание в ответах на POST-запросы, и в качестве разработчика мы можем попробовать только некоторые варианты работы.
Моим решением будет изменение библиотеки
EDIT 2: Чтобы дать вам окончательный ответ: Теперь вы можете использовать OkHttp как транспортный уровень для Volley с минимальным кодом.
Еще одно полезное решение
Ответ 2
Ну, прошло много времени, поэтому я решил, что отвечу моим текущим (не идеальным) решением для других людей, чтобы его легко увидеть (также написано внутри вопроса).
Просто установите readTimeout равным 0 (без тайм-аута) или, по крайней мере, значением таймаута соединения. Это позволит убедиться, что таймаут соединения будет поднят до таймаута чтения.
int timeoutMs = request.getTimeoutMs(); // or whatever
connection.setConnectTimeout(timeoutMs); // actual timeout desired
connection.setReadTimeout(timeoutMs); // >= timeoutMs or 0
Конечно, это зависит от использования запроса. В большинстве случаев приложение является частью проекта, содержащего связанный сервер, и вы знаете, что таймауты чтения не должны происходить в любом случае.
Как сказано, на самом деле это не "решение", а очень приятное и полезное решение на 90%.
Ответ 3
Вы можете проверить класс DefaultRetryPolicy волейбола.
Ответ 4
Как говорится в одной из ссылок, которые вы цитировали, вам нужно установить sun.net.http.retryPost
в false
.
Или, не используйте режим фиксированной или помеченной передачи: см. этот отчет об ошибках.