【问题标题】:Send a file from the server to client with Indy使用 Indy 从服务器向客户端发送文件
【发布时间】:2020-04-08 04:39:11
【问题描述】:

我有两个程序,一个用于服务器,另一个用于客户端。

服务器向客户端发送文件。在客户端中,我使用了TIdTCPClientTIdThreadComponentTIdAntiFreeze 组件。

文件创建良好,但线程永远不会结束。调试时,我从来没有到过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 的进度条,但我看不到它的进度

【问题讨论】:

  • edit您的问题也显示发送文件的服务器代码。但是,当您知道要发送的文件大小时,为什么要将ReadStream()AReadUntilDisconnect 参数设置为True?它应该是False。更好的是,如果您设置AByteCount=-1AReadUntilDisconnect=False(默认值),那么ReadStream() 将为您处理ReadInt64()
  • 另外,仅供参考,对MemoService.Lines.Add()MessageDlg() 的调用需要与主UI 线程同步,它们不能在工作线程中安全使用。如果你也在使用线程,为什么还要使用TIdAntiFreeze
  • 谢谢,我按照你的 cmets 修改了我的源代码
  • 我给你发了答案。

标签: delphi tcpclient indy10


【解决方案1】:

您误用了TIdIOHandler.Write(TStream)TIdIOHandler.ReadStream() 方法。

在客户端,将ReadStream()AByteCount 参数设置为-1 并将AReadUntilDisconnect 参数设置为False(这是默认值)会导致ReadStream() 为您读取流大小在读取流数据之前,使用ReadInt32()ReadInt64() 读取大小,具体取决于LargeStream 属性。

最初,您的客户调用ReadInt64()(并将结果保存到Integer!)然后调用ReadStream(),将AByteCount设置为ReadInt64()的结果,AReadUntilDisconnect设置为@987654338 @。因此,无论实际发送了多少字节,ReadStream() 都会无限期地读取,直到服务器关闭连接。

然后您将客户端代码更改为调用ReadStream(),并将AByteCount 设置为-1AReadUntilDisconnect 设置为False,但是您没有事先删除对ReadInt64() 的调用,所以现在您有了流大小的双重读取。

在服务器端,将AWriteByteCount 参数设置为True 会导致Write(TStream) 在发送流数据之前发送流大小,其中大小使用Write(Int32)Write(Int64) 发送,具体取决于LargeStream 财产。但是,您的服务器代码在调用Write(TStream) 之前调用了Write(Int64),因此您有双重发送流大小。

访问 UI 时,您也没有与主线程同步。

试试这个:

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;

【讨论】:

  • 谢谢。我做了更改,但是当我删除行 IdThreadComponent1.Active:= False;在客户端,第一次测试没问题,但是当我关闭表单再次测试时,表单不显示
  • @GB74 这可能与停用线程无关。使用调试器确保您的客户端代码的其他地方没有我们看不到的问题。
  • 感谢您的帮助。下面是我的客户端代码
  • 我想使用进度条,但看不到进度
  • @GB74 那么您没有正确使用进度事件,但您没有显示该代码。此外,事件在读取/写入数据的同一线程中触发,因此请确保在访问 ProgressBar 时同步对 UI 线程的访问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-29
  • 2017-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多