You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Delphi的TIdTCPClient/TIdTCPServer传文件时遇Socket Error #10053

Delphi中TIdTCPClient/TIdTCPServer文件传输中断问题(Connection abort error by software)

在使用Delphi的TIdTCPClient和TIdTCPServer组件实现客户端向服务器传输文件时,持续出现“Connection abort error by software”错误,导致文件传输中断。已设置大流并尝试分块传输,但问题依旧。

服务器端代码片段

procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
  ReceivedText, FileName: string;
  TotalSize: Int64;
begin
  ReceivedText := AContext.Connection.IOHandler.ReadLn; // Read incoming data

  if ReceivedText = '$File$' then
  begin
    AContext.Connection.IOHandler.LargeStream:=true;
    UpdateProgress(0);
    FileName := AContext.Connection.IOHandler.ReadLn; // Receive the file name from the client
    TotalSize := AContext.Connection.IOHandler.ReadInt64; // Read the total size of the incoming file
    SaveFile(FileName,TotalSize,AContext);
  end;
End;

Procedure TForm2.SaveFile(FileName:String;TotalSize:Int64;AContext:TIdContext);
var
SaveFileName:String;
FileStream:TFileStream;
BytesRead:Int64;
Buffer: TIdBytes;
begin
  TThread.Synchronize(nil,
      procedure
      var
        SaveDialog: TSaveDialog;
      begin
        SaveDialog := TSaveDialog.Create(nil);
        try
          SaveDialog.InitialDir := 'D:\TestFolder\';
          SaveDialog.FileName := FileName; // Set default file name to the received file name
          if SaveDialog.Execute then
          begin
            SaveFileName := SaveDialog.FileName;
            ProgressBar1.Max := TotalSize;
          end
          else
            SaveFileName := '';
        finally
          SaveDialog.Free;
        end;
      end
    );

  if SaveFileName = '' then Exit;

  // 文件接收逻辑留在工作线程,不阻塞主线程
  FileStream := TFileStream.Create(SaveFileName, fmCreate);
  try
    BytesRead := 0;
    SetLength(Buffer, 1024);
    while BytesRead < TotalSize do
    begin
      AContext.Connection.IOHandler.ReadBytes(Buffer, Min(1024, TotalSize - BytesRead), False);
      FileStream.Write(Buffer[0], Length(Buffer));
      Inc(BytesRead, Length(Buffer));
      // 仅更新UI时同步到主线程
      TThread.Synchronize(nil, procedure
        begin
          UpdateProgress(BytesRead);
        end);
    end;

    // 传输完成后更新UI
    TThread.Synchronize(nil, procedure
      begin
        Memo1.Lines.Add('File received and saved: ' + SaveFileName);
        UpdateProgress(0);
      end);
  finally
    FileStream.Free;
  end;
end;

客户端代码片段

procedure TForm2.PngSpeedButton1Click(Sender: TObject);
var
  FileStream: TFileStream;
  FileToSend: string;
  Buffer: TIdBytes;
  TotalSize, BytesRead, ReadCount: Int64;
begin
  if OpenDialog1.Execute then
  begin
    FileToSend := OpenDialog1.FileName;
    IdTCPClient1.IOHandler.LargeStream:=true;

    // 先获取文件大小再发送
    FileStream := TFileStream.Create(FileToSend, fmOpenRead or fmShareDenyWrite);
    try
      TotalSize := FileStream.Size;

      // 发送文件标识、文件名、文件大小
      IdTCPClient1.IOHandler.WriteLn('$File$');
      IdTCPClient1.IOHandler.WriteLn(ExtractFileName(FileToSend));
      IdTCPClient1.IOHandler.Write(Int64(TotalSize));

      BytesRead := 0;
      SetLength(Buffer, 1024);
      Try
        while BytesRead < TotalSize do
        begin
          ReadCount := FileStream.Read(Buffer[0], Min(1024, TotalSize - BytesRead));
          if ReadCount <= 0 then Break;
          IdTCPClient1.IOHandler.Write(Buffer, ReadCount);
          Inc(BytesRead, ReadCount);
        end;
        Memo1.lines.add('File : ['+ExtractFileName(FileToSend)+'] sent successfully.');
      Except 
        On E:Exception do Clipboard.AsText:=E.Message;
      End;
    finally
      FileStream.Free;
    end;
  end;
end;

问题分析与修复建议

  • 主线程阻塞导致连接超时:原服务器代码把整个文件接收逻辑都放到TThread.Synchronize中执行,这会让服务器工作线程等待用户选择保存路径(可能耗时很久),客户端长时间收不到服务器响应就会主动断开连接,触发“Connection abort”错误。修复时只将UI交互(弹窗、进度更新)放到同步块,文件读写留在工作线程。
  • 文件大小发送顺序错误:原客户端先发送了TotalSize=0,之后才读取真实文件大小,服务器拿到的是无效的0值,后续接收逻辑异常,导致通信协议混乱。调整顺序为:先打开文件获取大小,再发送给服务器。
  • IO超时设置:默认的IO超时可能太短,传输大文件时容易触发超时断开。在服务器和客户端的IOHandler中设置ReadTimeout和WriteTimeout为较大值(比如30000即30秒,或根据文件大小调整)。
  • 读写逻辑优化:客户端要正确处理FileStream.Read的返回值(实际读取字节数),避免累加错误;服务器端复用固定缓冲区,减少内存分配开销。

内容的提问来源于stack exchange,提问作者Issam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 01:58:13