【问题标题】:Indy FTP, large files and NAT routersIndy FTP、大文件和 NAT 路由器
【发布时间】:2010-04-19 07:28:59
【问题描述】:

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

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

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

【问题讨论】:

    标签: delphi ftp indy


    【解决方案1】:

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

    很多 FTP 客户端会在一切空闲时发送 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;
      
    2. 若要在文件已成功传输后正常恢复,只需在最后重新连接并执行 SIZELIST 以获取文件大小。如果它们匹配,则文件已成功传输,您无需执行任何其他操作。如果服务器支持,也可以发送XCRC命令获取CRC值,与本地文件对比。

    3. 如果你想真正健壮,也可以查看TIdFTP.CanResume。如果设置了服务器支持 REST 命令,那么您可以通过将true 传递给TIdFTP.Get/PutAResume 参数来从中断的地方继续传输。

      李>

    【讨论】:

    • 你好克雷格!这似乎是一个很好的解决方案。不过,我有一些问题。最初的问题是 command 频道被关闭了。如果我正确理解您的代码,您正在将 keepalive 分配给数据通道?
    • 这段代码确实会影响命令通道(FIdFTP是TIdFTP对象,FIdFTP.IOHandler代表命令通道)。它在 DataChannelCreate/Destroy 中的原因是因为我只在数据通道打开时启用它。您可以在 Connect 之后立即执行此操作,但如果您不发送命令(如 NOOP),服务器仍会在空闲时断开您的连接,这样只会导致不必要的网络流量。
    • 只是另一个问题。在 DataChannelDestroy 中,您只需在 9x/NT 的情况下禁用 KeepAlive,但对于较新的操作系统则不然。 WSAIoctl 不应该再次与 ka.onoff=0 一起使用吗? Socket.SetSockOpt(Id_SOL_SOCKET, Id_SO_KEEPALIVE, Id_SO_False) 是否也适用于较新的系统?
    • SetSockOpt 适用于两者; WSAIoctl 只需要设置超时和间隔值。备注部分中的详细信息,位于 SIO_KEEPALIVE_VALS 下 msdn.microsoft.com/en-us/library/ms741621%28VS.85%29.aspx
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2012-09-18
    相关资源
    最近更新 更多