在 Delphi 开发中,网络通信 是桌面应用、服务端程序、物联网设备交互的核心需求。Indy(Internet Direct)组件库是 Delphi 内置的老牌网络开发工具包,基于阻塞式套接字实现,封装了 TCP、UDP、HTTP 等主流网络协议,其中 TIdTCPClient(TCP 客户端) 和 TIdTCPServer(TCP 服务端) 是实现自定义 TCP 通信的核心类,也是 Delphi 网络编程的基础。
本文将从零讲解这两个核心类的用法、关键属性 / 方法、线程安全机制,并提供完整的客户端 + 服务端通信实战代码。
一、Indy 组件库基础
Indy 是 Delphi 自带的跨平台网络组件库,无需额外安装,直接在组件面板的Indy Clients 和Indy Servers分类中调用:
- TIdTCPClient:客户端组件,用于主动连接服务端、发送 / 接收数据;
- TIdTCPServer:服务端组件,用于监听端口、接受客户端连接、处理多客户端通信;
- 核心特性:阻塞式通信(数据收发时会阻塞当前线程,需配合线程避免界面卡死)、支持多客户端连接、内置数据编码 / 解码、异常捕获机制。
适用场景:自定义协议通信、设备控制、内网数据传输、服务端监听等。
二、TIdTCPClient 客户端核心详解
1. 关键属性
表格
| 属性名 | 作用 |
|---|---|
| Host | 目标服务端的 IP 地址(如 127.0.0.1 本地回环、192.168.1.100 内网 IP) |
| Port | 目标服务端的监听端口(需与服务端一致,范围 1~65535) |
| Connected | 只读属性,判断是否与服务端建立连接(True = 已连接,False = 未连接) |
| ReadTimeout | 数据读取超时时间(毫秒),避免无限阻塞 |
2. 核心方法
Connect:主动连接服务端,失败会抛出异常(需用try...except捕获);Disconnect:断开与服务端的连接,释放套接字资源;IOHandler.Write:发送数据(支持字符串、字节流、整数等类型);IOHandler.ReadString:读取指定长度的字符串数据;IOHandler.ReadLn:按行读取数据(以换行符\n为结束标记)。
三、TIdTCPServer 服务端核心详解
1. 关键属性
表格
| 属性名 | 作用 |
|---|---|
| DefaultPort | 服务端监听的端口(客户端需通过此端口连接) |
| Active | 启动 / 停止服务端(True = 启动监听,False = 停止监听) |
| Bindings | 绑定监听的 IP(默认 0.0.0.0 监听所有网卡,可指定内网 IP) |
| Contexts | 存储所有已连接的客户端上下文(用于遍历在线客户端) |
2. 核心事件(服务端核心逻辑)
OnConnect:客户端成功连接时触发,可记录客户端信息;OnDisconnect:客户端断开连接时触发,清理资源;OnExecute:核心事件,客户端发送数据时触发,用于接收 / 处理数据、回复客户端;OnException:捕获通信过程中的异常,避免服务端崩溃。
3. 线程安全机制
TIdTCPServer 内置多线程管理:每一个客户端连接都会分配一个独立线程(TIdContext),所有客户端通信互不干扰,无需手动创建线程,这是 Indy 最实用的特性。
四、实战:Delphi TCP 客户端 + 服务端完整实现
环境准备
- Delphi 7/XE/10.X 版本(本文以 Delphi 10.4 为例);
- 新建两个 VCL 工程:TCPClientDemo(客户端) 、TCPServerDemo(服务端)。
第一部分:服务端开发(TCPServerDemo)
1. 界面设计
- 1 个
TMemo(名称:memoLog,显示日志); - 2 个
TButton(btnStart = 启动服务,btnStop = 停止服务); - 1 个
TIdTCPServer(名称:IdTCPServer1,拖入界面即可)。
2. 完整代码
delphi
unit ServerMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.IdTCPServer,
IdContext, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPServer;
type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
memoLog: TMemo;
btnStart: TButton;
btnStop: TButton;
procedure btnStartClick(Sender: TObject);
procedure btnStopClick(Sender: TObject);
procedure IdTCPServer1Connect(AContext: TIdContext);
procedure IdTCPServer1Disconnect(AContext: TIdContext);
procedure IdTCPServer1Execute(AContext: TIdContext);
procedure IdTCPServer1Exception(AContext: TIdContext; AException: Exception);
private
{ Private declarations }
procedure Log(Msg: string); // 日志输出方法
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// 日志输出(线程安全,避免界面卡死)
procedure TForm1.Log(Msg: string);
begin
TThread.Synchronize(nil, procedure
begin
memoLog.Lines.Add(FormatDateTime('yyyy-mm-dd hh:MM:ss', Now) + ':' + Msg);
end);
end;
// 启动服务
procedure TForm1.btnStartClick(Sender: TObject);
begin
if IdTCPServer1.Active then Exit;
IdTCPServer1.DefaultPort := 8888; // 监听端口8888
IdTCPServer1.Active := True;
Log('服务已启动,监听端口:8888');
end;
// 停止服务
procedure TForm1.btnStopClick(Sender: TObject);
begin
IdTCPServer1.Active := False;
Log('服务已停止');
end;
// 客户端连接事件
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
var
ClientIP: string;
begin
// 获取客户端IP和端口
ClientIP := AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort);
Log('客户端连接:' + ClientIP);
end;
// 客户端断开事件
procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
var
ClientIP: string;
begin
ClientIP := AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort);
Log('客户端断开:' + ClientIP);
end;
// 核心:接收客户端数据并回复
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
RecvStr: string;
ClientIP: string;
begin
// 按行读取客户端发送的数据
RecvStr := AContext.Connection.IOHandler.ReadLn;
ClientIP := AContext.Binding.PeerIP;
Log('收到['+ClientIP+']数据:' + RecvStr);
// 回复客户端
AContext.Connection.IOHandler.WriteLn('服务端已收到:' + RecvStr);
end;
// 异常捕获
procedure TForm1.IdTCPServer1Exception(AContext: TIdContext; AException: Exception);
begin
Log('通信异常:' + AException.Message);
end;
end.
第二部分:客户端开发(TCPClientDemo)
1. 界面设计
- 1 个
TMemo(memoLog,显示通信日志); - 1 个
TEdit(edtSend,输入发送内容); - 3 个
TButton(btnConnect = 连接服务端,btnSend = 发送数据,btnDisconnect = 断开连接); - 1 个
TIdTCPClient(IdTCPClient1,拖入界面)。
2. 完整代码
delphi
unit ClientMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient;
type
TForm1 = class(TForm)
IdTCPClient1: TIdTCPClient;
memoLog: TMemo;
edtSend: TEdit;
btnConnect: TButton;
btnSend: TButton;
btnDisconnect: TButton;
procedure btnConnectClick(Sender: TObject);
procedure btnSendClick(Sender: TObject);
procedure btnDisconnectClick(Sender: TObject);
private
{ Private declarations }
procedure Log(Msg: string);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// 日志输出
procedure TForm1.Log(Msg: string);
begin
memoLog.Lines.Add(FormatDateTime('yyyy-mm-dd hh:MM:ss', Now) + ':' + Msg);
end;
// 连接服务端
procedure TForm1.btnConnectClick(Sender: TObject);
begin
if IdTCPClient1.Connected then Exit;
try
IdTCPClient1.Host := '127.0.0.1'; // 服务端IP(本地测试用回环地址)
IdTCPClient1.Port := 8888; // 服务端端口
IdTCPClient1.ReadTimeout := 5000; // 超时5秒
IdTCPClient1.Connect;
Log('成功连接服务端');
except
on E: Exception do
Log('连接失败:' + E.Message);
end;
end;
// 发送数据
procedure TForm1.btnSendClick(Sender: TObject);
var
SendStr, RecvStr: string;
begin
if not IdTCPClient1.Connected then
begin
Log('未连接服务端');
Exit;
end;
if Trim(edtSend.Text) = '' then
begin
Log('发送内容不能为空');
Exit;
end;
try
SendStr := edtSend.Text;
// 发送数据(按行发送,与服务端ReadLn对应)
IdTCPClient1.IOHandler.WriteLn(SendStr);
Log('发送数据:' + SendStr);
// 读取服务端回复
RecvStr := IdTCPClient1.IOHandler.ReadLn;
Log('收到回复:' + RecvStr);
except
on E: Exception do
Log('发送失败:' + E.Message);
end;
end;
// 断开连接
procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
if IdTCPClient1.Connected then
begin
IdTCPClient1.Disconnect;
Log('已断开服务端连接');
end;
end;
end.
tm0.cn/gokdl
tm0.cn/gtzo7
tm0.cn/ng6bi
tm0.cn/wm7oe
tm0.cn/hmax3
tm0.cn/uxune
五、运行测试步骤
- 先编译运行tm0.cn/hmax3服务端程序 ,点击【启动服务】,日志显示
服务已启动,监听端口:8888; - 再编译运行tm0.cn/uxune客户端程序 ,点击【连接服务端】,日志显示
成功连接服务端; - 客户端输入内容(如
Hello Delphi TCP),点击【发送数据】; - 服务端会收到客户端数据并回复,客户端同时显示服务端的回复信息;
- 测试多客户端:打开多个客户端程序,同时连接服务端,服务端可正常处理所有客户端通信。
六、关键注意事项
- 端口占用 :确保 8888 端口未被其他程序占用,可在 cmd 中用
netstat -ano查看; - 防火墙:内网通信时,关闭服务端防火墙或放行 8888 端口;
- 阻塞问题 :客户端数据收发会阻塞主线程,正式项目需将通信逻辑放入子线程,避免界面卡死;
- 数据编码 :传输中文时,需指定编码(如
IdTCPClient1.IOHandler.DefStringEncoding := TEncoding.UTF8),避免乱码; - 异常处理:网络通信必须捕获异常(断开、超时、网络错误),保证程序稳定性。
七、扩展与进阶
- UDP 通信:Indy 提供 TIdUDPClient/TIdUDPServer,用法与 TCP 类似,适用于实时性高、不要求可靠性的场景;
- 数据分包:大数据传输时,需自定义协议分包发送(如添加数据长度、校验位);
- SSL 加密:Indy 支持 TIdSSLIOHandlerSocketOpenSSL 实现加密通信,适用于敏感数据传输;
- HTTP 通信:TIdHTTP 组件快速实现 GET/POST 请求,对接 Web API。
总结
TIdTCPClient 和 TIdTCPServer 是 Delphi 网络编程的基石 ,依托 Indy 组件库的封装,无需深入底层套接字编程,即可快速实现稳定的 TCP 通信。本文的实战代码可直接用于项目开发,核心掌握连接、收发数据、事件处理、异常捕获四大要点tm0.cn/dltba就能应对绝大多数 Delphi 网络开发需求。
对于新手而言,先吃透 TCP 基础通信,再逐步扩展 UDP、HTTP、SSL 等高级功能,是学习 Delphi 网络编程的最佳路径。