procedure TThreadFTP.Execute;
begin
  inherited;
  while (not StartAtOnce) and (not Terminated) do
    Sleep(100);  Note:= TNote.Create;
  Note.FilePath:= ExtractFilePath(Application.ExeName) + 'Note\' + IntToStr(Self.Handle) + '.txt';
  try
    try
      if not Terminated then
        DoExec;
    except
    end;    Note.AddLine('DoExec Finished!'+#13#10);
    while not Terminated do
      Sleep(100);
    Note.AddLine('Thread Terminated!'+#13#10);
  finally
    Note.Free;
  end;
end;procedure TThreadFTP.DoExec;
var
  FTP: TIdFTP;
  RootDIR, RltDIR, File_Name: string;  I: Integer;
  List: TStringList;
  Stream: TStream;
begin
  Stream:= TMemoryStream.Create;
  List:= TStringList.Create;  FTP:= TIdFTP.Create(nil);
  try
    FTP.Host:= '192.168.0.2';  // FTP地址
    FTP.Username:= '**';
    FTP.Password:= '**';
    FTP.Connect(True, 5000);  // 连接
    if FTP.Connected then begin
      RootDIR:= Utf8ToAnsi( FTP.RetrieveCurrentDir );  // 获取根路径
      RltDIR:= '/TextFile/11';  // 设置相对路径
      FTP.ChangeDir( AnsiToUtf8( RootDIR+RltDIR ) ); // 切换当前目录
      FTP.List(List);  // 取当前目录的文件列表,不做该部貌似下面的DirectoryListing无法正常使用
      List.Clear;  // 清空列表
      for I:= 0 to FTP.DirectoryListing.Count - 1 do begin
        if Terminated then Exit;
        with FTP.DirectoryListing.Items[I] do begin
          if ItemType = ditFile then begin // 是文件
            File_Name:= Utf8ToAnsi( FileName );
            List.Add(File_Name);  // 添加文件名到列表
          end;
        end;
      end;      while not Terminated do begin
        for I:= 0 to List.Count - 1 do begin
          if Terminated then Exit;
          TMemoryStream(Stream).Clear;
          try
            Note.AddLine('FTP.Get('+List.Strings[I]+')');
            FTP.Get( AnsiToUtf8(RootDIR+RltDIR+'/'+List.Strings[I]), Stream); // 获取文件
            Note.AddLine('...Ok'+LRLS);
          except
            on E: Exception do begin
              Note.AddLine('...raise error'+#13#10);
              Note.AddLine('errmsg: '+E.Message+#13#10);
              raise;
            end;
          end;
          TMemoryStream(Stream).Clear;
        end;
      end;
    end;
  finally
    Note.AddLine('Free Objects');
    Stream.Free;
    List.Free;
    FTP.Free;
    Note.AddLine('...Ok'+#13#10);
  end;
end;
我做了个多线程FTP下载测试,目的是为了测试FTP服务器的并发承受能力。线程单元的代码如上。TNote是一个写日志到文件的类。
问题如下:
  开启50条线程 循环的去下载FTP上的一个目录中的文件。
  开启后,不到半分钟,所有线程都假死了,通过日志文件发现,问题出在了Get方法上(这个时候大概每个线程都已经下载了大约200多次)。
  想请教各位大虾,为什么会出现这种情况,是否有方法可以避免。 
  因为如果Get后立即返回异常 那到还好处理,但如果一直停在Get上 问题就比较大了。
  不知道是不是idFTP本身写的不是很稳定的缘故,各位在做FTP的时候都用什么实现的?
  ps: 下载时,应该存在多个线程下载同一个文件的情况,貌似FTP应该支持这种下载吧。错误日志如下:
FTP.Get(nr2008100313370782866)...Ok
FTP.Get(nr200810031337109847)...Ok
FTP.Get(nr200810031337118757)    // 停在这儿了
--------另外一个------------------------
FTP.Get(nr200810031341199218)...Ok
FTP.Get(nr200810031341470939)...raise error
errmsg: Socket Error # 10093  // 这个返回了错误号
Stream.Free;...Ok
List.Free;...Ok
FTP.Free;...Ok
DoExec Finished!
Thread Terminated!

解决方案 »

  1.   

    上面的代码少了一部分      while not Terminated do begin
            for I:= 0 to List.Count - 1 do begin
              if Terminated then Exit;
                TMemoryStream(Stream).Clear;
                try
                  Note.AddLine('FTP.Get('+List.Strings[I]+')');
                  FTP.Get( AnsiToUtf8(RootDIR+RltDIR+'/'+List.Strings[I]), Stream);
                  Note.AddLine('...Ok'+#13#10);
                except
                  on E: Exception do begin
                    Note.AddLine('...raise error'+#13#10);
                    Note.AddLine('errmsg: '+E.Message+#13#10);
                    raise;
                  end;
                end;
              TMemoryStream(Stream).Clear;
            end;
          end;
        end;
      finally
        Note.AddLine('Stream.Free;');
        Stream.Free;
        Note.AddLine('...Ok'+#13#10);
        Note.AddLine('List.Free;');
        List.Free;
        Note.AddLine('...Ok'+#13#10);
        Note.AddLine('FTP.Free;');
        FTP.Free;
        Note.AddLine('...Ok'+#13#10);
      end;
      

  2.   

    在Get方法里,获取数据的部分是
          FDataChannel := TIdSimpleServer.Create(nil); try
            with TIdSimpleServer(FDataChannel) do begin
              InitDataChannel;
              BoundIP := (Self.IOHandler as TIdIOHandlerSocket).Binding.IP;
              BoundPort := Self.DataPort;
              BoundPortMin := Self.DataPortMin;
              BoundPortMax := Self.DataPortMax;
              BeginListen;
              SendPort(Binding);
              if AResume then begin
                Self.SendCmd('REST ' + IntToStr(ADest.Position), [350]);  {Do not translate}
              end;
              Self.SendCmd(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP
              Listen;
              ReadStream(ADest, -1, True); // 估计是这里的问题吧。
            end;
          finally
            FreeAndNil(FDataChannel);
          end;
    好像socket出现异常后,ReadStream会出现死等的情况。
    记得在其他地方看到过,从FTP接收数据,好像有一种模式是服务器向客户端发起连接,并发生数据流,发完后服务端会主动断开连接。
    会不会是因为客户端的socket出现异常,导致TIdSimpleServer无法检测到Disconnect事件,结果ReadStream就在那里死等了。刚才发现出现死等后用 KillDataChannel 可以退出死等,不过这样的话必须得在另一个线程里调用 KillDataChannel,涉及到多线程同步了,是不是有简单点的方法
      

  3.   

    我做了一个fpt下载,每次只能成功下载一个文件,下载第二个文件时就没有响应?不知为什么?
      

  4.   

    我也做过类似的,比 TimeOut设的长一些!
      

  5.   

    帖子放了好久了,看来没人能解答了现在只好用KillDataChannel的方法做了,虽然麻烦了点。