Indy FTP,大文件和NAT路由器

Lob*_*uno 7 delphi ftp indy

我多年来一直使用Indy通过FTP传输文件,但未能找到满意的解决方案来解决以下问题.

当用户在路由器后面上传大文件时,有时会发生以下情况:文件上传正常,但同时命令通道因超时而断开连接.通常,直接连接到服务器不会发生这种情况,因为服务器"知道"正在数据通道上进行传输.但是有些路由器并未意识到这一点,并且命令通道已关闭.

许多程序定期发送NOOP命令以保持命令通道处于活动状态,即使这不是标准FTP规范的一部分.我的问题:我该怎么做?我是否在OnWork事件中发送NOOP命令?这是否会以某种方式造成任何附带损害,例如,我是否需要处理某些响应?我该如何最好地解决这个问题?

Zoë*_*son 5

我们使用多种方法来处理此问题:(1) 在传输期间在控制通道上启用TCP/IP 保持活动,(2) 在连接断开后正常恢复,(3) 支持恢复中断的传输。

许多 FTP 客户端会在一切空闲时发送 NOOP,但我不知道是否有任何在数据传输期间发送 NOOP,因为在这种情况下您需要处理响应,并且许多服务器直到数据传输完毕。

  1. Indy 10.5.8 (Delphi XE2) 本身支持 TCP/IP 保持活动。只需使用 TIdFTP 的NATKeepAlive属性即可。

    对于以前的版本,分配 OnDataChannelCreate/OnDataChannelDestroy 事件:

    const
      KeepAliveIdle = 2 * SecsPerMin;
      KeepAliveInterval = 2 * SecsPerMin;
      IOC_VENDOR = $18000000;
      SIO_KEEPALIVE_VALS = DWORD(IOC_IN or IOC_VENDOR or 4);
    
    type
      tcp_keepalive = record
        onoff: u_long;
        keepalivetime: u_long;
        keepaliveinterval: u_long;
      end;
    
    procedure TFtpConnection.DataChannelCreated(Sender: TObject;
      ADataChannel: TIdTCPConnection);
    var
      Socket: TIdSocketHandle;
      ka: tcp_keepalive;
      Bytes: DWORD;
    begin
      // Enable/disable TCP/IP keepalives.  They're very small (40-byte) packages
      // and will be sent every KeepAliveInterval seconds after the connection has
      // been idle for KeepAliveIdle seconds.  In Win9x/NT4 the idle and timeout
      // values are system wide and have to be set in the registry;  the default is
      // idle = 2 hours, interval = 1 second.
      Socket := (FIdFTP.IOHandler as TIdIOHandlerSocket).Binding;
      if Win32MajorVersion >= 5 then begin
        ka.onoff := 1;
        ka.keepalivetime := KeepAliveIdle * MSecsPerSec;
        ka.keepaliveinterval := KeepAliveInterval * MSecsPerSec;
        WSAIoctl(Socket.Handle, SIO_KEEPALIVE_VALS, @ka, SizeOf(ka), nil, 0, @Bytes,
          nil, nil)
      end
      else
        Socket.SetSockOpt(Id_SOL_SOCKET, Id_SO_KEEPALIVE, Id_SO_True)
    end;
    
    procedure TFtpConnection.DataChannelDestroy(ASender: TObject;
      ADataChannel: TIdTCPConnection);
    var
      Socket: TIdSocketHandle;
    begin
      Socket := (FIdFTP.IOHandler as TIdIOHandlerSocket).Binding;
      Socket.SetSockOpt(Id_SOL_SOCKET, Id_SO_KEEPALIVE, Id_SO_False)
    end;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果文件已成功传输,要正常恢复,只需在最后重新连接并执行SIZELIST即可获取文件大小。如果它们匹配,则文件已成功传输,您无需执行任何其他操作。如果服务器支持,您还可以发送XCRC命令来获取CRC值,以便您可以将其与本地文件进行比较。

  3. 如果你想真正强大,你也可以检查TIdFTP.CanResume. 如果设置了,服务器支持REST命令,因此您可以通过传递给的参数来true从上次中断的地方继续传输。AResumeTIdFTP.Get/Put