7

私は何年もの間、Indy を使用して FTP 経由でファイルを転送してきましたが、次の問題に対する満足のいく解決策を見つけることができませんでした。

ユーザーがルーターの背後で大きなファイルをアップロードしている場合、次のことが発生することがあります。ファイルは正常にアップロードされますが、タイムアウトのためにコマンド チャネルが切断されます。通常、これはサーバーへの直接接続では発生しません。これは、サーバーがデータ チャネルで転送が行われていることを「認識」しているためです。ただし、一部のルーターはこれを認識しておらず、コマンド チャネルは閉じられています。

多くのプログラムは、NOOP コマンドを定期的に送信して、これが標準の FTP 仕様の一部でなくても、コマンド チャネルを維持します。私の質問: どうすればいいですか? OnWork イベントで NOOP コマンドを送信しますか? これにより何らかの巻き添え被害が発生しますか? たとえば、何らかの対応を処理する必要がありますか? この問題を解決するにはどうすればよいですか?

4

1 に答える 1

3

これを処理するために、いくつかのアプローチを使用します: (1)転送中に制御チャネルでTCP/IP キープアライブを有効にし、(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. ファイルが正常に転送された場合に正常に回復するには、最後に再接続し、SIZEまたはLISTを実行してファイル サイズを取得します。それらが一致する場合、ファイルは正常に転送されており、他に何もする必要はありません。サーバーがサポートしている場合は、XCRCコマンドを送信して CRC 値を取得することもできるため、ローカル ファイルと比較できます。

  3. 本当に堅牢にしたい場合は、 も確認できますTIdFTP.CanResume。設定されている場合、サーバーはRESTコマンドをサポートしているため、 のパラメーターに渡すtrueことで、中断したところから転送を再開できます。AResumeTIdFTP.Get/Put

于 2010-04-19T15:04:31.840 に答える