Вы можете получить практически такой же эффект в локальной сети с http-прокси. Прокси-сервер загружает весь файл медленно по глобальной сети, а затем быстро представляет файл на ПК через локальную сеть. В моем случае задержка до получения первых данных составила 20 секунд, а затем весь файл прибывает почти мгновенно.
у утилиту загрузки файлов, используя WinINET, и заметил (особенно при больших загрузках), что WinINETInternetOpenUrl()
Вызов возвращается только после того, как весь 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 секунд)InternetOpenUrl()
выходит из строя.
Итак, у меня есть два вопроса:
Если это ожидаемое поведение библиотек WinInet и WinHttp, почему в документации предлагается циклическиInternetReadFile()
вызов, почему бы просто не прочитать весь буфер (ведь WinINET уже есть)?Я понимаю, предоставляя возможность, так как вы не всегда хотите выделять 150 МБ порций памяти, но оправдание заключается в том, что вы не знаете, сколько данных доступно ... но WinINET уже завершил загрузку.
И зачем так удивительно выглядетьrecv()
метод обернут, если это просто абстракция над временным файлом или файлом в кеше IE (или, что еще хуже, потраченный впустую блок памяти)?
И что я должен установить продолжительность тайм-аута? Если я никогда не узнаю, насколько велики данные до истечения времени ожидания, то как мне решить, на что установить значение времени ожидания?
Является ли это ожидаемым поведением, и если да, то есть ли способ получить данные в процессе их передачи?При медленном соединении или с большим файлом вполне возможно, что над данными может быть проделана большая работа до завершения всей загрузки. В классической реализации HTTP-сокета Беркли, повторяяrecv()
call предоставит мне данные по мере их поступления, что в конечном итоге мне и нужно.
Да, я мог бы переписать реализацию, используя простые сокеты, но я бы предпочел не тратить время на поддержку всей спецификации HTTP и шифрования SSL, не говоря уже о поддержке прокси в WinINET.