Я пишу утилиту загрузки файлов, используя WinINET, и заметил (особенно при больших загрузках), что вызов WinINET InternetOpenUrl()
возвращается только после того, как весь HTTP-ответ был загружен.
Я подтвердил этоиспользуя прокси-инструмент Charles, а также WireShark, и заметил, что загрузка полностью завершается, и только тогда WinINET уведомляет мой код.
Некоторый упрощенный (синхронный) код:
hInt = InternetOpen(USER_AGENT_NAME, INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, 0);
DWORD dwRequestFlags = INTERNET_FLAG_NO_UI // no UI please
|INTERNET_FLAG_NO_AUTH // don't authenticate
|INTERNET_FLAG_PRAGMA_NOCACHE // do not try the cache or proxy
|INTERNET_FLAG_NO_CACHE_WRITE; // don't add this to the IE cache
hUrl = InternetOpenUrl(hInt, szURL, NULL, 0, dwRequestFlags, NULL);
if (hUrl)
{
// <only gets here after entire download is complete>
InternetCloseHandle(hUrl);
}
InternetCloseHandle(hInt);
Документация предполагает , что это отправляет запрос и обрабатывает заголовки ответа (не завершает загрузку), а затем ожидается, что вы пройдете через цикл InternetReadFile()
, пока он не вернет TRUE
иdwNumberOfBytesRead
равно 0.
От MSDN
Функция InternetOpenUrl : Функция InternetOpenUrl анализирует строку URL, устанавливаетсоединение с сервером, и подготавливает для загрузки данных, идентифицированных URL-адресом.Затем приложение может использовать InternetReadFile [...] для получения данных URL.
Функция InternetReadFile : Чтобы обеспечить получение всех данных, приложение должно продолжатьвызывайте функцию InternetReadFile до тех пор, пока функция не вернет TRUE, а параметр lpdwNumberOfBytesRead не станет равным нулю.
Я пробовал это также с использованием асинхронного метода и заметил то же самое.В частности, INTERNET_STATUS_RESPONSE_RECEIVED
отправляется зарегистрированному методу обратного вызова только после завершения загрузки.Это означает, что мой клиент может начать доступ к данным только после завершения загрузки.
Аналогичным образом я реализовал версию, в которой также используется библиотека WinHttp, и заметил точно такие же результаты.
Это усложняет ситуацию, когда дело доходит до тайм-аутов.Если загрузка превышает время ожидания (по умолчанию 30 секунд), * 1042 * завершится неудачно.
Итак, у меня есть два вопроса:
Если это ожидаемое поведение библиотек WinInet и WinHttp, почему в документации предлагается циклически выполнять вызов InternetReadFile()
, почему бы просто не прочитать всебуфер (после того, как все WinINET уже имеет)?
Я понимаю, предоставляя возможность, так как вы не всегда хотите выделять 150 МБ порций памяти, но оправдание заключается в том, что вы не знаете, сколькоданные доступны ... но WinINET уже завершил загрузку.
И зачем делать так, чтобы он выглядел замечательно, как метод recv()
, свернутый, если это просто абстракция над временным файлом или файлом вкеш IE (или, что еще хуже, потраченный впустую блок памяти)?
И на что нужно установить продолжительность тайм-аута?Если я никогда не узнаю, насколько велики данные до истечения времени ожидания, то как мне решить, на что установить значение времени ожидания?
Является ли это ожидаемым поведением, и если да, есть ли способ получить данные во время потоковой передачи?
При медленном соединении или с большим файлом, это очень возможно.что над данными может быть проделана большая работа, прежде чем завершится полная загрузка.В классической повторной реализации HTTP сокета Berkley цикл по вызову recv()
предоставил бы мне данные по мере их поступления, что в конечном итоге мне и нужно.
Да, я мог бы переписать реализациюиспользуя простые сокеты, но я бы предпочел не тратить время на поддержку всей спецификации HTTP и шифрования SSL, не говоря уже о поддержке прокси в WinINET.