【问题标题】:Delphi XE6 Indy Chat RoomDelphi XE6 Indy 聊天室
【发布时间】:2014-10-17 13:28:13
【问题描述】:

我正在尝试使用 TIdTCP 在 Internet 上创建聊天室!
client发送消息给server,server会继续发送给其他clients,这里我的代码:

服务器

   var
  List: TIdContextList;
  Context: TIdContext;
  i: Integer;
  Msg: String;

Procedure TSForm.SendALL(text: string);
begin
  MSG:= Trim(text);
  List := Server.Contexts.LockList;
  try
    for i := 0 to List.Count - 1 do
    begin
      Context := TIDContext(List[i]);
      Context.Connection.IOHandler.WriteLn(UTF8Encode(msg));
    end;
  finally
    Server.Contexts.UnlockList;
  end;
end;

procedure TSForm.ServerExecute(AContext: TIdContext);
var m: string;
Begin
 m:= acontext.Connection.IOHandler.ReadLn();
  begin
   SForm.log.Lines.Add(Acontext.Connection.Socket.Binding.PeerIP+' > '+m); //Log is MEMO
   SendALL(m);
  end;
end;

和客户

type
 TReadingThread = class(TThread)
  protected
    FConn: TIdTCPConnection;
    procedure Execute; override;
    procedure DoTerminate; override;
  public
    constructor Create(AConn: TIdTCPConnection); reintroduce;
  end;

var readthread: TReadingThread = Nil;

constructor TReadingThread.Create(AConn: TIdTCPConnection);
begin
  FConn := AConn;
  inherited Create(False);
end;

procedure TReadingThread.Execute;
var
  cmd: string;
begin
  while not Terminated do
  begin
    cmd := UTF8ToUnicodeString(FConn.IOHandler.ReadLn());
    Trim(cmd);
    if cmd <> '' then
    begin
    CForm.Memo1.Lines.Add(cmd); //Memo1 to show messages
    end;
  end;
   Application.ProcessMessages;
end;

procedure TReadingThread.DoTerminate;
begin
  inherited;
end;

procedure TCForm.ClientConnected(Sender: TObject);
begin
 readthread:= TReadingThread.Create(Client);
end;

procedure TCForm.ClientDisconnected(Sender: TObject);
begin
    if  readthread<> nil then
  begin
    readthread.Terminate;
    readthread.WaitFor;
    FreeAndNil(readthread);
  end;
end;

一切看起来都很好,但是当服务器重新发送消息时,其他客户端得到它并正常显示在备忘录中发送该消息的客户端似乎冻结,必须单击备忘录才能使文字出现!

不知道哪里出错了,希望得到你的帮助,谢谢!

【问题讨论】:

  • 为什么要直接从线程访问控件?
  • 那么您认为如何在备忘录中添加消息?使用定时器?

标签: multithreading delphi firemonkey indy delphi-xe6


【解决方案1】:

您的代码有两处错误:

您正在从第二个线程访问 VCL 组件。这通常被认为是非常糟糕的,因为它可能会导致许多无法预料的问题。

procedure TReadingThread.Execute;
...
  CForm.Memo1.Lines.Add(cmd); //Verry bad as you are accesing VCL from another thread
...
  Application.ProcessMessages; //This is even worse. I suggest you get rid of this
...
end;

还要去掉 Application.ProcessMessages,因为这只会导致更多它解决的问题,更不用说它会极大地影响程序的性能。

所以你应该在更新备忘录时使用同步命令。这会强制更新备忘录的代码在主线程中执行,就像访问 VCL 的所有代码一样。

procedure TReadingThread.Execute;
...
  Synchronize(
    procedure
    begin
      CForm.Memo1.Lines.Add(cmd);
    end;);
...
end;

【讨论】:

  • 其实显然不是VCL,而是FMX,而是同一个概念。
  • Synchronize(CForm.Memo1.Lines.Add(cmd)); 不会编译。请改用匿名程序:@​​987654324@
  • 谢谢你!我已经删除了 Application.ProcessMessages,然后在 TReadingThread 中再添加 1 个过程并使用 Sysnchronize,它可以工作:)
  • @RemyLebeau 我完全忘记了这一点。我已经有一段时间不需要使用 Synchronize,因为我所有的多线程应用程序的所有数据都与 UI 分离,所以我不需要调用 Synchronize。编辑我的答案。
  • 我个人会排队,因为似乎不需要等待。
猜你喜欢
  • 2014-07-08
  • 2016-03-15
  • 2014-12-23
  • 2014-08-12
  • 2014-09-22
  • 2021-12-02
  • 1970-01-01
  • 2014-08-23
  • 2016-01-18
相关资源
最近更新 更多