如何在Delphi Tokyo 10.2中为IdTCPServer分配IP地址?
问题分析与解决方案
我看了你的Delphi Indy TCP服务器代码,这里有几个关键问题会导致程序不稳定、异常甚至崩溃,我来一步步帮你修正:
1. 重复创建组件的错误
你在FormCreate里手动调用了IdTCPServer1:=IdTCPServer1.Create(nil);,但如果这个组件是你在设计时拖到窗体上的,这就会导致重复创建组件,引发内存泄漏或者后续操作的异常。
修正方案:移除手动创建的代码,直接设置组件属性即可:
procedure TForm1.FormCreate(Sender: TObject); begin // 删掉 IdTCPServer1:=IdTCPServer1.Create(nil); 这一行 IdTCPServer1.DefaultPort:=50000; IdTCPServer1.OnExecute:=IdTCPServer1Execute; IdTCPServer1.Active:=true; end;
2. 跨线程操作VCL控件的风险
Indy的OnConnect和OnExecute事件都是在工作线程中触发的,直接在这些事件里操作Memo1或者调用ShowMessage(VCL控件)属于跨线程操作,会导致线程不安全,大概率引发程序崩溃或UI异常。
解决这个问题需要用TThread.Synchronize(同步)或者TThread.Queue(异步)把UI操作委托给主线程执行:
修正OnConnect事件
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext); var PeerIP: string; begin PeerIP := AContext.Connection.Socket.Binding.PeerIP; // 用Synchronize把UI操作同步到主线程 TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('客户端连接: ' + PeerIP); end); end;
修正OnExecute事件
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var ReceivedMsg: string; begin try // 给ReadLn设置超时,避免线程无限阻塞 AContext.Connection.Socket.ReadTimeout := 5000; // 5秒超时 ReceivedMsg := AContext.Connection.Socket.ReadLn(); if Trim(ReceivedMsg) <> '' then begin // 用Queue异步更新UI,避免阻塞工作线程 TThread.Queue(nil, procedure begin Memo1.Lines.Add('收到来自 ' + AContext.Connection.Socket.Binding.PeerIP + ' 的消息: ' + ReceivedMsg); ShowMessage('收到新消息: ' + ReceivedMsg); end); end; except on E: Exception do begin // 忽略超时异常,只记录其他错误 if not(E is EIdReadTimeout) then begin TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('连接异常 (' + AContext.Connection.Socket.Binding.PeerIP + '): ' + E.Message); end); end; AContext.Connection.Disconnect; end; end; end;
3. 额外优化建议
- 建议在窗体销毁时关闭服务器,确保资源正确释放:
procedure TForm1.FormDestroy(Sender: TObject); begin if IdTCPServer1.Active then IdTCPServer1.Active := False; end;
- 尽量避免在工作线程中使用
ShowMessage这类阻塞弹窗,优先用Memo记录消息,减少对服务器并发能力的影响。
完整修正后的代码
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, Vcl.StdCtrls; type TForm1 = class(TForm) IdTCPServer1: TIdTCPServer; Memo1: TMemo; procedure IdTCPServer1Execute(AContext: TIdContext); procedure FormCreate(Sender: TObject); procedure IdTCPServer1Connect(AContext: TIdContext); procedure FormDestroy(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin IdTCPServer1.DefaultPort := 50000; IdTCPServer1.OnExecute := IdTCPServer1Execute; IdTCPServer1.Active := True; end; procedure TForm1.FormDestroy(Sender: TObject); begin if IdTCPServer1.Active then IdTCPServer1.Active := False; end; procedure TForm1.IdTCPServer1Connect(AContext: TIdContext); var PeerIP: string; begin PeerIP := AContext.Connection.Socket.Binding.PeerIP; TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('客户端已连接: ' + PeerIP); end); end; procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var ReceivedMsg: string; begin try AContext.Connection.Socket.ReadTimeout := 5000; ReceivedMsg := AContext.Connection.Socket.ReadLn(); if Trim(ReceivedMsg) <> '' then begin TThread.Queue(nil, procedure begin Memo1.Lines.Add('[' + AContext.Connection.Socket.Binding.PeerIP + '] 发来消息: ' + ReceivedMsg); ShowMessage('收到新消息: ' + ReceivedMsg); end); end; except on E: Exception do begin if not(E is EIdReadTimeout) then begin TThread.Synchronize(nil, procedure begin Memo1.Lines.Add('[' + AContext.Connection.Socket.Binding.PeerIP + '] 连接异常: ' + E.Message); end); end; AContext.Connection.Disconnect; end; end; end; end.
内容的提问来源于stack exchange,提问作者yacime11
相关产品推荐
相关产品推荐

