InternetOpenUrl возвращается только после полной загрузки ответа HTTP - PullRequest
5 голосов
/ 24 апреля 2011

Я пишу утилиту загрузки файлов, используя 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.

1 Ответ

12 голосов
/ 24 апреля 2011

Я знаю, что, вероятно, не вежливо отвечать на ваш собственный вопрос, но я считаю, что выяснил, в чем проблема.

После перезагрузки (и много, много, много минут потрачено впустую на автоматических обновлениях) я попытался снова и столкнулся с той же проблемой, но я принял решение от комментариев Алекса К. и ДжейДжея, предлагающих это Это не ожидаемое поведение, и он начал исследовать работающее на компьютере программное обеспечение, которое может создавать помехи.

После того, как многие приложения были закрыты, и многие службы были отключены, я наткнулся на одну службу, которая, я действительно надеялся, не даст такого эффекта, однако это имело место.

Я выключил «Агент Лаборатории Касперского», и привет, InternetOpenUrl возвратился через 2 секунды после начала загрузки HTTP-ответа. Я бы предпочел немедленно, но вторая или две из 75-секундной загрузки, по крайней мере, дают WinINET время для обработки заголовков и выполнения любой предварительной обработки, которая может понадобиться.

Также оказалось, что, если я не читаю данные из InternetReadFile (), загрузка никогда не завершается (как это видно через Чарльза), подразумевая (надеюсь), что InternetReadFile () действительно является оберткой для вызова recv () (как я и ожидал).

Последовательное повторное включение и отключение службы сетевого агента подтвердило этот вывод. Я хотел бы как-то окончательно доказать (или опровергнуть) это.

Получается, что мой (читай: отдел информационной безопасности) выбор антивируса и его защиты от перехвата на уровне сети, похоже, был причиной проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...