Delphi / Indy. Долгая задержка в процедуре TIdStackWindows.Connect

Мое приложение зависает при вызове процедуры TIdStackWindows.Connect. Когда TCP / IP-адрес существует, проблем нет, но если нет, я зависаю. IP-адрес является буквальным, поиск в DNS не выполняется. Я ожидал, что попытка подключения завершится неудачно после тайм-аута (TCPClient.ConnectTimeout), который я установил в 1 секунду, но приложение зависает до 30 секунд при этом вызове (вызов из моего приложения не связан с потоком. Я намерен переместить TCP-соединение к потоку, но длительный тайм-аут соединения по-прежнему будет проблемой).

Если я приостановлю выполнение в среде IDE Delphi, когда приложение не отвечает, я окажусь по адресу:

ntdll.KiUserApcDispatcher:
7C90E450 8D7C2410         lea edi,[esp+$10]

Я затем нажимаю F8 пару раз, пока не увижу фрейм стека. Я тогда нахожусь в:

IdStack.TIdStack.RaiseSocketError(10038)
IdStack.TIdStack.RaiseLastSocketError
IdStack.TIdStack.CheckForSocketError(-1)
IdStackWindows.TIdStackWindows.Connect(912,'10.8.2.170',5001,Id_IPv4)
IdSocketHandle.TIdSocketHandle.Connect
IdIOHandlerStack.TIdConnectThread.Execute
:00451fc1 HookedTThreadExecute + $2D
Classes.ThreadProc($254B910)
System.ThreadWrapper($5456CB0)
:00451ea3 CallThreadProcSafe + $F
:00451f10 ThreadExceptFrame + $3C
:7c80b729 ; C:\WINDOWS\system32\kernel32.dll

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


person rossmcm    schedule 31.10.2011    source источник


Ответы (1)


Блокирующие сокеты не имеют понятия тайм-аута соединения на уровне API, поэтому Indy ConnectTimeout - это тайм-аут, реализованный вручную. Indy вызывает TIdStack.Connect() во внутреннем рабочем потоке, а TIdTCPClient.Connect() запускает цикл ожидания, ожидающий завершения этого потока. Если цикл определяет, что период ConnectTimeout истек, он закрывает сокет, что должно вызвать немедленный выход заблокированного TIdStack.Connect(), но это не гарантия. Также существуют накладные расходы ОС при создании и завершении потока. Определенно не должно потребоваться 30 секунд, чтобы среагировать на тайм-аут в 1 секунду, но, с другой стороны, 1 секунда обычно слишком мала. Возможно, поток даже не запустится в течение 1 секунды. Обычно вы должны установить ConnectTimeout как минимум на 5-10 секунд, чтобы дать ОС достаточно времени для выполнения своей работы.

person Remy Lebeau    schedule 31.10.2011
comment
Спасибо, Реми. У меня есть подозрения, потому что я видел другие комментарии по этой проблеме, в которых упоминается 30 секунд (задержка также находится в пределах 10 мс от 30 секунд - так что, вероятно, где-то есть фрагмент исходного кода с жестко запрограммированной задержкой 30000 мс). Я попробую отложить завтра, но я не надеюсь. - person rossmcm; 31.10.2011
comment
В коде Indy нет жестко запрограммированной 30-секундной задержки (хотя есть жестко запрограммированный 2-минутный тайм-аут, если ConnectTimeout равен 0). См. Реализацию TIdIOHandlerStack.ConnectClient() в IdIOHandlerStack.pas. Цикл сна, который выполняется, пока connect() занят, использует 125 мс (макс.) Сна на итерацию цикла, но это потребует большой задержки со стороны API, чтобы добавить до 30 секунд. Обратите внимание, что если вы вызываете TIdTCPClient.Connect() внутри основного потока и используете компонент TIdAntiFreeze, то дополнительные задержки могут быть вызваны основной очередью сообщений VCL за пределами Indy. - person Remy Lebeau; 31.10.2011
comment
Когда дело дошло до задержки 30000 мс, я имел в виду Windows, а не Indy! :) - person rossmcm; 01.11.2011