FMX/Delphi中TIdTCPClient按钮触发失效与Timer关联问题
问题分析与解决方案
你的问题核心在于TIdThreadComponent的OnRun事件仅会执行一次,线程运行完OnRun中的代码后就会终止,后续客户端接收到的数据会堆积在IOHandler的输入缓冲区中,无人处理。
至于启用Timer后功能看似正常,是因为Timer触发时会维持主线程的消息循环持续运行,间接让Indy的内部机制在主线程中处理了部分数据,但这只是巧合,并非正确的解决方案。
修复步骤
- 让线程持续运行:在
IdThreadComponentRun中加入循环,让线程不断检查客户端的输入缓冲区,同时加入等待逻辑避免CPU占用过高。 - 优化数据读取逻辑:使用
IOHandler.WaitForData替代轮询缓冲区是否为空,效率更高。 - 正确销毁线程:在窗体销毁时终止线程并释放资源,避免内存泄漏。
修改后的代码
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,提问作者Ильдар Ильясов
相关产品推荐
相关产品推荐

