Indy错误10038"非套接字上的套接字操作"经过61秒不活动

时间:2016-08-11 08:17:20

标签: delphi ftp indy delphi-xe7 indy10

我想从FTP服务器下载一些大文件(GB)。 第一个文件的下载始终有效。然后当我试图得到第二个文件时:

  

"套接字错误#10038。非套接字上的套接字操作。"

错误已开启' Get'。在' Get'我看到这些消息(通过FTP状态事件):

   Starting FTP transfer
   Disconnecting.
   Disconnected.

代码是这样的:

{pseudo-code}
for 1 to AllFiles do 
 begin 
   if Connect2FTP then 
    begin
     FTP.Get(Name, GzFile, TRUE, FALSE);  <--- Socket operation on non-socket" error (I also get EIdConnClosedGracefully 'Connection Closed Gracefully' in IDE, F9 will resume execution without problems, but this is OK)
     Unpack(GzFile); <--- this takes more than 60 seconds
    end;
 end;
if FTP.Connected
then FTP.Disconnect;

-

function Connect2FTP(FTP: TIdFTP; RemoteFolder: string; Log: TRichLog): Boolean;      
begin
 Result:= FTP.Connected;
 if NOT Result then
  begin         { We are already connected }
   FTP.Host    := MyFTP;
   FTP.Username:= usr;
   FTP.Password:= psw;

   TRY
    FTP.Connect;
   EXCEPT
    on E: Exception DO

   Result:= FTP.Connected;
   if Result then FTP.ChangeDir(RemoteFolder); 
  end;
end;

此处的完整代码:http://pastebin.com/RScj86R8(PAS)或此处https://ufile.io/26b54(ZIP)

我认为在调用&#39; Unpack&#39;这需要几分钟。

更新:已确认:调用&#39;解包&#39;后出现问题。我取消了电话,一切都很好。暂停(睡眠或断点)下载程序一段时间(我认为超过60秒)会产生同样的问题。

enter image description here

1 个答案:

答案 0 :(得分:2)

FTP使用多个套接字连接,一个用于命令/响应,每个传输使用单独的连接。

套接字错误的最可能原因是位于TIdFTP之间的FTP不知情的代理/路由器/防火墙,并且FTP服务器在短暂不活动后关闭命令连接。在Unpack()(或手动暂停)期间,没有命令/响应在命令连接上传输,它处于空闲状态,因此在这样的代理/路由器/防火墙上被超时关闭。 / p>

在传输过程中,命令连接处于空闲状态,没有传输FTP命令/响应(除非您中止传输),直到传输完成。 FTP-unaware代理/路由器/防火墙可能会在此期间过早关闭命令连接。

为了避免这种情况,TIdFTP具有NATKeepAlive属性,可以在命令连接空闲时启用TCP保持活动。这通常可以防止过早关闭。

但是,当prgress中没有传输时,如果TIdFTP为True,NATKeepAlive.UseKeepAlive将禁用命令连接上的TCP保持活动。 TIdFTP仅在传输期间使用TCP保持活动,并假设您不会在FTP命令之间执行长时间延迟。如果您需要延迟一段时间,请关闭FTP连接,或定期发送FTP命令(例如在计时器/线程中调用TIdFTP.Noop())。

或者,您可以尝试在连接到服务器后手动启用TCP保持活动,并将NATKeepAlive.UseKeepAlive设置为False,以便TIdFTP在每次转移后不会自动禁用保持活动,例如:< / p>

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,当在Kylix中使用Indy时
  • Unix / Linux和NetBSD,当在FreePascal中使用Indy时。

否则,将使用操作系统默认时间间隔,根据操作系统配置,可能会或可能不会对此情况的值过大。

此时,除了Windows之外,Delphi FireMonkey中的间隔不可自定义。

如果特定平台支持设置自定义TCP保持活动间隔,但Indy未在SetKeepAliveValues()中实现它们,则可以使用TIdFTP.Socket.Binding.SetSockOpt()代替手动设置值。许多平台都支持TCP_KEEPIDLE / TCP_KEEPINTVL或等效的套接字选项。