delphiindyindy10

Encountering 'Socket Error # 10053' while transferring files using TIdTCPClient and TIdTCPServer


When attempting to send a file from a client to a server using TIdTCPClient and TIdTCPServer components in Delphi, I encounter a persistent issue. The server code is set to receive a file, but during the transfer process, an error reading 'Connection abort error by software' occurs, disrupting the file transmission.

Here's a snippet of the server-side code:

procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
  ReceivedText, FileName: string;
  FileStream: TFileStream;
  TotalSize, BytesRead: Int64;
  Buffer: TIdBytes;
  SaveFileName: string;
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
          BytesRead:=0;
          if SaveDialog.Execute then
          begin
            SaveFileName := SaveDialog.FileName;
            ProgressBar1.Max := TotalSize;
            FileStream := TFileStream.Create(SaveFileName, fmCreate);
            try
              while BytesRead < TotalSize do
              begin
                AContext.Connection.IOHandler.ReadBytes(Buffer, Min(1024, TotalSize - BytesRead), False);
                FileStream.Write(Buffer[0], Length(Buffer));
                UpdateProgress(BytesRead);
                Inc(BytesRead, Length(Buffer));
              end;

              // File transfer completed
              Memo1.Lines.Add('File received and saved: ' + SaveFileName);
              UpdateProgress(0);
            finally
              FileStream.Free;
            end;
          end;
        finally
          SaveDialog.Free;
        end;
      end
    );
  end;

And the client-side code:

procedure TForm2.PngSpeedButton1Click(Sender: TObject);
var
  FileStream: TFileStream;
  FileToSend: string;
  Buffer: TIdBytes;
  TotalSize, BytesRead: Int64;
begin

  if OpenDialog1.Execute then
  begin
    TotalSize:=0;
    FileToSend := OpenDialog1.FileName;
    IdTCPClient1.IOHandler.LargeStream:=true;
    IdTCPClient1.IOHandler.WriteLn('$File$'); // Indicate to server to expect a file
    IdTCPClient1.IOHandler.WriteLn( ExtractFileName(OpenDialog1.FileName));
    IdTCPClient1.IOHandler.Write(Int64(TotalSize)); // Send the total size of the file

    FileStream := TFileStream.Create(FileToSend, fmOpenRead or fmShareDenyWrite);
    try
      TotalSize := FileStream.Size;


      BytesRead := 0;
      SetLength(Buffer, 1024); // Set buffer size
      Try
      while BytesRead < TotalSize do
      begin
        SetLength(Buffer, Min(1024, TotalSize - BytesRead));
        BytesRead := BytesRead + FileStream.Read(Buffer[0], Length(Buffer));
        IdTCPClient1.IOHandler.Write(Buffer, Length(Buffer)); // Send file content in chunks
      end;
      Except On E:Exception do    Clipboard.AsText:=E.Message;

      End;

      Memo1.lines.add('File : ['+ExtractFileName(OpenDialog1.FileName)+'] sent successfully.');
    finally
      FileStream.Free;
    end;
  end;


end;

Despite setting the stream to be large and attempting to transfer the file in chunks, this error persists. Any insights or suggestions.


Solution

  • I see a number of issues with your code:

    With all of that said, try something more like this:

    Server:

    procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
    var
      ReceivedText: string;
    begin
      ReceivedText := AContext.Connection.IOHandler.ReadLn; // Read incoming data
      if ReceivedText = '$File$' then
        ReceiveFile(AContext);
    End;   
    
    Procedure TForm2.ReceiveFile(AContext: TIdContext);
    var
      FileName, SaveFileName: String;
      FileStream: TFileStream;
    begin
      FileName := AContext.Connection.IOHandler.ReadLn; // Receive the file name from the client
    
      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
              SaveFileName := SaveDialog.FileName;
          finally
            SaveDialog.Free;
          end;
        end
      );            
    
      if SaveFileName = '' then
      begin
        AContext.Connection.IOHandler.WriteLn('REFUSED');
        Exit;
      end;
    
      try
        FileStream := TFileStream.Create(SaveFileName, fmCreate);
      except
        AContext.Connection.IOHandler.WriteLn('ERROR');
        Exit;
      end;
    
      try
        AContext.Connection.OnWorkBegin := TransferWorkBegin;
        AContext.Connection.OnWorkEnd := TransferWorkEnd;
        AContext.Connection.OnWork := TransferWork;
        try
          AContext.Connection.IOHandler.WriteLn('SEND');
          try
            AContext.Connection.IOHandler.LargeStream := True;
            AContext.Connection.IOHandler.ReadStream(FileStream, -1, False);
          except
            AContext.Connection.IOHandler.WriteLn('ERROR');
            raise;
          end;
        finally
          AContext.Connection.OnWorkBegin := nil;
          AContext.Connection.OnWorkEnd := nil;
          AContext.Connection.OnWork := nil;
        end;
      finally
        FileStream.Free;
      end;
    
      AContext.Connection.IOHandler.WriteLn('OK');
    
      TThread.Queue(nil,
        procedure
        begin
          Memo1.Lines.Add('File received and saved: ' + SaveFileName);
        end
      );
    end;
    
    var
      TransferProgress: Int64 = 0;
    
    procedure TForm2.TransferTimerElapsed(Sender: TObject);
    begin
      ProgressBar1.Position := TInterlocked.Read(TransferProgress);
    end;
    
    procedure TForm2.TransferWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
    begin
      TInterlocked.Exchange(TransferProgress, 0);
      TThread.Queue(nil,
        procedure
        begin
          ProgressBar1.Position := 0;
          ProgressBar1.Max := AWorkCountMax;
          TransferTimer.Enabled := True;
        end
      );
    end;
    
    procedure TForm2.TransferWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
    begin
      TInterlocked.Exchange(TransferProgress, 0);
      TThread.Queue(nil,
        procedure
        begin
          TransferTimer.Enabled := False;
          ProgressBar1.Position := 0;
        end
      );
    end;
    
    procedure TForm2.TransferWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
    begin
      TInterlocked.Exchange(TransferProgress, AWorkCount);
    end;
    

    Client:

    procedure TForm2.PngSpeedButton1Click(Sender: TObject);
    var
      FileStream: TFileStream;
      FileToSend, FileName: string;
    begin
      if not OpenDialog1.Execute then Exit;
    
      FileToSend := OpenDialog1.FileName;
      FileName := ExtractFileName(FileToSend);
    
      try
        FileStream := TFileStream.Create(FileToSend, fmOpenRead or fmShareDenyWrite);
        try
          IdTCPClient1.IOHandler.WriteLn('$File$'); // Indicate to server to expect a file
          IdTCPClient1.IOHandler.WriteLn(FileName);
    
          if IdTCPClient1.IOHandler.ReadLn <> 'SEND' then
            raise Exception.Create('Server cannot receive file');
    
          IdTCPClient1.IOHandler.LargeStream := True;
          IdTCPClient1.IOHandler.Write(FileStream, 0, True);
        finally
          FileStream.Free;
        end;
    
        if IdTCPClient1.IOHandler.ReadLn <> 'OK' then
          raise Exception.Create('Server failed to receive file');
    
      except
        on E: Exception do
        begin
          Memo1.Lines.Add('File : [' + FileName + '] failed.');
          Clipboard.AsText := E.Message;
          Exit;
        end;
      end;
    
      Memo1.Lines.Add('File : [' + FileName + '] sent successfully.');
    end;