You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FMX/Delphi中TIdTCPClient按钮触发失效与Timer关联问题

问题分析与解决方案

你的问题核心在于TIdThreadComponent的OnRun事件仅会执行一次,线程运行完OnRun中的代码后就会终止,后续客户端接收到的数据会堆积在IOHandler的输入缓冲区中,无人处理。

至于启用Timer后功能看似正常,是因为Timer触发时会维持主线程的消息循环持续运行,间接让Indy的内部机制在主线程中处理了部分数据,但这只是巧合,并非正确的解决方案。

修复步骤

  1. 让线程持续运行:在IdThreadComponentRun中加入循环,让线程不断检查客户端的输入缓冲区,同时加入等待逻辑避免CPU占用过高。
  2. 优化数据读取逻辑:使用IOHandler.WaitForData替代轮询缓冲区是否为空,效率更高。
  3. 正确销毁线程:在窗体销毁时终止线程并释放资源,避免内存泄漏。

修改后的代码

type
  TForm2 = class(TForm)
    Client1: TIdTCPClient;
    Client2: TIdTCPClient;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    idThreadComponent : TIdThreadComponent;
    procedure IdThreadComponentRun(Sender: TIdThreadComponent);
    procedure IdTCPClientConnected(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  // 指定窗体为线程组件的Owner,方便自动管理
  idThreadComponent:= TIdThreadComponent.Create(Self);
  idThreadComponent.OnRun := IdThreadComponentRun;

  Client1.Port:= 1202;
  Client1.Host:= 'localhost';
  Client2.Port:= 1203;
  Client2.Host:= 'localhost';

  Client1.OnConnected:= IdTCPClientConnected;
  Client2.OnConnected:= IdTCPClientConnected;

  Client1.Connect;
  Client2.Connect;
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  // 终止线程并等待其结束,再释放资源
  if Assigned(idThreadComponent) then
  begin
    idThreadComponent.Terminate;
    idThreadComponent.WaitFor;
  end;
end;

procedure TForm2.IdThreadComponentRun(Sender: TIdThreadComponent);
var
  Rx: TIDBytes;
begin
  // 循环运行直到线程被终止
  while not Sender.Terminated do
  begin
    // 等待Client1的数据(超时10ms,避免死等)
    if Client1.IOHandler.WaitForData(10) then
    begin
      // 读取所有可用数据
      Client1.IOHandler.ReadBytes(Rx, -1);
      if Length(Rx) > 0 then
        Client2.IOHandler.Write(Rx);
    end;

    // 等待Client2的数据
    if Client2.IOHandler.WaitForData(10) then
    begin
      Client2.IOHandler.ReadBytes(Rx, -1);
      if Length(Rx) > 0 then
        Client1.IOHandler.Write(Rx);
    end;
  end;
end;

procedure TForm2.IdTCPClientConnected(Sender: TObject);
begin
  IdThreadComponent.Active := True;
end;

procedure TForm2.Button1Click(Sender: TObject);
var
  Tx: TIDBytes;
begin
  SetLength(Tx, 1);
  Tx[0] := $FF; // 给字节赋值,方便测试验证
  Client1.IOHandler.Write(Tx, Length(Tx));
end;

额外说明

  • 移除了无用的Timer组件,现在线程会持续运行处理数据,不需要Timer维持消息循环。
  • 使用WaitForData(10)让线程在没有数据时休眠10ms,避免CPU空转。
  • 在窗体销毁时正确终止线程,防止内存泄漏和程序崩溃。

内容的提问来源于stack exchange,提问作者Ильдар Ильясов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 00:02:02