Delphi和Indy - 如何从IdTCPServer向特定的IdTCPClient发送内容

时间:2014-11-30 21:54:39

标签: delphi tcp indy

我最近开始使用Indy 10(来自Delphi XE3)和TCP连接。在您的帮助下(特别感谢Remy Lebeau),我已经可以构建一个用于管理客户端连接的简单服务器应用程序(请参阅此处Delphi - Simple TCP client / server using Indy to check clients status)。我正在使用列表框添加连接的客户端。见代码:

procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
  Host: String;
begin
  Host := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
  TThread.Queue(nil,
    Procedure
    begin
      ListBox.Itens.Add(Host);
      Log('Connected - ' + Host);
      With TCPServer.Contexts.LockList Do
      Try
        StatusBar.Panels[0].Text := 'Connected Clients: ' + IntToStr(Count);
      Finally
        TCPServer.Contexts.UnlockList;
      end;
    end
  );
end;

现在我正在尝试发送"你好"从服务器到列表中的特定客户端。我的想法是单击以在列表框中选择客户端主机名,然后单击按钮发送消息。但我正在研究的是事情并不像我想的那么容易......

拜托,一些Indy专家可以指出正确的方向(使用Indy 10)吗?

谢谢!

1 个答案:

答案 0 :(得分:2)

最常从OnConnect事件发送问候语,您可以直接访问已连接的客户端的TIdContext

话虽如此,如果您想从服务器事件外部向特定客户端发送数据,那么您需要跟踪所需客户端的TIdContext对象,或者在服务器中查找它#39; s Contexts列表。在此特定示例中,您可以将TIdContext对象指针存储在ListBox本身中,然后在需要时检索它,例如:

procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
  Host: String;
begin
  Host := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
  TThread.Queue(nil,
    procedure
    begin
      ListBox.Items.AddObject(Host, AContext);
      Log('Connected - ' + Host);
      With TCPServer.Contexts.LockList Do
      Try
        StatusBar.Panels[0].Text := 'Connected Clients: ' + IntToStr(Count);
      Finally
        TCPServer.Contexts.UnlockList;
      end;
    end
  );
end;

procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
  Host: String;
begin
  Host := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
  TThread.Queue(nil,
    procedure
    var
      Index: Integer;
    begin
      Index := ListBox.Items.IndexOfObject(AContext);
      if Index <> -1 then
        ListBox.Items.Delete(Index);
      Log('Disconnected - ' + Host);
      With TCPServer.Contexts.LockList Do
      Try
        StatusBar.Panels[0].Text := 'Connected Clients: ' + IntToStr(Count);
      Finally
        TCPServer.Contexts.UnlockList;
      end;
    end
  );
end;

procedure TfrmMain.sendButtonClick(Sender: TObject);
var
  Index: Integer;
  Ctx: TIdContext;
begin
  Index := ListBox.ItemIndex;
  if Index = -1 then Exit;
  Context := TIdContext(ListBox.Items.Objects[Index]);
  // use Context as needed...
end;

当然,这不是最安全的方法,但它会让你开始。你需要考虑的事情:

  1. 客户端可能会断开并释放其TIdContext对象,然后再将其从ListBox中删除。在使用之前,您应确保该对象仍在服务器的Contexts列表中。

  2. 从按钮事件(或任何其他非服务器事件)发送未经请求的数据不是线程安全的。如果您从其他线程向同一客户端发送数据,特别是从服务器的事件发送数据,而不同步线程,则通信可能会损坏。您仅在服务器事件中管理通信。如果需要从事件外部发送数据,将数据放入每个客户端的线程安全队列更安全,然后让OnExecute事件在安全的情况下发送队列内容所以。我之前已经在几个不同的论坛上发布了很多次的例子,所以在网上找到它们应该不难。

  3. OnDisconnect事件不是查找客户端详细信息的好地方,例如其主机名。您应该在OnConnect和/或OnExecute事件中执行此操作,然后缓存该值以供以后使用。您可以将TIdContext.Data属性用于此目的,或从TIdServerContext派生新类并在激活服务器之前设置服务器的ContextClass属性。

相关问题