По какой-то причине HttpURLConnection
, похоже, буферизует загружаемые данные независимо от того, что я пытаюсь. Я могу показать процент выполнения данных, но ясно, что прогресс продвигается слишком быстро, пока данные не передаются с такой высокой скоростью.
Принимающий сервер не находится во внутренней сети, но где-то размещен. Пограничный маршрутизатор сокращает полосу загрузки до 2 Мбит, чтобы имитировать медленную сеть, и на графике пропускной способности маршрутизатора я вижу график скорости передачи данных для устройства разработки. Точка доступа WiFi также позволяет мне видеть график скорости передачи данных, и он выглядит так же, как и граничный маршрутизатор, поэтому ни одно устройство в интрасети не буферизует данные. Это определенно девелоперское устройство (Nexus 5X)
Ниже приводится код, который используется:
HttpURLConnection hucConnection = (HttpURLConnection) url.openConnection();
//hucConnection.setUseCaches(false); // does not solve the issue
//hucConnection.setDefaultUseCaches(false); // does not solve the issue
//hucConnection.setAllowUserInteraction(true); // does not solve the issue
hucConnection.setConnectTimeout(6 * 1000);
hucConnection.setReadTimeout(30 * 1000);
hucConnection.setRequestProperty("content-type", "application/json; charset=UTF-8");
hucConnection.setRequestMethod("POST");
hucConnection.setDoInput(true);
hucConnection.setDoOutput(true);
// Data to transfer
byte[] bData = joTransfer.toString().getBytes("UTF-8");
int iDataLength = bData.length;
//hucConnection.setRequestProperty("content-transfer-encoding", "binary"); // does not solve the issue
// use compression
hucConnection.setRequestProperty("content-encoding", "deflate");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION);
DeflaterOutputStream zip = new DeflaterOutputStream(stream, deflater);
zip.write(bData);
zip.close();
deflater.end();
byte[] bZippedData = stream.toByteArray();
Integer iZippedDataLength = bZippedData.length;
int iChunk = 1000;
hucConnection.setChunkedStreamingMode(iChunk);
//hucConnection.setFixedLengthStreamingMode(iZippedDataLength); // does not solve the issue
hucConnection.connect();
OutputStream osOutputStream = hucConnection.getOutputStream();
// FROM HERE ---->>>
int iUploadedLength;
for (iUploadedLength = 0; iUploadedLength < iZippedDataLength - iChunk; iUploadedLength += iChunk) {
LogWrapper.e(TAG, "l -> f:" + iUploadedLength + " t:" + (iUploadedLength+iChunk));
osOutputStream.write(Arrays.copyOfRange(bZippedData, iUploadedLength , iUploadedLength+iChunk));
osOutputStream.flush();
}
LogWrapper.e(TAG, "r -> f:" + iUploadedLength + " t:" + iZippedDataLength);
osOutputStream.write(Arrays.copyOfRange(bZippedData, iUploadedLength, iZippedDataLength));
osOutputStream.flush();
osOutputStream.close();
// <<<---- TO HERE ---- XXXXXXXXX max 1 second XXXXXXXXX
// FROM HERE ---->>>
int iResponseCode = hucConnection.getResponseCode();
// <<<---- TO HERE ---- XXXXXXXXX about 10 seconds XXXXXXXXX
if (iResponseCode != HttpURLConnection.HTTP_OK) {
...
Я ожидал, что вызовы osOutputStream.flush();
заставят HttpURLConnection
отправить данные на сервер, но по какой-то причине этого не происходит.
Кажется, что где-то буферизуется, потому что после osOutputStream.close();
и до hucConnection.getResponseCode();
данные загружаются на сервер.
Все переводы (загрузка и выгрузка) работают правильно, данные не повреждены.
Есть ли способ исправить это или альтернатива использованию HttpURLConnection? Я читал, что класс Socket
не имеет этой проблемы, но я не уверен, правильно ли он обрабатывает перенаправления и тому подобное. Мне не нужно использовать куки или другие вещи.
Апрокс. 10 секунд, необходимых для завершения hucConnection.getResponseCode();
, - это когда загружено около 3 МБ (3 МБ * 8b / B = 24 МБ, 24 МБ / 2 МБ / с = 12 с), загруженные данные отправляются после этого вызова. Ход загруженных данных является точным.
Возможно ли, что сторонняя библиотека изменяет поведение HttpURLConnection и выполняет прокси? Нравится Firebase или что-то? Я уже отключил Crashlytics, но я думаю, что Firebase также выполняет сбор статистики (время отклика). Я думаю, что у меня были некоторые странные проблемы около 1-2 месяцев назад в другом приложении, где я получал проблему с ошибкой прокси в разрешении доменного имени, как будто что-то внутри Android пропускало сетевой трафик.
Я собираюсь попробовать OkHttp, у одного из получателей есть пример пост-трансляции (https://github.com/square/okhttp/wiki/Recipes)
Обновление: я реализовал это, используя okhttp3
, следуя вышеупомянутому получателю. У меня там точно такая же проблема.
Это на Android 8.1
Сервер является экземпляром nginx.
Я также запустил приложение на экземпляре эмулятора Genymotion, той же ОС, и похоже, что там лучше, но проблема все еще, кажется, присутствует. Хотя радикальное ограничение на граничном маршрутизаторе не влияет на Nexus 5X, оно влияет на эмулятор. Но, тем не менее, даже точность отслеживания загрузки эмулятора оставляет желать лучшего.
Имеет ли смысл использовать для этого соединение WebSocket? Это было бы моим последним средством.