Отправить файл с сервера клиенту с помощью Indy - PullRequest
0 голосов
/ 06 апреля 2020

У меня есть две программы, одна для сервера и другая для клиента.

Сервер отправляет файл клиенту. В клиенте я использую компоненты TIdTCPClient, TIdThreadComponent и TIdAntiFreeze.

Файл хорошо создан, но поток никогда не заканчивается. При отладке я никогда не получаю Fs.Free.

Это мой код клиента:

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;

Это мой код сервера

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;

я удалил антифриз и после ReadStream () я использую IdThreadComponent1.Active: = False; Сейчас все в порядке. Как синхронизируется? Действительно, я хочу использовать индикатор прогресса с IdTCPClientWork, IdTCPClientWorkBegin, IdTCPClientWorkEnd, но не вижу его прогресса

1 Ответ

1 голос
/ 07 апреля 2020

Вы неправильно используете методы TIdIOHandler.Write(TStream) и TIdIOHandler.ReadStream().

На стороне клиента задайте для параметра AByteCount для ReadStream() значение -1 и для параметра AReadUntilDisconnect значение False (которые являются значениями по умолчанию) заставляет ReadStream() прочитать размер потока перед тем, как он прочитает данные потока, где размер читается с использованием ReadInt32() или ReadInt64(), в зависимости от свойства LargeStream.

Изначально ваш клиент вызывал ReadInt64() (и сохранял результат в Integer!), А затем вызывал ReadStream() с AByteCount, для которого установлено ReadInt64() и AReadUntilDisconnect, установленным на True. Таким образом, независимо от того, сколько байтов было отправлено, ReadStream() будет считывать бесконечно, пока сервер не закроет соединение.

Затем вы изменили свой клиентский код на вызов ReadStream() с AByteCount, установленным на -1 и AReadUntilDisconnect установлен на False, но вы не удалили вызов на ReadInt64() заранее, поэтому теперь у вас есть двойное чтение размера потока.

На стороне сервера, установка Параметр AWriteByteCount в True заставляет Write(TStream) отправлять размер потока перед отправкой данных потока, где размер отправляется с использованием Write(Int32) или Write(Int64), в зависимости от свойства LargeStream. Но ваш серверный код вызывает Write(Int64) перед вызовом Write(TStream), поэтому у вас есть двойная передача размера потока.

Вы также не синхронизируете с основным потоком при доступе к вашему пользовательскому интерфейсу.

Попробуйте вместо этого:

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;
...