При загрузке файла из интернета соединение не прерывается - PullRequest
3 голосов
/ 28 июня 2010

В связи с моим сообщением ( Как получить файл из Интернета через HTTP? ) о том, как легко и надежно загрузить файл из Интернета, я нашел возможное решение - однако не работаеткак это должно было работать.

В соответствии с документацией MS, срок действия приведенного ниже кода истекает через 500 мс после того, как я отключился от Интернета.Тем не менее, похоже, что он полностью игнорирует параметр INTERNET_OPTION_RECEIVE_TIMEOUT.Приложение зависает во время загрузки.Для этой функции требуется около 20-30, чтобы понять, что там интернет-соединение не работает, и вернуть управление графическому интерфейсу.

Кто-нибудь знает почему?

function GetBinFileHTTP (const aUrl: string; const pStream: TStream; wTimeOut: Word= 500; wSleep: Word= 500; wAttempts: Word= 10): Integer;
CONST
  BufferSize = 1024;
VAR
  hSession, hService: HINTERNET;
  Buffer     : array[0..BufferSize-1] of Char;
  dwBytesRead, dwBytesAvail: DWORD;
  lSucc        : LongBool;
  lRetries, dwTimeOut: Integer;   
begin
 Result:= 0;
 if NOT IsConnectedToInternet then
  begin
   Result:= -1;
   EXIT;
  end;

 hSession := InternetOpen(PChar(ExtractFileName(Application.ExeName)), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);  { The INTERNET_OPEN_TYPE_PRECONFIG flag specifies that if the user has configured Internet Explorer to use a proxy server, WinInet will use it as well. }
 if NOT Assigned(hSession) then
  begin
   Result:= -4;
   EXIT;
  end;

 TRY
   hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, INTERNET_FLAG_RELOAD, 0);
   if NOT Assigned(hService) then Exit;
   TRY
     FillChar(Buffer, SizeOf(Buffer), 0);

     { Set time out }
     dwTimeOut:= wTimeOut;
     InternetSetOption(hService, INTERNET_OPTION_RECEIVE_TIMEOUT, @dwTimeOut, SizeOf(dwTimeOut));   { use INTERNET_FLAG_RELOAD instead of NIL to redownload the file instead of using the cache }


     InternetSetOption(hService, INTERNET_OPTION_CONNECT_TIMEOUT, @dwTimeOut, SizeOf(dwTimeOut));

     REPEAT
       lRetries := 0;

       REPEAT
         lSucc:= InternetQueryDataAvailable( hService, dwBytesAvail, 0, 0);
         if NOT lSucc
         then Sleep( wSleep );
         if lRetries > wAttempts
         then Result:= -2;
       UNTIL lSucc OR (Result= -2);

       if NOT InternetReadFile(hService, @Buffer, BufferSize, dwBytesRead) then
        begin
          Result:= -3;                                                          { Error: File not found/File cannot be downloaded }
          EXIT;
        end;

       if dwBytesRead = 0
       then Break;

       pStream.WriteBuffer(Buffer[0], dwBytesRead);

     UNTIL False;
   FINALLY
     InternetCloseHandle(hService);
   end;
 FINALLY
   InternetCloseHandle(hSession);
 end;

 Result:= 1;
end;

ВотДокументация:

{

INTERNET_OPTION_CONNECT_TIMEOUT         Sets or retrieves an unsigned long integer value that contains the time-out value to use for Internet connection requests. If a connection request takes longer than this time-out value, the request is canceled. When attempting to connect to multiple IP addresses for a single host (a multihome host), the timeout limit is cumulative for all of the IP addresses. This option can be used on any HINTERNET handle, including a NULL handle. It is used by InternetQueryOption  and InternetSetOption.
INTERNET_OPTION_RECEIVE_TIMEOUT         Sets or retrieves an unsigned long integer value that contains the time-out value to receive a response to a request.      If the response takes longer than this time-out value, the request is canceled. This option can be used on any HINTERNET handle, including a NULL handle. It is used by InternetQueryOption and InternetSetOption. For using WinInet synchronously, only the default value for this flag can be changed by calling InternetSetOption and passing NULL in the hInternet parameter.
                  INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT - Identical to INTERNET_OPTION_RECEIVE_TIMEOUT. This is used by InternetQueryOption and InternetSetOption.
}

Редактировать: я отключаю Интернет, отсоединив кабель или (для беспроводной связи) от программного обеспечения ПОСЛЕ того, как приложение начнет загрузку (я решил загрузить большой файл).Он имитирует отключение веб-сайта.

Ответы [ 4 ]

5 голосов
/ 28 июня 2010

Тайм-аут соединения явно не применим в вашем тесте, потому что к моменту запуска теста (т. Е. Выдернуть вилку) соединение уже установлено. Действительно, соединение уже установлено, прежде чем вы сможете приступить к настройке тайм-аута.

Подлинность тайм-аута приема также подозрительна, потому что вы уже начали получать ответ.

Наиболее многообещающим является время ожидания отключения, но MSDN сообщает, что оно еще не реализовано.

Мне кажется, что нужно использовать асинхронные операции. Используйте InternetReadFileEx и используйте флаги irf_Async и irf_No_Wait. Если прошло слишком много времени без получения каких-либо данных, закройте соединение. Другой вариант - придерживаться синхронных вызовов, но затем вызвать InternetCloseHandle из другого потока, если загрузка занимает слишком много времени.

3 голосов
/ 29 июня 2010

Есть документированная ошибка в коде MS IE.Решается только с помощью кода в потоке и повторной реализации механизма тайм-аута.

Сведения:

"В этой статье показан обходной путь к ошибке API InternetSetOption при установке значений времени ожидания путем создания второго потока. InternetSetOption не устанавливает значения времени ожидания"

http://support.microsoft.com/default.aspx?scid=kb;en-us;Q224318
(Ссылка была нарушена. Виноват MS, а не я)

Может быть, кто-то может помочь с реализацией этого исправления ошибки также в Delphi.Лично у меня нет опыта работы с C. Даже основа в псевдопаскале будет хорошей.

0 голосов
/ 28 июня 2010

Вы уверены, что не нажимаете на INTERNET_OPTION_CONNECT_TIMEOUT?Сначала он попытается подключиться, а затем получить.

Чтобы проверить время ожидания подключения, он должен разрешиться, но никогда не подключаться.Чтобы проверить тайм-аут чтения, он должен подключиться, но никогда не получать никаких данных.

Обычно я устанавливаю тайм-аут подключения на 10 секунд, а тайм-аут чтения на 30 секунд.Что-нибудь более длинное, я считаю все равно.

0 голосов
/ 28 июня 2010

IMO, вы должны запустить это в потоке.Потоки не должны означать цикл - это может быть поток «один и готово».Запустите его таким образом, и ваш графический интерфейс останется отзывчивым, пока поток не завершится.Я понимаю, что это на самом деле не отвечает на ваш вопрос, но сделает ваш код лучше.

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

Наконец, я не думаю, что вы должны использовать EXIT, когда у вас есть ручки и вещи открыты.Перерыв вместо этого, так что вы все еще пробегаете разъединения.Я ожидаю, что ваш код свяжет сокет.Я видел это недавно во время обзора кода, когда имел место EXIT intead BREAK, и это вызывает утечку памяти, потому что объекты создаются и никогда не освобождаются.Я бы использовал здесь то же правило.

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