繁体   English   中英

Delphi-使用Indy检查客户端状态的简单TCP客户端/服务器

[英]Delphi - Simple TCP client / server using Indy to check clients status

我最近开始使用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;

谢谢!

您正在犯一些错误。

  1. 不要直接实例化TIdStack类。 当任何Indy套接字组件都处于活动状态时,Indy会为您实例化一个实例。 如果需要访问套接字堆栈,请使用全局GStack对象指针,例如:

     GStack.HostByAddress(AContext.Binding.PeerIP) 

    在极少数情况下,当没有Indy组件GStack时需要访问GStack时,可以使用TIdStack.IncUsage()TIdStack.DecUsage()方法调用包装代码,以确保GStack可用。

  2. TIdTCPServer是一个多线程组件。 侦听套接字和客户端套接字在工作线程中运行。 OnConnectOnDisconnectOnExecuteOnExceptionOnListenException事件是在那些辅助线程的上下文中触发的,而不是在主UI线程的上下文中触发的。 因此,您必须与主线程进行同步,以便安全地访问VCL / FMX UI控件,否则会发生不良情况,包括死锁等。 您可以使用Indy的TIdSync (同步)或TIdNotify类(异步)类,或TThread.Synchronize() (同步)或TThread.Queue() (异步)方法的静态版本在需要时与主线程同步。 或您选择的任何其他线程间同步机制。 VCL / FMX UI控件必须仅在主线程的上下文中访问。

    警告:从主线程禁用TIdTCPServer ,请勿使用同步同步,这是有保证的死锁。 您可以使用异步同步,也可以从另一个线程(虽然不是服务器线程)停用服务器,以便主线程可以自由地正常处理同步。

  3. 绝对不应在主UI线程之外调用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;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM