Delphi:INDY组件IDFTP/IDHTTP的connecttimeout超时问题

http://www.piaoyi.org/computer/Delphi-INDY-IDFTP-IDHTTP-connecttimeout.html

正 文:

    DELPHI中,indy组件是一套性能还不错的网络套件,使用这个组件里的Tidftp、Tidhttp等功能可以十分方便地进行网络交互。但正是由于indy的功能太多了,封装了上百种的协议,也同时产生了不少的兼容问题,有些错误甚至需要重写indy底层协议才能解决。

    飘易最近使用delphi开发了一个软件,使用indy的TIDftp组件,由于indy是阻塞式的通讯方式,所以在网络出现异常或服务器失去响应的情况下,TIDFTP的connect方法就会卡死大约20秒的时间。

    具体的原始代码是(INDY 10版本):

procedure TForm1.Button1Click(Sender: TObject);
var
   IdFTP1:  TIDFTP;
   cn: boolean;
begin
IdFTP1 := TIdFTP.create(nil);
IdFTP1.UserName := 'PIAOYI';
IdFTP1.PassWord := 'PIAOYI';
IdFTP1.HostName := 'WWW.PIAOYI.ORG';
IdFTP1.Port := 21;
IdFTP1.ConnctTimeout := 1000; //注意,这里的超时时间并不一定起作用,单位毫秒
cn:=IdFTP1.connect;
if cn then
form1.memo1.Lines.add('FTP打开成功!');
if not cn then
form1.memo1.Lines.add('FTP打开失败!');   
IdFTP1.free;
end;

    上面 IdFTP1.ConnctTimeout 设置不一定起作用,经过 飘易 测试,只有当网络正常并且服务器正常响应的情况下,ConnctTimeout方法才起作用,但是当网络断开时或服务器失去响应或服务器并未开放21端口时,IdFTP1.connect 这个语句就会一直卡死 20秒钟,而这个20s对于批量操作来说是太长了。用try except捕获错误是“SocketError #10054 Connection reset by peer”。原因是由于indy的tidftp是阻塞式通讯,也就意味了 IdFTP1.connect 不完成,整个线程都会一直等待下去,直到超时。

     解决方法是不使用indy组件,而使用同样功能强大的异步通讯的ICS组件下的 TFtpClient 即可。

Uses OverbyteIcsWndControl, OverbyteIcsFtpCli;
procedure TForm1.Button2Click(Sender: TObject);
var
   FtpClient1:  TFTPClient;
   cn: boolean;
begin
FtpClient1 := TFTPClient.create(nil);
FtpClient1.UserName := 'piaoyi';
FtpClient1.PassWord := 'piaoyi';
FtpClient1.HostName := 'www.piaoyi.org';
FtpClient1.Port := '21';
FtpClient1.Timeout := 2;  //单位秒
cn:=FtpClient1.connect;
if cn then
form1.memo1.Lines.add('FTP扫描成功!');
if not cn then
form1.memo1.Lines.add('FTP扫描失败!');   
FtpClient1.free;
end;

    ICS下的 TFtpClient 组件的 FtpClient1.Timeout 是起作用的,即使在网络断开或服务器失去响应的情况下, timeout 方法一样起作用。Internet Component Suite就是大名鼎鼎的ICS,免费提供ICS-SSL,提供SSL支持,支持代理,非堵塞式通讯。
    官方网站: http://www.overbyte.be
    最新版本:[ Download ICS-V7 Distribution (Sep 12, 2009) ], Support all Delphi 7-2010 and BCB version 2006-2010, ICS-SSL included for free. 
    直接下载地址: http://www.overbyte.be/arch/OverbyteIcsV7.zip

     ICS的安装方法
下载完成后解压到delphi的 lib目录(注, 飘易 使用的是delphi2007)!
1、在Tools >  Options > Delphi Options > library win32 > library path 里加入 $(BDS)\Lib\OverbyteIcsV7\Delphi\Vc32 目录。
2、打开 lib\OverbyteIcsV7\Delphi\Vc32 下的 OverbyteIcsD2007Design.dproj 文件。(文件名在其它Delphi版本略有不同)
3、在项目管理器中,右键 OverbyteIcsD2007Design.dproj 选择Build和Install,就安装完成了!

    PS:2010-12-11,经过悦然大悟朋友的提示,飘易发现 ReadTimeout 的确起了作用,新的测试数据在这里:《解决INDY组件IDHTTP超时问题》!

5 悦然大悟
2010-12-11 11:46:52
嘿嘿,哪儿有那么麻烦了,不需要修改代码。indy这么多年了,这种低级错误在indy10里面处理的差不多了。
只要设置
   IdFTP1.ReadTimeout:=3000;
   IdFTP1.ConnectTimeout:=3000;
这就OK了。
IdIOHandlerStack 里的 WaitFor 是受ReadTimeout限制的,不要去掉,加上 IdFTP1.ReadTimeout:=3000;
就成了。
----------------------------------------------------------

----------------------------------------------------------

解决INDY组件IDHTTP超时问题

Author:飘易 Source: 飘易
Categories: Delphi编程 PostTime:2010-12-11 17:13:04
正 文:
    飘易在以前的博文里曾经提起过INDY组件IDHTTP超时问题,链接:《 INDY组件IDFTP/IDHTTP的connecttimeout超时问题》,在那篇文字里,我采用了ICS的组件代替INDY组件的方法解决这个超时问题。今天,我看到那篇文章下面的一位网友留言:

“嘿嘿,哪儿有那么麻烦了,不需要修改代码。indy这么多年了,这种低级错误在indy10里面处理的差不多了。
只要设置
   IdFTP1.ReadTimeout:=3000;
   IdFTP1.ConnectTimeout:=3000;
这就OK了。
IdIOHandlerStack 里的 WaitFor 是受ReadTimeout限制的,不要去掉,加上 IdFTP1.ReadTimeout:=3000;
就成了。”

    经过飘易的测试,果然,当加上 ReadTimeout 的属性时,我要的延时时间的效果就出来了。虽然超时的时间和我预期的有所差别,但这个INDY 10 版本的超时时间真的起了作用。详细的分析看我的测试,我的测试代码如下:

procedure TForm1.Button1Click(Sender: TObject);
var
t1,t2:int64;
str1:string;
IdHTTP1:TIdHTTP;
begin
try
t1:=GetTickCount; //毫秒级
IdHTTP1 := TIdHTTP.create(nil);
IdHTTP1.ReadTimeout:= strtoint(edit2.Text)*1000;
IdHTTP1.ConnectTimeout:= strtoint(edit3.Text)*1000;
str1:=IdHTTP1.Get(edit1.Text);
IdHTTP1.Free;
t2:=GetTickCount;
memo1.Clear ;
memo1.Lines.add(inttostr(t2-t1));
memo1.Lines.add(str1);
except
t2:=GetTickCount;
memo1.Clear ;
memo1.Lines.add(inttostr(t2-t1));
memo1.Lines.add('err:'+DateTimeToStr(now()));
end;
end;

    我对这样的网址 www.qq.org、www.youtube.com 进行了测试,当我没有加上 IdHTTP1.ReadTimeout 这个属性时,访问以上2个网址的超时时间均是 20秒左右,无论 IdHTTP1.ConnectTimeout 设置多少。而当我加上 IdHTTP1.ReadTimeout 属性时,超时时间立即体现了出来。

ReadTimeout设为1000ms时,实际超时时间为3125ms
ReadTimeout设为2000ms时,实际超时时间为6125ms
ReadTimeout设为3000ms时,实际超时时间为9125ms
ReadTimeout设为4000ms时,实际超时时间为12125ms
ReadTimeout设为5000ms时,实际超时时间为15125ms
ReadTimeout设为6000ms时,实际超时时间为18125ms
ReadTimeout设为7000ms时,实际超时时间为20969ms
ReadTimeout设为8000ms时,实际超时时间为20953ms
ReadTimeout设为9000ms时,实际超时时间为20953ms

    从以上的实际测验数据来看,飘易发现, 当 ReadTimeout 设置值小于7秒时,实际超时时间是 ReadTimeout的值的3倍(即=ReadTimeout*3);当 ReadTimeout 设置值大于7s时,实际超时时间维持在20s左右,这是因为indy10组件的默认最大超时时间即20S。实际超时时间和 ConnectTimeout 并无关系。

    飘易猜测,ConnectTimeout只有在网络正常的情况下才有效,而当网络不正常时,ReadTimeout才真正的起作用,即IdIOHandlerStack 里的 WaitFor 是受ReadTimeout限制的,因此,这2个属性应该结合实用,感谢“悦然大悟”。






你可能感兴趣的:(Delphi:INDY组件IDFTP/IDHTTP的connecttimeout超时问题)