Indy使用SSL时如何设置ConnectTimeout与ReadTimeout?
Indy组件SSL模式下ConnectTimeout/ReadTimeout配置失效的解决方案
问题重现
我在使用Indy的TIdHTTP搭配TIdSSLIOHandlerSocketOpenSSL时,遇到了一个诡异的问题:HTTP协议下ConnectTimeout和ReadTimeout都能正常按设置值生效,但切换到HTTPS后,超时完全不符合预期——比如明明设了1秒的读超时,实际却等了3分多钟才抛出错误。
先贴出最小可复现的代码:
program mcve; uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF}SysUtils, IdHTTP, IdSSLOpenSSL, DateUtils; var HTTP : TIdHTTP; SSL : TIdSSLIOHandlerSocketOpenSSL; Started : TDateTime; begin HTTP := TIdHTTP.Create(); try HTTP.ReadTimeout := 1000; HTTP.ConnectTimeout := 2000; SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP); SSL.ConnectTimeout := HTTP.ConnectTimeout; SSL.ReadTimeout := HTTP.ReadTimeout; SSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; HTTP.IOHandler := SSL; Started := Now; try HTTP.Get(ParamStr(1)); except On E: Exception do WriteLn(E.Message); end; Writeln(FormatDateTime('hh:nn:ss', SecondsBetween(Started, Now) / SecsPerDay)); finally HTTP.Free; end; end.
异常现象
测试结果对比非常明显:
:~$ ./mcve http://x.x.x.x Read timed out. 00:00:01 ← 符合预期 :~$ ./mcve https://x.x.x.x Socket Error # 0 00:03:38 ← 不符合预期,超时远超设置值
环境差异
这个问题具有明显的平台兼容性差异:
- Linux:Lazarus 2.0.6 + OPM安装的Indy 10.6.2.5494,触发问题
- Windows:Delphi自带Indy 10.6.2.5366,代码运行完全正常
问题根因
经过深入排查,发现两个核心问题:
- Linux下的
timeval结构体单位容易踩坑:它的tv_sec字段是秒级,但我们常误将毫秒值直接赋值,导致超时被放大1000倍; - Indy在Linux环境下的SSL握手阶段,内部超时处理存在兼容性bug,无法正确传递我们设置的超时参数。
修复方案
针对这个问题,我们可以通过给TIdSSLIOHandlerSocketOpenSSL绑定OnBeforeConnect事件,手动直接操作底层套接字来设置超时:
procedure TCustomIdHTTP.OnBeforeConnect(ASender: TIdSSLIOHandlerSocketOpenSSL); var time : timeval; res : Integer; begin // 将毫秒转换为秒,tv_sec是秒单位,tv_usec是微秒单位 time.tv_sec := Round(ASender.ConnectTimeout / 1000); time.tv_usec := 0; // 直接设置套接字的接收超时参数 res := fpsetsockopt(ASender.Binding.Handle, Id_SOL_SOCKET, Id_SO_RCVTIMEO, @time, SizeOf(time)); if res < 0 then raise Exception.Create(SysErrorMessage(GetLastOSError)); end;
使用时,只需要把这个事件绑定到你的SSL IOHandler实例上即可:
SSL.OnBeforeConnect := OnBeforeConnect;
这个方案绕过了Indy在Linux SSL模式下的超时处理bug,直接通过系统调用设置套接字参数,确保超时时间严格符合预期。
内容的提问来源于stack exchange,提问作者RepeatUntil




