Delphi网络编程进阶:自定义协议、数据加密与断点续传实战
前三篇我们依次讲解了Delphi Indy组件库的TCP、HTTP/HTTPS、UDP核心通信方式覆盖了基础连接、接口交互、实时传输三大场景。但在实际项目开发中基础通信往往无法满足需求——比如敏感数据传输需要加密、大数据传输需要避免中断、多设备交互需要统一规范这就需要掌握自定义通信协议、数据加密、断点续传三大进阶技能。本文将围绕这三个核心需求结合Delphi实战代码讲解从协议设计、数据加密到断点续传的完整实现流程解决实际开发中的痛点问题让你的网络程序更稳定、更安全、更实用。一、自定义通信协议解决数据传输的“规范与安全”无论是TCP还是UDP基础的字符串、字节流传输缺乏统一规范容易出现数据错乱、丢包误判、解析失败等问题。自定义通信协议就是为数据传输制定“规则”明确数据格式、校验方式、指令含义适用于多设备交互、自定义指令下发、大数据传输等场景。1. 自定义协议设计原则简洁性协议格式尽量简洁减少冗余数据降低传输开销可扩展性预留字段方便后续新增指令、扩展功能可靠性添加校验位、长度字段避免数据错乱、丢包通用性格式统一支持不同设备、不同语言如Delphi、C、Java对接。2. 通用协议格式设计实战版结合实际项目需求设计一套“头部数据校验”的通用协议适用于TCP/UDP双向通信格式如下按字节排列字段名称字节长度作用说明协议头Magic2字节固定标识如0xAA55用于区分协议包避免误解析r2199.cn/zeyf5指令码Cmd1字节指令类型如0x01心跳、0x02数据上报、0x03指令下发数据长度Len2字节后续数据段的长度用于判断数据是否接收完整数据段DataLen字节实际传输的数据字符串、二进制、JSON等校验位CheckSum1字节校验数据完整性采用异或校验避免数据篡改、错乱协议尾End2字节固定标识如0x55AA标志协议包结束协议总长度 212Len12 8 Len 字节Len最大可设为655352字节最大值满足绝大多数数据传输需求。3. 协议封装与解析实战Delphi代码封装协议工具类实现“协议打包”发送时和“协议解析”接收时可直接复用在TCP、UDP项目中。工具类代码Unit_Protocol.pasunit Unit_Protocol; interface uses System.SysUtils, System.Classes, System.Types; // 指令码定义 type TCmdType ( ctHeartBeat $01, // 心跳包 ctDataReport $02, // 数据上报 ctCmdSend $03, // 指令下发 ctReply $04 // 响应包 ); // 协议结构体 type TProtocolPack record Magic: Word; // 协议头 0xAA55 Cmd: Byte; // 指令码 Len: Word; // 数据长度 Data: TBytes; // 数据段 CheckSum: Byte; // 校验位 EndFlag: Word; // 协议尾 0x55AA end; // 协议工具类 type TProtocolTool class public // 打包协议将指令、数据转为协议字节流 class function PackProtocol(Cmd: TCmdType; Data: TBytes): TBytes; // 解析协议将字节流转为协议结构体返回是否解析成功 class function UnpackProtocol(RecvData: TBytes; out Pack: TProtocolPack): Boolean; // 异或校验计算校验位 class function XorCheckSum(Data: TBytes): Byte; end; implementation { TProtocolTool } // 异或校验所有字节异或得到校验位 class function TProtocolTool.XorCheckSum(Data: TBytes): Byte; var I: Integer; begin Result : 0; for I : 0 to Length(Data)-1 do begin Result : Result xor Data[I]; end; end; // 打包协议 class function TProtocolTool.PackProtocol(Cmd: TCmdType; Data: TBytes): TBytes; var Pack: TProtocolPack; Stream: TMemoryStream; begin // 初始化协议字段 Pack.Magic : $AA55; // 固定协议头 Pack.Cmd : Byte(Cmd); // 指令码 Pack.Len : Length(Data); // 数据长度 Pack.Data : Data; // 数据段 // 计算校验位校验指令码数据长度数据段 with TMemoryStream.Create do try WriteBuffer(Pack.Cmd, 1); WriteBuffer(Pack.Len, 2); WriteBuffer(Pack.Data[0], Pack.Len); Position : 0; SetLength(Result, Size); ReadBuffer(Result[0], Size); Pack.CheckSum : XorCheckSum(Result); finally Free; end; Pack.EndFlag : $55AA; // 固定协议尾 // 将协议结构体转为字节流 Stream : TMemoryStream.Create; try Stream.WriteBuffer(Pack.Magic, 2); Stream.WriteBuffer(Pack.Cmd, 1); Stream.WriteBuffer(Pack.Len, 2); if Pack.Len 0 then Stream.WriteBuffer(Pack.Data[0], Pack.Len); Stream.WriteBuffer(Pack.CheckSum, 1); Stream.WriteBuffer(Pack.EndFlag, 2); SetLength(Result, Stream.Size); Stream.Position : 0; Stream.ReadBuffer(Result[0], Stream.Size); finally Stream.Free; end; end; // 解析协议 class function TProtocolTool.UnpackProtocol(RecvData: TBytes; out Pack: TProtocolPack): Boolean; var CheckData: TBytes; CheckSum: Byte; Stream: TMemoryStream; begin Result : False; // 1. 校验数据长度最小8字节协议头2指令1长度2校验1协议尾2 if Length(RecvData) 8 then Exit; Stream : TMemoryStream.Create; try Stream.WriteBuffer(RecvData[0], Length(RecvData)); Stream.Position : 0; // 2. 读取协议头、协议尾校验合法性 Stream.ReadBuffer(Pack.Magic, 2); if Pack.Magic $AA55 then Exit; Stream.ReadBuffer(Pack.Cmd, 1); Stream.ReadBuffer(Pack.Len, 2); // 3. 校验数据长度是否匹配总长度 8 数据长度 if Length(RecvData) 8 Pack.Len then Exit; // 4. 读取数据段 SetLength(Pack.Data, Pack.Len); if Pack.Len 0 then Stream.ReadBuffer(Pack.Data[0], Pack.Len); // 5. 校验校验位 Stream.ReadBuffer(Pack.CheckSum, 1); // 重新计算校验位与接收的校验位对比 SetLength(CheckData, 1 2 Pack.Len); CheckData[0] : Pack.Cmd; Move(Pack.Len, CheckData[1], 2); if Pack.Len 0 then Move(Pack.Data[0], CheckData[3], Pack.Len); CheckSum : XorCheckSum(CheckData); if CheckSum Pack.CheckSum then Exit; // 6. 读取协议尾 Stream.ReadBuffer(Pack.EndFlag, 2); if Pack.EndFlag $55AA then Exit; Result : True; // 所有校验通过解析成功 finally Stream.Free; end; end; end.实战调用示例TCP客户端发送协r2199.cn/9vuyl议包// 引用协议工具类 uses Unit_Protocol; procedure TForm1.btnSendProtocolClick(Sender: TObject); var SendData: TBytes; ProtocolData: TBytes; begin if not IdTCPClient1.Connected then begin Log(未连接服务端); Exit; end; try // 1. 准备要发送的数据示例上报温r2199.cn/itwpl度数据字符串转字节流 SendData : TEncoding.UTF8.GetBytes(温度25℃湿度60%); // 2. 打包协议指r2199.cn/qmvn3令码数据上报 ctDataReport ProtocolData : TProtocolTool.PackProtocol(ctDataReport, SendData); // 3. 发送协议字节流TCP发送 IdTCPClient1.IOHandler.Write(ProtocolData); Log(协议包发送成功长度 IntToStr(Length(ProtocolData))); except on E: Exception do Log(发送失败 E.Message); end; end;实战调用示例TCP服务端解析协议r2199.cn/mngfw包uses Unit_Protocol; procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var RecvBuffer: TBytes; RecvLen: Integer; Pack: TProtocolPack; DataStr: string; begin // 读取协议头2字节判断r2199.cn/zo0fu是否是合法协议包 RecvLen : AContext.Connection.IOHandler.ReadBytes(RecvBuffer, 2, False); if RecvLen 2 then Exit; // 校验r2199.cn/jginu协议头 if TBytesToWord(RecvBuffer) $AA55 then Exit; // 读取剩余数据协议头已读2字节剩余长度6 数据r2199.cn/sxuha长度 // 先读取数据长度2字节 AContext.Connection.IOHandler.ReadBytes(RecvBuffer, 2, True); Pack.Len : TBytesToWord(RecvBuffer); // 读取剩余数据指令1 数据Len 校验1 协议尾2 AContext.Connection.IOHandler.ReadBytes(RecvBuffer, 1 Pack.Len 1 2, True); // 拼接完整协议包协议头2 后续数据 SetLength(RecvBuffer, 2 2 1 Pack.Len 1 2); AContext.Connection.IOHandler.ReadBuffer(RecvBuffer[0], Length(RecvBuffer)); // 解析协议 if TProtocolTool.UnpackProtocol(RecvBuffer, Pack) then begin // 解析成功根据指令码处理数据 DataStr : TEncoding.UTF8.GetString(Pack.Data); case TCmdType(Pack.Cmd) of ctHeartBeat: Log(收到心跳包客户端r2199.cn/kuqlb在线); ctDataReport: Log(收到数据上报 DataStr); ctCmdSend: Log(收到指令下发 DataStr); end; // 回复响应包 SendReply(AContext, ctReply, TEncoding.UTF8.GetBytes(解析成功)); end else begin Log(协议解析失败非法数据包); end; end; // 发送响r2199.cn/72vo8应包 procedure TForm1.SendReply(AContext: TIdContext; Cmd: TCmdType; Data: TBytes); var ProtocolData: TBytes; begin ProtocolData : TProtocolTool.PackProtocol(Cmd, Data); AContext.Connection.IOHandler.Write(ProtocolData); end;二、数据加密保护敏感数据传输安全网络传输中敏感数据如密码、设备信息、交易数据容易被截取、篡改必须进行加密处理。Delphi 提供多种加密方式结合网络编程常用对称加密AES和哈希校验MD5/SHA1前者用于数据加密后者用于数据完整性校验。1. 常用加密方式选择AES加密对称加密加密和解密使用同一密钥速度快、安全性高适合大数据、敏感数据传输推荐使用AES-128MD5/SHA1哈希算法不可逆用于校验数据是否被篡改如密码存储、文件校验Base64编码非加密用于将二进制数据转为字符串避免传输过程中乱码配合加密使用。2. AES加密实战Delphi代码Delphi XE及以上版本自带AES加密单元System.Security.Cryptography无需额外引入第三方组件直接封装工具类用于网络数据加密/解密。AES加密工具类Unit_Encryption.pasunit Unit_Encryption; interface uses System.SysUtils, System.Classes, System.Security.Cryptography, System.EncdDecd; type TAESTool class public // AES加密密钥16位AES-128密钥24位AES-192密钥32位AES-256 class function AESEncrypt(Data: TBytes; Key: string): TBytes; // AES解密 class function AESDecrypt(Data: TBytes; Key: string): TBytes; // MD5校验字符串转MD5 class function MD5Check(Data: string): string; // Base64编码字节流转字符串 class function Base64Encode(Data: TBytes): string; // Base64解码字符串转字节流 class function Base64Decode(Data: string): TBytes; end; implementation { TAESTool } // AES加密 class function TAESTool.AESEncrypt(Data: TBytes; Key: string): TBytes; var AES: TAESCryptoServiceProvider; Encryptor: ICryptoTransform; Stream: TMemoryStream; CryptoStream: TCryptoStream; begin AES : TAESCryptoServiceProvider.Create; try // 设置密钥必须是16/24/32位 AES.Key : TEncoding.UTF8.GetBytes(Key); // 设置初始向量IV与密钥长度一致 AES.IV : Copy(AES.Key, 0, AES.BlockSize); // 加密模式CBC填充模式PKCS7 Encryptor : AES.CreateEncryptor(AES.Key, AES.IV); Stream : TMemoryStream.Create; CryptoStream : TCryptoStream.Create(Stream, Encryptor, ctWrite); try CryptoStream.Write(Data[0], Length(Data)); CryptoStream.FlushFinalBlock; SetLength(Result, Stream.Size); Stream.Position : 0; Stream.ReadBuffer(Result[0], Stream.Size); finally CryptoStream.Free; Stream.Free; end; finally AES.Free; end; end; // AES解密 class function TAESTool.AESDecrypt(Data: TBytes; Key: string): TBytes; var AES: TAESCryptoServiceProvider; Decryptor: ICryptoTransform; Stream: TMemoryStream; CryptoStream: TCryptoStream; BytesRead: Integer; begin AES : TAESCryptoServiceProvider.Create; try AES.Key : TEncoding.UTF8.GetBytes(Key); AES.IV : Copy(AES.Key, 0, AES.BlockSize); Decryptor : AES.CreateDecryptor(AES.Key, AES.IV); Stream : TMemoryStream.Create; CryptoStream : TCryptoStream.Create(Stream, Decryptor, ctRead); try SetLength(Result, Length(Data)); BytesRead : CryptoStream.Read(Result[0], Length(Data)); SetLength(Result, BytesRead); finally CryptoStream.Free; Stream.Free; end; finally AES.Free; end; end; // MD5校验 class function TAESTool.MD5Check(Data: string): string; var MD5: TMD5; begin MD5 : TMD5.Create; try Result : MD5.HashStringAsHex(Data, TEncoding.UTF8); finally MD5.Free; end; end; // Base64编码 class function TAESTool.Base64Encode(Data: TBytes): string; begin Result : EncodeBase64(Data); end; // Base64解码 class function TAESTool.Base64Decode(Data: string): TBytes; begin Result : DecodeBase64(Data); end; end.加密传输实战结合自定义协议实际项目中通常是“加密→打包协议→发送”接收时“解析协议→解密→使用数据”确保敏感数据安全。// 发送端加密打包协议 uses Unit_Protocol, Unit_Encryption; procedure TForm1.btnSendEncryptDataClick(Sender: TObject); var RawData: string; // 原始敏感数据 RawBytes: TBytes; // 原始字节流 EncryptBytes: TBytes; // 加密后字节流 ProtocolBytes: TBytes; // 打包协议后字节流 AESKey: string; // AES密钥16位 begin AESKey : delphi_network_123; // 自定义密钥客户端和服务端必须一致 RawData : 用户密码123456设备ID10001; // 敏感数据 try // 1. 原始数据转字节流 RawBytes : TEncoding.UTF8.GetBytes(RawData); // 2. AES加密 EncryptBytes : TAESTool.AESEncrypt(RawBytes, AESKey); // 3. 打包协议指令码数据上报 ProtocolBytes : TProtocolTool.PackProtocol(ctDataReport, EncryptBytes); // 4. 发送数据TCP IdTCPClient1.IOHandler.Write(ProtocolBytes); Log(加密数据发送成功); except on E: Exception do Log(发送失败 E.Message); end; end; // 接收端解析协议解密 procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var RecvBuffer: TBytes; Pack: TProtocolPack; EncryptBytes: TBytes; DecryptBytes: TBytes; RawData: string; AESKey: string; begin AESKey : delphi_network_123; // 与客户端密钥一致 // 读取并解析协议包省略协议读取逻辑参考前文 if TProtocolTool.UnpackProtocol(RecvBuffer, Pack) then begin if TCmdType(Pack.Cmd) ctDataReport then begin // 1. 获取加密后的数据段 EncryptBytes : Pack.Data; // 2. AES解密 DecryptBytes : TAESTool.AESDecrypt(EncryptBytes, AESKey); // 3. 解密后数据转字符串 RawData : TEncoding.UTF8.GetString(DecryptBytes); Log(解密后数据 RawData); end; end; end;三、断点续传解决大数据传输中断问题在HTTP文件下载、TCP大数据传输如视频、固件、大文件中网络中断、程序重启会导致传输失败需要重新传输效率极低。断点续传的核心是“记录已传输进度”中断后从已传输位置继续传输节省时间和带宽。本文以HTTP断点续传最常用场景为例结合TIdHTTP组件实现TCP断点续传逻辑类似可参考扩展。1. 断点续传原理客户端下载前先获取文件总大小若文件已部分下载读取已下载大小向服务器发送“Range”请求头请求从已下载位置开始传输服务器响应“206 Partial Content”返回剩余部分数据客户端将剩余数据追加到已下载文件中直至传输完成。2. HTTP断点续传实战Delphi代码实现一个带进度条的HTTP断点续传工具支持暂停、继续下载显示下载进度和速度。界面设计1个TIdHTTP组件IdHTTP11个TEditedtUrl填写文件下载地址1个TEditedtSavePath填写保存路径2个TButtonbtnStart开始下载btnPause暂停下载1个TProgressBarprogressBar1显示下载进度1个TLabellblProgress显示进度百分比、速度1个TThread自定义下载线程避免界面卡死。