0

I have two programs, one for the server and another for the client.

The server sends a file to the client. In the client, I use the TIdTCPClient, TIdThreadComponent, and TIdAntiFreeze components.

The file is well created, but the thread never finishes. When debugging, I never get to Fs.Free.

This is my client code:

procedure TForm1.IdThreadComponentRun(Sender: TIdThreadComponent);
var
  MsgDuServeur: string;
  Taille: Integer;
  Fs : TFileStream;
begin
  MsgDuServeur:= Trim(IdTCPClient.IOHandler.ReadLn(nil));
  MemoService.Lines.Add('Serveur : ' + MsgDuServeur);
  if (MsgDuServeur = 'RECUP_ENCOURS') then
  begin
    try
      Fs:= TFileStream.Create('C:\temp\client\1test.cds', fmCreate);
      try
        IdTCPClient.IOHandler.LargeStream:= True;
        Taille:= IdTCPClient.IOHandler.ReadInt64();
        IdTCPClient.IOHandler.ReadStream(Fs, -1, False); 
        IdThreadComponent1.Active:= False;
      finally
        Fs.Free;
      end;
    except
      on E: Exception do
      _MessageDlg(E.message, mtWarning, [mbOK], 0);
    end;
  end;
end;

this is my server code

procedure TServiceServeurTrf.IdTCPServerExecute(AContext: TIdContext);
var
  Flux: TMemoryStream;
  LStreamSize : int64;
begin
  try
    // Transfert de fichiers volumineux
    Context.Connection.Socket.WriteLn('RECUP_ENCOURS'); 
    AContext.Connection.IOHandler.LargeStream:= True;
    Flux:= TMemoryStream.Create;
    try
      Flux.LoadFromFile(C:\Windows\Temp\Transfert\3000\1test.cds);
      Flux.Position:= 0;
      AContext.Connection.IOHandler.Write(Flux.Size);
      AContext.Connection.IOHandler.Write(Flux, 0, True);
    finally
      FreeAndNil(Flux);
    end;
  except
    EcrireLog('Erreur');
  end;
end;

i deleted the antifreeze and after ReadStream() i use IdThreadComponent1.Active:= False; Now it's ok. How synchronised ? Indeed i want use progressbar with IdTCPClientWork, IdTCPClientWorkBegin, IdTCPClientWorkEnd but i don't see it progress

GB74
  • 11
  • 2
  • Please [edit] your question to also show the server code that is sending the file. But why are you setting the `AReadUntilDisconnect` parameter of `ReadStream()` to True when you know the file size being sent? It should be `False` instead. Better, if you set `AByteCount=-1` and `AReadUntilDisconnect=False` (the defaults) then `ReadStream()` will handle `ReadInt64()` for you. – Remy Lebeau Apr 06 '20 at 19:46
  • Also, FYI, the calls to `MemoService.Lines.Add()` and `MessageDlg()` need to be synchronized with the main UI thread, they can't be used safely in a worker thread. And why are you using `TIdAntiFreeze` if you are also using a thread? – Remy Lebeau Apr 06 '20 at 19:50
  • thanks, i modified my source code following your comments – GB74 Apr 06 '20 at 21:15
  • I posted an answer for you. – Remy Lebeau Apr 06 '20 at 21:47

1 Answers1

1

You are misusing the TIdIOHandler.Write(TStream) and TIdIOHandler.ReadStream() methods.

On the client side, setting the AByteCount parameter of ReadStream() to -1 and the AReadUntilDisconnect parameter to False (which are the default values) causes ReadStream() to read the stream size for you before it reads the stream data, where the size is read using ReadInt32() or ReadInt64(), depending on the LargeStream property.

Originally, your client was calling ReadInt64() (and saving the result into an Integer!) followed by calling ReadStream() with AByteCount set to the result of ReadInt64() and AReadUntilDisconnect set to True. So, no matter how many bytes were actually being sent, ReadStream() would read indefinitely until the server closed the connection.

Then you changed your client code to call ReadStream() with AByteCount set to -1 and AReadUntilDisconnect set to False, but you did not remove the call to ReadInt64() beforehand, so now you have a double-read of the stream size.

On the server side, setting the AWriteByteCount parameter to True causes Write(TStream) to send the stream size before sending the stream data, where the size is sent using Write(Int32) or Write(Int64), depending on the LargeStream property. But, your server code is calling Write(Int64) before calling Write(TStream), so you have a double-send of the stream size.

You are also not synchronizing with the main thread when accessing your UI.

Try this instead:

procedure TForm1.IdThreadComponentRun(Sender: TIdThreadComponent);
var
  MsgDuServeur: string;
  Fs : TFileStream;
begin
  MsgDuServeur := Trim(IdTCPClient.IOHandler.ReadLn);
  TThread.Synchronize(
    procedure
    begin
      MemoService.Lines.Add('Serveur : ' + MsgDuServeur);
    end
  );
  if (MsgDuServeur = 'RECUP_ENCOURS') then
  begin
    try
      Fs := TFileStream.Create('C:\temp\client\1test.cds', fmCreate);
      try
        IdTCPClient.IOHandler.LargeStream := True;
        IdTCPClient.IOHandler.ReadStream(Fs, -1, False); // <-- calls ReadInt64() internally for you!
      finally
        Fs.Free;
      end;
    except
      on E: Exception do
        IdTCPClient.Disconnect;
        TThread.Queue(
          procedure
          begin
            MessageDlg(E.message, mtWarning, [mbOK], 0);
          end
        );
    end;
  end;
end;
procedure TServiceServeurTrf.IdTCPServerExecute(AContext: TIdContext);
var
  Flux: TMemoryStream;
begin
  try
    // Transfert de fichiers volumineux
    Flux := TMemoryStream.Create; // <-- consider using TFileStream instead...
    try
      Flux.LoadFromFile(C:\Windows\Temp\Transfert\3000\1test.cds);
      AContext.Connection.IOHandler.LargeStream := True;
      AContext.Connection.IOHandler.WriteLn('RECUP_ENCOURS'); 
      AContext.Connection.IOHandler.Write(Flux, 0, True); // <-- calls Write(Int64) internally for you!
    finally
      Flux.Free;
    end;
  except
    EcrireLog('Erreur'); // <-- make sure this is thread-safe!
    raise;
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • thanks. i did the changes, but when i delete the line IdThreadComponent1.Active:= False; in client, the first test is ok, but when i close the form to test again the form don't show – GB74 Apr 07 '20 at 09:25
  • @GB74 that likely has nothing to do with deactivating the thread. Use the debugger to make sure you don't have problems elsewhere in your client code, which we can't see. – Remy Lebeau Apr 07 '20 at 16:09
  • Thank you for your help. Below this is my client code – GB74 Apr 07 '20 at 20:33
  • I want to use a progressbar but i don't see the progress – GB74 Apr 07 '20 at 23:36
  • @GB74 Then you are not using the progress events correctly, but you did not show that code. Also, the events are fired in the same thread that is reading/writing the data, so make sure you synchronize access to the UI thread when accessing the ProgressBar. – Remy Lebeau Apr 07 '20 at 23:46
  • would you have an example ? Sorry but I don't understand your request – GB74 Apr 08 '20 at 00:13
  • @GB74 You should already know how to assign event handlers. And I've already shown you examples of syncing with the main UI thread. – Remy Lebeau Apr 08 '20 at 00:25