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

Delphi 7中如何用Indy 10 TCP/IP实现服务器到客户端的文本接收?

解决Delphi 7 + Indy 10客户端接收服务器文本的问题

首先明确:Indy 10的TIdTCPClient确实没有直接的OnRead或OnDataReceived事件,但它的IOHandler提供了完整的数据读取接口。因为Indy是阻塞式组件,直接在主线程调用读取方法会卡死UI,所以正确做法是用后台线程处理数据接收,而非依赖OnWork事件和定时器。

你的现有代码存在两个核心问题:

  • OnWork事件仅用于报告组件的工作状态(比如发送/接收字节数),完全没有从服务器读取数据的逻辑,zb_FReceivedData的赋值环节缺失,定时器自然无法触发有效操作。
  • 定时器依赖主线程消息循环,若主线程被Indy的阻塞操作占用,定时器消息会被延迟甚至无法处理。

正确实现方案:后台线程接收数据

步骤1:定义接收线程类

在单元中创建一个继承自TThread的线程类,专门负责循环读取服务器数据,并通过同步方法更新UI:

type
  TClientReceiveThread = class(TThread)
  private
    FClient: TIdTCPClient;
    FOnDataReceived: TNotifyEvent;
    FReceivedText: string;
    procedure UpdateUI;
  protected
    procedure Execute; override;
  public
    constructor Create(AClient: TIdTCPClient);
    property OnDataReceived: TNotifyEvent read FOnDataReceived write FOnDataReceived;
    property ReceivedText: string read FReceivedText;
  end;

constructor TClientReceiveThread.Create(AClient: TIdTCPClient);
begin
  inherited Create(True); // 创建后先挂起,手动启动
  FClient := AClient;
  FreeOnTerminate := True; // 线程结束自动释放
end;

procedure TClientReceiveThread.Execute;
var
  ReceivedLine: string;
begin
  inherited;
  while not Terminated and FClient.Connected do
  begin
    try
      // 读取服务器发送的文本行(假设服务器以换行分隔消息)
      ReceivedLine := FClient.IOHandler.ReadLn;
      if ReceivedLine <> '' then
      begin
        FReceivedText := ReceivedLine;
        // 同步更新UI(避免跨线程操作控件)
        Synchronize(UpdateUI);
      end;
    except
      // 处理断开连接或读取错误
      Break;
    end;
  end;
end;

procedure TClientReceiveThread.UpdateUI;
begin
  if Assigned(FOnDataReceived) then
    FOnDataReceived(Self);
end;

步骤2:在窗体中使用线程

在窗体类中添加线程变量,连接服务器后启动接收线程:

type
  TForm1 = class(TForm)
    IdTCPClient1: TIdTCPClient;
    ClientOutput: TMemo;
    btnConnect: TButton;
    procedure btnConnectClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FReceiveThread: TClientReceiveThread;
    procedure OnThreadDataReceived(Sender: TObject);
  public
    { Public declarations }
  end;

procedure TForm1.btnConnectClick(Sender: TObject);
begin
  // 连接服务器(需提前设置IdTCPClient1的Host和Port)
  IdTCPClient1.Connect;
  
  // 创建并启动接收线程
  FReceiveThread := TClientReceiveThread.Create(IdTCPClient1);
  FReceiveThread.OnDataReceived := OnThreadDataReceived;
  FReceiveThread.Start;
end;

procedure TForm1.OnThreadDataReceived(Sender: TObject);
begin
  // 将接收到的文本添加到Memo控件
  ClientOutput.Lines.Add('Server: ' + TClientReceiveThread(Sender).ReceivedText);
end;

步骤3:处理断开与线程终止

在窗体关闭或主动断开连接时,终止线程避免内存泄漏:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if IdTCPClient1.Connected then
    IdTCPClient1.Disconnect;
  if Assigned(FReceiveThread) then
  begin
    FReceiveThread.Terminate;
    FReceiveThread.WaitFor;
  end;
end;

补充说明

  • 如果服务器发送的不是换行分隔的文本,可根据实际协议调整读取逻辑:比如用IOHandler.ReadBytes读取指定长度的字节,再转换为字符串。
  • 绝对不要在主线程中调用IdTCPClient1.IOHandler.ReadLn这类阻塞方法,否则会导致UI假死。
  • OnWork事件可用于显示接收进度(比如统计接收字节数),但不能替代数据读取逻辑。

内容的提问来源于stack exchange,提问作者Syed ZBK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 02:30:34