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

TIdTCPServer.OnExecute事件陷入无限循环的原因排查求助

TIdTCPServer.OnExecute无限循环的原因与修复方案

问题根源

  1. OnExecute事件的执行机制
    TIdTCPServer的每个客户端连接对应独立工作线程,该线程会持续循环调用OnExecute事件处理程序,直到连接断开。你的代码中,当输入缓冲区为空时DoOnIndyExecute直接返回,线程会立刻再次调用该方法,导致高频空转,看起来像无限循环,同时占用大量CPU资源。

  2. 错误的缓冲区判断逻辑
    你用InputBufferIsEmpty判断是否读取数据,但这个判断是瞬时的——如果当前无数据,方法直接退出,线程马上再次执行,形成无意义的循环。正确做法是让线程在无数据时阻塞等待,而非反复检查。

  3. 线程不安全的UI操作
    Memo1.Lines.Add在工作线程中直接调用VCL控件方法,VCL控件并非线程安全,这可能导致UI异常或程序崩溃,也会间接加剧循环的异常表现。

  4. 未正确保护共享变量
    你创建了FCS临界区,但IncrementConnectionCount未用它保护FConnectionCount的递增,多线程环境下会导致计数错误。

修复方案

1. 让线程阻塞等待数据

去掉InputBufferIsEmpty判断,直接调用ReadLn——Indy的ReadLn默认会阻塞线程,直到有数据到来或连接断开,线程无数据时会休眠,不会空转循环。若需超时,可指定参数,比如ReadLn(5000)表示等待5秒超时。

2. 线程安全更新UI

使用TThread.Synchronize或TThread.Queue将UI操作同步到主线程执行。

3. 用临界区保护共享变量

修改FConnectionCount时必须进入临界区,避免多线程竞争。

修正后的服务器代码

procedure TForm1.FormCreate(Sender: TObject);
begin
  FConnectionCount := 0;
  FCS := TCriticalSection.Create;
  FIdTCPServer := TIdTCPServer.Create(nil);
  FIdTCPServer.DefaultPort := 50000;
  FIdTCPServer.MaxConnections := 9000;
  FIdTCPServer.OnExecute := DoOnIndyExecute;
  FIdTCPServer.Active := true;
end;

procedure TForm1.IncrementConnectionCount;
begin
  FCS.Enter;
  try
    Inc(FConnectionCount);
  finally
    FCS.Leave;
  end;
end;

procedure TForm1.DoOnIndyExecute(AContext: TIdContext);
var
  recv, peerIP: string;
  peerPort: Word;
  LogText: string;
begin
  try
    // 阻塞等待数据,可自行设置超时(如ReadLn(5000))
    recv := AContext.Connection.IOHandler.ReadLn;
    IncrementConnectionCount;
    peerIP := AContext.Binding.PeerIP;
    peerPort := AContext.Binding.PeerPort;
    AContext.Connection.IOHandler.WriteLn('Done');

    LogText := IntToStr(FConnectionCount) + ' - IP: ' + peerIP + ' Port: ' + IntToStr(peerPort) + ' Message: ' + recv;
    // 同步到主线程更新UI
    TThread.Synchronize(nil, procedure
    begin
      Memo1.Lines.Add(LogText);
    end);
  except
    on E: Exception do
    begin
      // 处理连接异常
      TThread.Synchronize(nil, procedure
      begin
        Memo1.Lines.Add('Connection error: ' + E.Message);
      end);
      // 断开连接,让线程退出循环
      AContext.Connection.Disconnect;
    end;
  end;
end;

额外提示

  • 客户端创建大量TIdTCPClient并保持连接时,注意系统端口限制(默认临时端口范围有限),若需数千级连接,需调整系统TCP参数。
  • 客户端的ReadLn会阻塞直到收到服务器响应,确保服务器能及时处理每个连接请求。

内容的提问来源于stack exchange,提问作者Francesco Esposito

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:14:53