Delphi网络编程综合实战:多协议网络工具开发(TCP/UDP/HTTP三合一)
前四篇我们分别讲解了Delphi网络编程的核心组件(TCP、UDP、HTTP)、进阶技能(自定义协议、数据加密、断点续传),掌握了单个模块的用法,但实际项目中,往往需要整合多种网络协议,实现多场景适配。
本文将以「多协议网络工具」为实战案例,整合TCP客户端/服务端、UDP客户端/服务端、HTTP请求/断点下载三大核心功能,复用前序文章的工具类(协议、加密),完整实现从界面设计、代码编写、功能测试到异常处理的全流程,帮助你打通知识点,提升综合开发能力,最终实现一个可直接使用的桌面端网络工具。
工具核心功能:支持TCP双向通信、UDP单播/广播、HTTP GET/POST请求、HTTP断点续传,内置数据加密、日志记录、异常处理,适配日常开发调试、设备通信测试等场景。
一、项目需求与整体设计
1. 核心需求
-
TCP模块:支持TCP客户端(连接、发送/接收数据)、TCP服务端(监听、多客户端连接、数据转发);
-
UDP模块:支持UDP客户端(单播/广播发送、接收数据)、UDP服务端(监听、回复数据);
-
HTTP模块:支持GET/POST请求(普通请求、JSON提交)、HTTP断点续传(下载文件、暂停/继续);
-
通用功能:数据加密(AES)、自定义协议、日志记录、异常提示、界面无卡死(多线程);
-
易用性:分标签页展示不同功能,操作简洁,支持参数配置(IP、端口、密钥等)。
2. 整体架构设计
采用「分层设计+模块化封装」,降低耦合度,方便后续扩展,架构分为3层:
-
UI层:主窗体(标签页)、各功能模块界面(TCP、UDP、HTTP)、控件交互;
-
业务逻辑层:各协议的核心逻辑(TCP通信、UDP通信、HTTP请求/下载)、线程管理;
-
工具层:复用前序文章的协议工具类(Unit_Protocol.pas)、加密工具类(Unit_Encryption.pas),新增日志工具类。
3. 环境准备
-
Delphi版本:Delphi XE及以上(本文以Delphi 10.4为例);
-
依赖组件:Indy组件库(自带,无需额外安装)、System.JSON(JSON解析)、System.Security.Cryptography(AES加密);
-
工具类依赖:Unit_Protocol.pas(自定义协议)、Unit_Encryption.pas(数据加密),需提前准备(参考上一篇文章)。
二、界面设计(核心部分)
主窗体采用标签页(TTabControl),分为4个标签页:TCP模块、UDP模块、HTTP请求、HTTP下载,每个标签页对应一个功能模块,界面简洁易用,控件命名规范,便于代码编写。
1. 主窗体整体布局
-
1个TTabControl(tabMain):4个标签页(tabTCP、tabUDP、tabHTTPReq、tabHTTPDownload);
-
1个TMemo(memoLog):全局日志显示,记录所有操作、通信数据、异常信息;
-
1个TButton(btnClearLog):清空日志;
-
公共配置区:AES密钥输入框(edtAESKey)、协议开关(chkUseProtocol),控制是否启用自定义协议和加密。
2. TCP模块界面(tabTCP)
-
客户端区域:IP输入框、端口输入框、连接/断开按钮、发送内容输入框、发送按钮;
-
服务端区域:监听端口输入框、启动/停止服务按钮;
-
数据显示区域:接收数据显示框(TMemo)。
3. UDP模块界面(tabUDP)
-
客户端区域:目标IP、目标端口、发送内容、单播发送/广播发送按钮;
-
服务端区域:监听端口、启动/停止监听按钮;
-
数据显示区域:接收数据显示框(TMemo)。
4. HTTP请求界面(tabHTTPReq)
-
请求配置:请求地址(URL)、请求方式(GET/POST,TRadioGroup);
-
请求参数:GET参数输入框、POST参数(普通表单/JSON,切换按钮);
-
操作按钮:发送请求、清空响应;
-
响应显示:响应内容、响应码、响应时间(TMemo+TLabel)。
5. HTTP下载界面(tabHTTPDownload)
-
下载配置:下载地址(URL)、保存路径(输入框+浏览按钮);
-
操作按钮:开始下载、暂停/继续下载、停止下载;
-
进度显示:进度条(TProgressBar)、进度信息(下载速度、已下载/总大小)。
三、核心代码实现(分模块)
代码采用模块化编写,每个功能模块单独封装方法,复用前序工具类,重点实现多线程、多协议整合、异常处理,确保工具稳定运行。
1. 日志工具类(新增,Unit_Log.pas)
封装全局日志记录方法,支持线程安全,统一日志格式,便于调试和问题排查。
unit Unit_Log; interface uses System.SysUtils, System.Classes, Vcl.StdCtrls; type TLogTool = class public // 记录日志(线程安全,支持传入Memo控件显示) class procedure Log(Msg: string; Memo: TMemo); // 清空日志 class procedure ClearLog(Memo: TMemo); end; implementation { TLogTool } class procedure TLogTool.ClearLog(Memo: TMemo); begin TThread.Synchronize(nil, procedure begin Memo.Clear; end); end; class procedure TLogTool.Log(Msg: string; Memo: TMemo); var LogStr: string; begin LogStr := FormatDateTime('yyyy-mm-dd hh:MM:ss.zzz', Now) + ' | ' + Msg; TThread.Synchronize(nil, procedure begin Memo.Lines.Add(LogStr); // 自动滚动到最后一行 Memo.SelStart := Length(Memo.Text); Memo.Perform(EM_SCROLLCARET, 0, 0); end); end; end.
2. 主窗体核心代码(MainForm.pas)
主窗体整合所有功能sccla.cn/pacy7模块,实现控件交互、线程管理、功能调用,核心代码如下(重点展示多协议整合逻辑,完整代码可参考注释补充)。
unit MainForm; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.Buttons, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdTCPServer, IdUDPBase, IdUDPClient, IdUDPServer, IdSocketHandle, IdHTTP, Unit_Protocol, Unit_Encryption, Unit_Log; type // HTTP下载线程(复用前一篇断点续传逻辑,略作修改) THTTPDownloadThread = class(TThread) private FUrl, FSavePath: string; FTotalSize, FDownloadedSize: Int64; FIsPaused, FIsStopped: Boolean; FMemo: TMemo; FProgressBar: TProgressBar; FProgressLabel: TLabel; procedure UpdateProgress; function GetFileTotalSize: Int64; protected procedure Execute; override; public constructor Create(Url, SavePath: string; Memo: TMemo; ProgressBar: TProgressBar; ProgressLabel: TLabel); procedure PauseOrResume; procedure Stop; property IsPaused: Boolean read FIsPaused; end; TForm_Main = class(TForm) tabMain: TTabControl; tabTCP: TTabSheet; tabUDP: TTabSheet; tabHTTPReq: TTabSheet; tabHTTPDownload: TTabSheet; memoLog: TMemo; btnClearLog: TButton; edtAESKey: TEdit; Label1: TLabel; chkUseProtocol: TCheckBox; Label2: TLabel; // TCP客户端控件 edtTCPClientIP: TEdit; edtTCPClientPort: TEdit; btnTCPClientConnect: TButton; btnTCPClientDisconnect: TButton; edtTCPClientSend: TEdit; btnTCPClientSend: TButton; memoTCPClientRecv: TMemo; // TCP服务端控件 edtTCPServerPort: TEdit; btnTCPServerStart: TButton; btnTCPServerStop: TButton; memoTCPServerRecv: TMemo; IdTCPClient1: TIdTCPClient; IdTCPServer1: TIdTCPServer; // UDP客户端控件 edtUDPClientIP: TEdit; edtUDPClientPort: TEdit; edtUDPClientSend: TEdit; btnUDPClientSend: TButton; btnUDPClientBroadcast: TButton; memoUDPClientRecv: TMemo; // UDP服务端控件 edtUDPServerPort: TEdit; btnUDPServerStart: TButton; btnUDPServerStop: TButton; memoUDPServerRecv: TMemo; IdUDPClient1: TIdUDPClient; IdUDPServer1: TIdUDPServer; // HTTP请求控件 edtHTTPUrl: TEdit; rgHTTPMethod: TRadioGroup; edtHTTPGetParam: TEdit; edtHTTPPostParam: TEdit; btnHTTPPostType: TButton; btnHTTPSend: TButton; btnHTTPClear: TButton; memoHTTPResponse: TMemo; lblHTTPResponseCode: TLabel; lblHTTPResponseTime: TLabel; IdHTTP1: TIdHTTP; // HTTP下载控件 edtHTTPDownloadUrl: TEdit; edtHTTPDownloadSavePath: TEdit; btnHTTPDownloadBrowse: TButton; btnHTTPDownloadStart: TButton; btnHTTPDownloadPause: TButton; btnHTTPDownloadStop: TButton; progressBarHTTPDownload: TProgressBar; lblHTTPDownloadProgress: TLabel; OpenDialog1: TOpenDialog; procedure btnClearLogClick(Sender: TObject); // TCP相关事件 procedure btnTCPClientConnectClick(Sender: TObject); procedure btnTCPClientDisconnectClick(Sender: TObject); procedure btnTCPClientSendClick(Sender: TObject); procedure btnTCPServerStartClick(Sender: TObject); procedure btnTCPServerStopClick(Sender: TObject); procedure IdTCPServer1Connect(AContext: TIdContext); procedure IdTCPServer1Disconnect(AContext: TIdContext); procedure IdTCPServer1Execute(AContext: TIdContext); procedure IdTCPServer1Exception(AContext: TIdContext; AException: Exception); // UDP相关事件 procedure btnUDPClientSendClick(Sender: TObject); procedure btnUDPClientBroadcastClick(Sender: TObject); procedure btnUDPServerStartClick(Sender: TObject); procedure btnUDPServerStopClick(Sender: TObject); procedure IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TBytes; ABinding: TIdSocketHandle); // HTTP请求相关事件 procedure btnHTTPSendClick(Sender: TObject); procedure btnHTTPClearClick(Sender: TObject); procedure btnHTTPPostTypeClick(Sender: TObject); // HTTP下载相关事件 procedure btnHTTPDownloadBrowseClick(Sender: TObject); procedure btnHTTPDownloadStartClick(Sender: TObject); procedure btnHTTPDownloadPauseClick(Sender: TObject); procedure btnHTTPDownloadStopClick(Sender: TObject); private { Private declarations } HTTPDownloadThread: THTTPDownloadThread; FHTTPPostIsJSON: Boolean; // POST请求是否为JSON格式 // 通用方法:数据处理(加密+协议打包) function ProcessSendData(Data: string): TBytes; // 通用方法:数据解析(协议解析+解密) function ProcessRecvData(Data: TBytes): string; public { Public declarations } end; var Form_Main: TForm_Main; implementation {$R *.dfm} { TForm_Main } // 通用方法:发送数据处理(根据配置,决定是否加密、是否打包协议) function TForm_Main.ProcessSendData(Data: string): TBytes; var RawBytes, EncryptBytes, ProtocolBytes: TBytes; AESKey: string; begin RawBytes := TEncoding.UTF8.GetBytes(Data); AESKey := Trim(edtAESKey.Text); // 1. 加密(若密钥不为空) if AESKey <> '' then EncryptBytes := TAESTool.AESEncrypt(RawBytes, AESKey) else EncryptBytes := RawBytes; // 2. 打包协议(若启用协议) if chkUseProtocol.Checked then ProtocolBytes := TProtocolTool.PackProtocol(ctDataReport, EncryptBytes) else ProtocolBytes := EncryptBytes; Result := ProtocolBytes; end; // 通用方法:接收数据处理(协议解析+解密) function TForm_Main.ProcessRecvData(Data: TBytes): string; var DecryptBytes, ProtocolData: TBytes; Pack: TProtocolPack; AESKey: string; begin Result := ''; AESKey := Trim(edtAESKey.Text); // 1. 解析协议(若启用协议) if chkUseProtocol.Checked then begin if TProtocolTool.UnpackProtocol(Data, Pack) then ProtocolData := Pack.Data else begin TLogTool.Log('协议解析失败', memoLog); Exit; end; end else begin ProtocolData := Data; end; // 2. 解密(若密钥不为空) if AESKey <> '' then DecryptBytes := TAESTool.AESDecrypt(ProtocolData, AESKey) else DecryptBytes := ProtocolData; // 3. 转字符串 Result := TEncoding.UTF8.GetString(DecryptBytes); end; // 清空日志 procedure TForm_Main.btnClearLogClick(Sender: TObject); begin TLogTool.ClearLog(memoLog); end; { TCP客户端相关 } procedure TForm_Main.btnTCPClientConnectClick(Sender: TObject); var IP: string; Port: Integer; begin IP := Trim(edtTCPClientIP.Text); if not TryStrToInt(Trim(edtTCPClientPort.Text), Port) then begin TLogTool.Log('TCP客户端:端口格式错误', memoLog); Exit; end; try if IdTCPClient1.Connected then Exit; IdTCPClient1.Host := IP; IdTCPClient1.Port := Port; IdTCPClient1.ReadTimeout := 5000; IdTCPClient1.Connect; TLogTool.Log(Format('TCP客户端:成功连接 %s:%d', [IP, Port]), memoLog); btnTCPClientConnect.Enabled := False; btnTCPClientDisconnect.Enabled := True; btnTCPClientSend.Enabled := True; except on E: Exception do TLogTool.Log(Format('TCP客户端:连接失败 - %s', [E.Message]), memoLog); end; end; procedure TForm_Main.btnTCPClientDisconnectClick(Sender: TObject); begin if IdTCPClient1.Connected then begin IdTCPClient1.Disconnect; TLogTool.Log('TCP客户端:已断开连接', memoLog); btnTCPClientConnect.Enabled := True; btnTCPClientDisconnect.Enabled := False; btnTCPClientSend.Enabled := False; end; end; procedure TForm_Main.btnTCPClientSendClick(Sender: TObject); var SendStr: string; SendData: TBytes; begin SendStr := Trim(edtTCPClientSend.Text); if SendStr = '' then begin TLogTool.Log('TCP客户端:发送内容不能为空', memoLog); Exit; end; if not IdTCPClient1.Connected then begin TLogTool.Log('TCP客户端:未连接服务端', memoLog); Exit; end; try // 处理发送数据(加密+协议) SendData := ProcessSendData(SendStr); // 发送数据 IdTCPClient1.IOHandler.Write(SendData); TLogTool.Log(Format('TCP客户端:发送数据 - %s', [SendStr]), memoLog); edtTCPClientSend.Clear; // 接收服务端回复(阻塞式,实际项目可放入子线程) if IdTCPClient1.IOHandler.CheckForDataOnSource(5000) then begin var RecvData := IdTCPClient1.IOHandler.ReadBytes(-1); var RecvStr := ProcessRecvData(RecvData); TLogTool.Log(Format('TCP客户端:收到回复 - %s', [RecvStr]), memoLog); memoTCPClientRecv.Lines.Add(RecvStr); end; except on E: Exception do TLogTool.Log(Format('TCP客户端:发送失败 - %s', [E.Message]), memoLog); end; end; { TCP服务端相关 } procedure TForm_Main.btnTCPServerStartClick(Sender: TObject); var Port: Integer; begin if not TryStrToInt(Trim(edtTCPServerPort.Text), Port) then begin TLogTool.Log('TCP服务端:端口格式错误', memoLog); Exit; end; try if IdTCPServer1.Active then Exit; IdTCPServer1.DefaultPort := Port; IdTCPServer1.ThreadedEvent := True; IdTCPServer1.Active := True; TLogTool.Log(Format('TCP服务端:已启动,监听端口 %d', [Port]), memoLog); btnTCPServerStart.Enabled := False; btnTCPServerStop.Enabled := True; except on E: Exception do TLogTool.Log(Format('TCP服务端:启动失败 - %s', [E.Message]), memoLog); end; end; procedure TForm_Main.btnTCPServerStopClick(Sender: TObject); begin if IdTCPServer1.Active then begin IdTCPServer1.Active := False; TLogTool.Log('TCP服务端:已停止', memoLog); btnTCPServerStart.Enabled := True; btnTCPServerStop.Enabled := False; end; end; procedure TForm_Main.IdTCPServer1Connect(AContext: TIdContext); var ClientIP: string; begin ClientIP := AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort); TLogTool.Log(Format('TCP服务端:客户端连接 - %s', [ClientIP]), memoLog); end; procedure TForm_Main.IdTCPServer1Disconnect(AContext: TIdContext); var ClientIP: string; begin ClientIP := AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.PeerPort); TLogTool.Log(Format('TCP服务端:客户端断开 - %s', [ClientIP]), memoLog); end; procedure TForm_Main.IdTCPServer1Execute(AContext: TIdContext); var RecvData: TBytes; RecvStr, ClientIP: string; SendData: TBytes; begin try // 读取数据(根据协议判断是否读取完整) if chkUseProtocol.Checked then begin // 读取协议头(2字节) RecvData := AContext.Connection.IOHandler.ReadBytes(2, False); if Length(RecvData) < 2 then Exit; if TBytesToWord(RecvData) <> $AA55 then Exit; // 读取数据长度(2字节) AContext.Connection.IOHandler.ReadBytes(RecvData, 2, True); var DataLen := TBytesToWord(RecvData); // 读取剩余sccla.cn/ducde数据 AContext.Connection.IOHandler.ReadBytes(RecvData, 1 + DataLen + 1 + 2, True); SetLength(RecvData, 2 + 2 + 1 + DataLen + 1 + 2); AContext.Connection.IOHandler.ReadBuffer(RecvData[0], Length(RecvData)); end else begin // 非协议模式,读取所有数据 RecvData := AContext.Connection.IOHandler.ReadBytes(-1); end; // 解析数据 RecvStr := ProcessRecvData(RecvData); ClientIP := AContext.Binding.PeerIP; TLogTool.Log(Format('TCP服务端:收到[%s]数据 - %s', [ClientIP, RecvStr]), memoLog); memoTCPServerRecv.Lines.Add(Format('[%s] %s', [ClientIP, RecvStr])); // 回复客户端 var ReplyStr := '服务端已收到:' + RecvStr; SendData := ProcessSendData(ReplyStr); AContext.Connection.IOHandler.Write(SendData); except on E: Exception do TLogTool.Log(Format('TCP服务端:处理异常 - %s', [E.Message]), memoLog); end; end; procedure TForm_Main.IdTCPServer1Exception(AContext: TIdContext; AException: Exception); begin TLogTool.Log(Format('TCP服务端:通信异常 - %s', [AException.Message]), memoLog); end; { UDP相关代码(省略部分重复逻辑,核心与前一篇一致,整合通用数据处理方法) } procedure TForm_Main.btnUDPClientSendClick(Sender: TObject); var IP: string; Port: Integer; SendStr: string; SendData: TBytes; begin IP := Trim(edtUDPClientIP.Text); if not TryStrToInt(Trim(edtUDPClientPort.Text), Port) then begin TLogTool.Log('UDP客户端:端口格式错误', memoLog); Exit; end; SendStr := Trim(edtUDPClientSend.Text); if SendStr = '' then begin TLogTool.Log('UDP客户端:发送内容不能为空', memoLog); Exit; end; try IdUDPClient1.Host := IP; IdUDPClient1.Port := Port; IdUDPClient1.ReceiveTimeout := 3000; // 处理发送数据 SendData := ProcessSendData(SendStr); IdUDPClient1.SendBuffer(SendData, Length(SendData)); TLogTool.Log(Format('UDP客户端:单播发送至[%s:%d] - %s', [IP, Port, SendStr]), memoLog); // 接收回复 var RecvData: TBytes; var PeerIP: string; var PeerPort: Integer; if IdUDPClient1.ReceiveBuffer(RecvData, 1024*10, PeerIP, PeerPort) > 0 then begin var RecvStr := ProcessRecvData(RecvData); TLogTool.Log(Format('UDP客户端:收到[%s:%d]回复 - %s', [PeerIP, PeerPort, RecvStr]), memoLog); memoUDPClientRecv.Lines.Add(RecvStr); end; except on E: Exception do TLogTool.Log(Format('UDP客户端:发送失败 - %s', [E.Message]), memoLog); end; end; // UDP广播、UDP服务端相关代码(略,核心逻辑参考前一篇,整合ProcessSendData/ProcessRecvData方法) procedure TForm_Main.btnUDPClientBroadcastClick(Sender: TObject); begin // 代码略,启用BroadcastEnabled,发送广播报文,调用ProcessSendData处理数据 end; procedure TForm_Main.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TBytes; ABinding: TIdSocketHandle); begin // 代码略,调用ProcessRecvData解析数据,处理后回复客户端 end; { HTTP请求相关代码 } procedure TForm_Main.btnHTTPPostTypeClick(Sender: TObject); begin // 切换POST请求类型(普通表单/JSON) FHTTPPostIsJSON := not FHTTPPostIsJSON; if FHTTPPostIsJSON then begin btnHTTPPostType.Caption := 'JSON格式'; IdHTTP1.Request.ContentType := 'application/json'; end else begin btnHTTPPostType.Caption := '表单格式'; IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded'; end; end; procedure TForm_Main.btnHTTPSendClick(Sender: TObject); var URL, ParamStr, ResponseStr: string; StartTime, EndTime: Cardinal; Stream: TStringStream; begin URL := Trim(edtHTTPUrl.Text); if URL = '' then begin TLogTool.Log('HTTP请求:请填写请求地址', memoLog); Exit; end; StartTime := GetTickCount; // 记录开sccla.cn/sywmb始时间 try IdHTTP1.ConnectTimeout := 5000; IdHTTP1.ReadTimeout := 10000; IdHTTP1.Request.CharSet := 'UTF-8'; if rgHTTPMethod.ItemIndex = 0 then begin // GET请求:拼接参数 ParamStr := Trim(edtHTTPGetParam.Text); if ParamStr <> '' then URL := URL + '?' + ParamStr; ResponseStr := IdHTTP1.Get(URL); end else begin // POST请求:根据类型提交数据 ParamStr := Trim(edtHTTPPostParam.Text); Stream := TStringStream.Create(ParamStr, TEncoding.UTF8); try ResponseStr := IdHTTP1.Post(URL, Stream); finally Stream.Free; end; end; // 计算响应时间 EndTime := GetTickCount; var ResponseTime := (EndTime - StartTime) / 1000; // 显示结果 memoHTTPResponse.Text := ResponseStr; lblHTTPResponseCode.Caption := Format('响应码:%d', [IdHTTP1.Response.ResponseCode]); lblHTTPResponseTime.Caption := Format('响应时间:%.2f秒', [ResponseTime]); TLogTool.Log(Format('HTTP请求:%s请求成功,响应码:%d', [rgHTTPMethod.Text, IdHTTP1.Response.ResponseCode]), memoLog); except on E: Exception do begin TLogTool.Log(Format('HTTP请求:失败 - %s', [E.Message]), memoLog); if IdHTTP1.Response <> nil then lblHTTPResponseCode.Caption := Format('响应码:%d', [IdHTTP1.Response.ResponseCode]); end; end; end; procedure TForm_Main.btnHTTPClearClick(Sender: TObject); begin memoHTTPResponse.Clear; lblHTTPResponseCode.Caption := '响应码:'; lblHTTPResponseTime.Caption := '响应时间:'; end; { HTTP下载相关代码(THTTPDownloadThread实现略,复用前一篇断点续传逻辑,整合日志工具) } procedure TForm_Main.btnHTTPDownloadBrowseClick(Sender: TObject); begin if OpenDialog1.Execute then begin edtHTTPDownloadSavePath.Text := OpenDialog1.FileName; end; end; procedure TForm_Main.btnHTTPDownloadStartClick(Sender: TObject); var URL, SavePath: string; begin URL := Trim(edtHTTPDownloadUrl.Text); SavePath := Trim(edtHTTPDownloadSavePath.Text); if (URL = '') or (SavePath = '') then begin TLogTool.Log('HTTP下载:请填写下载地址和保存路径', memoLog); Exit; end; // 停止已有线程 if Assigned(HTTPDownloadThread) then begin HTTPDownloadThread.Stop; HTTPDownloadThread.WaitFor; end; // 创建并启动下sccla.cn/xuf1d载线程 HTTPDownloadThread := THTTPDownloadThread.Create(URL, SavePath, memoLog, progressBarHTTPDownload, lblHTTPDownloadProgress); HTTPDownloadThread.Start; TLogTool.Log('HTTP下载:开始下载', memoLog); btnHTTPDownloadPause.Enabled := True; btnHTTPDownloadStop.Enabled := True; end; procedure TForm_Main.btnHTTPDownloadPauseClick(Sender: TObject); begin if Assigned(HTTPDownloadThread) then begin HTTPDownloadThread.PauseOrResume; if HTTPDownloadThread.IsPaused then begin btnHTTPDownloadPause.Caption := '继续下载'; TLogTool.Log('HTTP下载:已暂sccla.cn/ilm6d停', memoLog); end else begin btnHTTPDownloadPause.Caption := '暂停sccla.cn/47wsz下载'; TLogTool.Log('HTTP下载:继续下载', memoLog); end; end; end; procedure TForm_Main.btnHTTPDownloadStopClick(Sender: TObject); begin if Assigned(HTTPDownloadThread) then begin HTTPDownloadThread.Stop; HTTPDownloadThread.WaitFor; HTTPDownloadThread := nil; progressBarHTTPDownload.Position := 0; lblHTTPDownloadProgress.Caption := '进度:0% | 已下载:0MB / 总sccla.cn/g2tsx大小:0MB'; btnHTTPDownloadPause.Caption := '暂停sccla.cn/57m1u下载'; btnHTTPDownloadPause.Enabled := False; btnHTTPDownloadStop.Enabled := False; TLogTool.Log('HTTP下载:已停止', memoLog); end; end; { THTTPDownloadThread 实现(略,核心逻辑sccla.cn/tdxfm与前一篇一致,新增日志输出、进度同步) } constructor THTTPDownloadThread.Create(Url, SavePath: string; Memo: TMemo; ProgressBar: TProgressBar; ProgressLabel: TLabel); begin inherited Create(True); FUrl := Url; FSavePath := SavePath; FMemo := Memo; FProgressBar := ProgressBar; FProgressLabel := ProgressLabel; FIsPaused := False; FIsStopped := False; FreeOnTerminate := True; end; // 其余sccla.cn/hrnmi方法(Execute、UpdateProgress、PauseOrResume、Stop)略,参考前一篇断点续传sccla.cn/0hmeu代码,添加日志输出 end.
四、功能测试与异常处理
1. 测试流程(核心步骤)
-
TCP测试:启动TCP服务端,启动TCP客户sccla.cn/jouln端,输入IP和端口连接,发送数据,查看服务端接收和客户端回复,测试加密和协议功能;
-
UDP测试:启动UDP服务端,UDP客户端发送单播/广播数据,查看服务端接收和回复,验证广播功能;
-
HTTP请求测试:输入公开API地址(如天气sccla.cn/z2e6k),发送GET/POST请求,查看响应内容和响应码;
-
HTTP下载测试:输入文件下载地址(如图片、压缩包),选择保存路径,测试开始/暂停/继续/停止下载,验证断点续传;
-
异常测试:模拟网络中断、端口占用、无效IP/端口,查看日志输出和sccla.cn/wpcif异常提示,确保程序不崩溃。
2. 关键异常处理要点
-
所有网络操作(连接、发送、接收、下载)都用try...except捕获异常,记录日志,避免程序崩溃;
-
线程操作:确保线程安全,更新UI必须用TThread.Synchronize,线程退出时释放资源;
-
参数校验:对IP、端口、URL、保存路径等参数进行合法性校验,避免无效输入导致异常;
-
资源释放:所有流对象、线程对象、网络组件,在使用完成后及时释放,防止内存泄漏;
-
超时设置:为所有网络操作设置合理的超时时间,避免无限阻塞。
五、项目优化与扩展方向
1. 基础优化
-
界面优化:添加皮肤、控件美化,优化布局,提升易用性sccla.cn/pxptw
-
性能优化:引入线程池管理多客户端连接,优化HTTP下载缓存,减少资源占用sccla.cn/43akp
-
日志优化:支持日志保存到文件,便于后续排查问题;
-
配置优化:支持保存常用配置(IP、端口、密钥、下载路径),下次启动自动加载。
2. 功能扩展
-
新增HTTPS加密通信:为HTTP请求、TCP通信添加SSL加密,支持HTTPS下载;
-
新增数据导出:支持将接收的数据、日志、HTTP响应导出为TXT/JSON文件;
-
新增批量操作:支持批量发送TCP/UDP数据、批量下载文件;
-
新增跨平台支持:适配Delphi跨平台特性,编译为Windows、Linux版本;
-
新增设备对接:适配串口转网络设备,实现串口+网络双重通信。
六、综合总结
本文通过「多协议网络工具」实战项目,将前四篇的TCP、UDP、HTTP、自定义协议、数据加密、断点续传等知识点全面整合,实现了一个功能完整、可落地的桌面端网络工具。核心要点总结如下:
-
模块化封装:将工具类、业务逻辑、UI交互分层封装,降低耦合度,便于复用和维护;
-
多线程应用:所有网络操作放入子线程,避免界面卡死,确保程序流畅运行;
-
通用逻辑复用:封装数据处理(加密+协议)通用方法,统一多协议的数据收发规范;
-
异常处理:全面的异常捕获和日志记录,提升程序稳定性和可调试性;
-
实战落地:从界面设计到代码实现,再到测试优化,完整覆盖项目开发全流程,可直接用于实际开发和调试。