2010-06-28 3 views
3

Что касается сообщения моего (How to retrieve a file from Internet via HTTP?) о том, как легко и безопасно загружать файл из Интернета, я нашел возможное решение - однако он не работает так, как предполагалось Работа.Соединение не время ожидания при загрузке файла из интернета

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

Никто не знает почему?

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. 
} 

Edit: я отключаю интернет, отсоединив кабель или (для беспроводных сетей) от программного обеспечения после применения начнет загрузку (я выбрал, чтобы загрузить большой файл). Он имитирует веб-сайт, отправляющийся в автономный режим.

+1

Разве вы не рады, что вы не усложнили ситуацию, просто используя Indy? –

+0

Просто уточнить. Я не говорю, что Indy плохо! Это слишком много для того, что мне нужно. Я хочу, чтобы иметь возможность взять приложение со мной и скомпилировать его на ЛЮБОЙ Delphi включить компьютер. Портативность - это не плохо, не так ли? – Ampere

+0

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

ответ

3

В документе MS IE имеется документально подтвержденная ошибка. Может быть решена только с помощью кода в потоке и повторного внедрения механизма тайм-аута.

Деталь:

«Это статья показывает обходной путь к API ошибке InternetSetOption по настройке значения тайма-аута пути создания второго потока InternetSetOption не устанавливает таймаут значение»

http://support.microsoft.com/default.aspx?scid=kb;en-us;Q224318
(Ссылка сообщалась Поражение MS not me)

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

+0

У вас есть ссылка на эту документацию? –

+0

Привет, Роб, см. Ссылку, которую я только что добавил. – Ampere

+1

Похож на последнее предложение в моем ответе. Вместо того, чтобы ждать таймаута и вызывать InternetCloseHandle в отдельном потоке, статья KB устанавливает соединение в другом потоке. Пример кода - это почти все вызовы API. Какая часть вас беспокоит? –

0

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

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

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

+0

-1. Не отвечает на вопрос. Кроме того, откуда вы знаете, что этот код не * уже * работает в отдельном потоке? –

+1

Код внутри 'finally' будет выполнен, даже если вы« выходите ». –

+0

@ Крис, я думаю, что выход может быть прекрасным здесь, поскольку OP использует try ... наконец, чтобы закрыть соединения. –

0

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

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

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

+0

«Вы уверены, что не попадете в INTERNET_OPTION_CONNECT_TIMEOUT? Сначала он попытается подключиться, а затем принять». - Совершенно верно! Для этого я сделал несколько тестов. – Ampere

5

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

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

Наиболее перспективным таймаутом является таймаут отключения, но MSDN говорит, что он еще не реализован.

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

+0

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

+1

Право. Если загрузка началась, соединение уже установлено, и ответ уже начался. Таким образом, таймаут соединения больше не имеет значения, и ни один тайм-аут получения. –

+0

Итак, в принципе, в этой библиотеке нет тайм-аута (после начала загрузки). – Ampere

Смежные вопросы