FTP 使用多个套接字连接,一个用于命令/响应,每个传输使用单独的连接。
造成套接字错误的最可能原因是中间存在 FTP 无法识别的代理/路由器/防火墙TIdFTP
并且 FTP 服务器在短时间不活动后将关闭命令连接。在此期间Unpack()
(或手动暂停),命令连接上没有传输命令/响应,它处于空闲状态,因此可能会因此类代理/路由器/防火墙上的超时而关闭。
在传输期间,命令连接处于空闲状态,不会在其上传输任何 FTP 命令/响应(除非您中止传输),直到传输完成。在此期间,不知道 FTP 的代理/路由器/防火墙可能会提前关闭命令连接。
为了避免这种情况,TIdFTP
has a NATKeepAlive
属性,可以在命令连接闲置时启用 TCP 保持活动。这通常可以防止过早关闭。
但是,当没有正在进行的传输时,TIdFTP
在以下情况下禁用命令连接上的 TCP keepalive:NATKeepAlive.UseKeepAlive
是真的。TIdFTP
仅在传输期间使用 TCP 保持活动,并假设您不会在 FTP 命令之间执行长时间延迟。如果需要延迟一段时间,要么关闭FTP连接,要么定期发送FTP命令(例如调用TIdFTP.Noop()
在计时器/线程中)。
或者,您可以尝试在连接到服务器后手动启用 TCP keep-alives,并设置NATKeepAlive.UseKeepAlive
为假所以TIdFTP
每次传输后不会自动禁用保持活动,例如:
function Connect2FTP(FTP: TIdFTP; RemoteFolder: string; Log: TRichLog): Boolean;
begin
Result := FTP.Connected;
if not Result then
begin { We are not already connected }
FTP.Host := MyFTP;
FTP.Username:= usr;
FTP.Password:= psw;
try
FTP.Connect;
try
FTP.ChangeDir(RemoteFolder);
// send a TCP keep-alive every 5 seconds after being idle for 10 seconds
FTP.NATKeepAlive.UseKeepAlive := False; // False by default, but just in case...
FTP.Socket.Binding.SetKeepAliveValues(True, 10000, 5000);
except
FTP.Disconnect(False);
raise;
end;
except
Exit;
end;
Result := True;
end;
end;
请注意,虽然大多数平台支持在每个连接的基础上启用 TCP 保持活动(保持活动是 TCP 规范的一部分),但设置保持活动间隔是特定于平台的。目前,Indy 支持将间隔设置为:
- Windows 2000+ 和 WinCE 4.x+,适用于 Win32 和 .NET
- Linux,当 Indy 在 Kylix 中使用时
- Unix/Linux 和 NetBSD,当 Indy 在 FreePascal 中使用时。
否则,将使用操作系统默认间隔,对于这种情况,该间隔的值可能太大也可能不会太大,具体取决于操作系统配置。
目前,除了 Windows 之外,Delphi FireMonkey 中的间隔不可自定义。
如果特定平台支持设置自定义 TCP keep-alive 间隔,但 Indy 没有实现它们SetKeepAliveValues()
, 您可以使用TIdFTP.Socket.Binding.SetSockOpt()
而不是根据需要手动设置值。很多平台都支持TCP_KEEPIDLE
/TCP_KEEPINTVL
或等效的套接字选项。