You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Indy使用SSL时如何设置ConnectTimeout与ReadTimeout?

Indy组件SSL模式下ConnectTimeout/ReadTimeout配置失效的解决方案

问题重现

我在使用Indy的TIdHTTP搭配TIdSSLIOHandlerSocketOpenSSL时,遇到了一个诡异的问题:HTTP协议下ConnectTimeoutReadTimeout都能正常按设置值生效,但切换到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,代码运行完全正常

问题根因

经过深入排查,发现两个核心问题:

  1. Linux下的timeval结构体单位容易踩坑:它的tv_sec字段是秒级,但我们常误将毫秒值直接赋值,导致超时被放大1000倍;
  2. 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

火山引擎 最新活动