使用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
相关产品推荐
相关产品推荐

