我最近开始使用Indy 10(来自Delphi XE3)和TCP连接。目前,我正在尝试创建一个简单的服务器应用程序来检查客户端状态。但是当我尝试在已连接某个客户端的情况下停用TCPServer时,客户端会断开连接,但TCPServer会停止应答。
我在某处读到TCPServer应该没有问题地处理客户端的断开连接。我必须在OnExecute事件上添加一些代码来解决这个问题吗?
以下是代码:
procedure TfrmMain.btnConnectClick(Sender: TObject);
begin
If (not TCPServer.Active) Then
Try
TCPServer.Bindings.Clear;
With TCPServer.Bindings.Add Do
Begin
IP := '192.168.1.11';
Port := StrToInt(edtPort.Text);
end;
TCPServer.Active := True;
Except
On E:Exception Do ShowMessage(E.Message);
End
end;
procedure TfrmMain.btnDisconnectClick(Sender: TObject);
begin
If (TCPServer.Active) Then
Try
TCPServer.Active := False;
Except
On E:Exception Do ShowMessage(E.Message);
End
end;
procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
IdStackWin: TIdStackWindows;
begin
IdStackWin := TIdStackWindows.Create;
With IdStackWin Do
Try
memLog.Lines.Add('Connected - ' + HostByAddress(AContext.Binding.PeerIP) + ' (' + AContext.Binding.PeerIP + ')');
Finally
IdStackWin.Free;
end;
end;
procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
IdStackWin: TIdStackWindows;
begin
IdStackWin := TIdStackWindows.Create;
With IdStackWin Do
Try
memLog.Lines.Add('Disconnected - ' + HostByAddress(AContext.Binding.PeerIP) + ' (' + AContext.Binding.PeerIP + ')');
Finally
IdStackWin.Free;
end;
end;
procedure TfrmMain.TCPServerExecute(AContext: TIdContext);
begin
Application.ProcessMessages;
end;
谢谢!
答案 0 :(得分:9)
你犯了一些错误。
不要直接实例化TIdStack
类。当任何Indy套接字组件存活时,Indy会为您实例化一个。如果需要访问套接字堆栈,请使用全局GStack
对象指针,例如:
GStack.HostByAddress(AContext.Binding.PeerIP)
在极少数情况下,当没有Indy组件处于活动状态时需要访问GStack
时,您可以使用TIdStack.IncUsage()
和TIdStack.DecUsage()
方法调用来包装代码以确保GStack
可以。
TIdTCPServer
是一个多线程组件。侦听套接字和客户端套接字在工作线程中运行。 OnConnect
,OnDisconnect
,OnExecute
,OnException
和OnListenException
事件在这些工作线程的上下文中触发,而不是在主UI的上下文中触发线。因此,您必须与主线程同步才能安全地访问VCL / FMX UI控件,否则会发生不良事件,包括死锁等。您可以使用Indy的TIdSync
(同步)或TIdNotify
类(异步)类,或TThread.Synchronize()
(同步)或TThread.Queue()
(异步)方法的静态版本需要时与主线程同步。或者您选择的任何其他线程间同步机制。必须仅在主线程的上下文中访问VCL / FMX UI控件。
警告:在从主线程停用TIdTCPServer
时,请勿使用同步同步,即保证死锁。要么使用异步同步,要么从另一个线程(不是服务器线程)停用服务器,这样主线程就可以正常处理同步了。
Application.ProcessMessages()
。没有必要从TIdTCPServer
事件中调用它。
请改为尝试:
procedure TfrmMain.btnConnectClick(Sender: TObject);
begin
if not TCPServer.Active then
begin
TCPServer.Bindings.Clear;
with TCPServer.Bindings.Add do
Begin
IP := '192.168.1.11';
Port := StrToInt(edtPort.Text);
end;
TCPServer.Active := True;
end;
end;
procedure TfrmMain.btnDisconnectClick(Sender: TObject);
begin
TCPServer.Active := False;
end;
procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
Msg: string;
begin
Msg := 'Connected - ' + GStack.HostByAddress(AContext.Binding.PeerIP) + ' (' + AContext.Binding.PeerIP + ')';
TThread.Queue(nil,
procedure
begin
memLog.Lines.Add(Msg);
end
);
end;
procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
Msg: string;
begin
Msg := 'Disconnected - ' + GStack.HostByAddress(AContext.Binding.PeerIP) + ' (' + AContext.Binding.PeerIP + ')';
TThread.Queue(nil,
procedure
begin
memLog.Lines.Add(Msg);
end
);
end;
procedure TfrmMain.TCPServerExecute(AContext: TIdContext);
begin
// your communications logic here
end;