如何使用IndyFtpServer(v10)限制检索文件的速度

时间:2017-10-31 04:50:59

标签: delphi ftp indy ftp-server

我正在使用Delphi XE 6和Indy10开发FTP服务器。问题是我需要限制下载速度(必须是可配置的Ex.1 KB / s,1 MB / s等)并且我不能使它工作。我知道像BitsPerSec等道具,但这只会影响协议数据交换,而不会影响与RETR命令的文件交换。我在IdFTPServer.pass中看到,并且Stream被转换为字符串并使用repeat / until循环发送(使用IOHandler.Write())但我需要某种形式的控制上传/下载过程并且能够限制速度所有传入客户端连接。有人帮忙实现这个目标吗?。

PD:抱歉我的英语很差。

我尝试使用以下代码为RETR命令实现CommandHandler:

procedure TForm1.IdFTPServer1CommandHandlers0Command(ASender: TIdCommand);
var
  LContext : TIdFTPServerContext;
  vStream: TFileStream;
  path: String;
  vX: Integer;
  LEncoding: IIdTextEncoding;
begin

  LEncoding := IndyTextEncoding_8Bit;
  LContext := ASender.Context as TIdFTPServerContext;
  path := 'D:\indy_in_depth.pdf';


  try
    vStream := TFileStream.Create(path, fmOpenRead);
    //LContext.DataChannel.FtpOperation := ftpRetr;
    //LContext.DataChannel.Data := VStream;
    LContext.DataChannel.OKReply.SetReply(226, RSFTPDataConnClosed);
    LContext.DataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally);

    ASender.Reply.SetReply(150, RSFTPDataConnToOpen);
    ASender.SendReply;

    Memo1.Lines.Add('Sending a file with: ' + IntToStr(vStream.Size) + ' bytes');
    //Make control of speed here !
    for vX := 1 to vStream.Size do
      begin
        vStream.Position := vX;
        LContext.Connection.IOHandler.Write(vStream, 1, False);
        if vX mod 10000 = 0 then
          Memo1.Lines.Add('Sended byte: ' + IntToStr(vX));
      end;

    //LContext.DataChannel.InitOperation(False);

    //LContext.Connection.IOHandler.Write(vStream);

    //LContext.KillDataChannel;
    LContext.Connection.Socket.Close;

    VStream.Free;
  except
    on E: Exception do
    begin
      ASender.Reply.SetReply(550, E.Message);
    end;
  end;

end;

但Filezilla FTP客户端显示流数据,就像其他命令一样。

Filezilla Client

1 个答案:

答案 0 :(得分:12)

  

问题是我需要限制下载速度(必须是可配置的例如1 KB / s,1 MB / s等)并且我不能使它工作。我知道一些道具,如BitsPerSec等,但这只会影响协议数据交换,而不会影响与RETR命令的文件交换。

BitsPerSecTIdInterceptThrottler类的属性(以及RecvBitsPerSecSendBitsPerSec属性)。可以通过其TIdTCPConnection属性将该类的实例分配给任何Intercept对象。这不仅限于命令连接,它也可以用于传输连接。

您可以通过TIdTCPConnection成员访问TIdFTPServerTIdFTPServerContext.DataChannel.FDataChannel文件传输的OnRetrieveFile对象,例如type // the TIdDataChannel.FDataChannel member is 'protected', // so use an accessor class to reach it... TIdDataChannelAccess = class(TIdDataChannel) end; procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerContext; const AFileName: TIdFTPFileName; var VStream: TStream); var Conn: TIdTCPConnection; begin Conn := TIdDataChannelAccess(ASender.DataChannel).FDataChannel; if Conn.Intercept = nil then Conn.Intercept := TIdInterceptThrottler.Create(Conn); TIdInterceptThrottler(Conn.Intercept).BitsPerSec := ...; VStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); end; 事件:

T(File)Stream

另一种方法是创建自己的Read()派生类,以覆盖虚拟Write()VStream方法,根据需要执行自己的限制,然后将该类的实例分配给OnRetrieveFileOnStoreFile事件的IdFTPServer.pas参数。

  

我在IOHandler.Write(TStream)中看到,Stream被转换为字符串并使用repeat / until循环发送(使用IOHandler.Write())

不,流不会转换为字符串。你误读了代码。流的内容将作为二进制数据原样发送到RETR方法的单个调用中。

  

但我需要某种形式的控制上传/下载过程,并能够限制所有传入客户端连接的速度。

见上文。

  

我尝试为RETR命令实现CommandHandler

您不应该直接处理TIdFTPServer命令 OnRetrieveFile已经为您做到了。您打算使用OnStoreFile事件来准备下载,并使用assign sum_count = (en0 ? cnt0 : 32'b0) + (en1 ? cnt1 : 32'b0) 事件进行上传。