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

标签 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;

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

请一些 Indy 专家能给我指出正确的方向(Indy 10)吗?

谢谢!

最佳答案

问候最常见的是从 OnConnect 事件发送的,您可以在其中直接访问连接的客户端的 TIdContext

话虽这么说,如果您想从服务器事件外部将数据发送到特定客户端,那么您需要跟踪所需客户端的 TIdContext 对象,或者查找它在服务器的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. 在您将其从 ListBox 中删除之前,客户端可能会断开连接并释放其 TIdContext 对象。在使用该对象之前,您应该确保该对象仍然位于服务器的Contexts列表中。

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

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

关于Delphi 和 Indy - 如何从 IdTCPServer 发送某些内容到特定的 IdTCPClient,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27218701/

相关文章:

delphi - 在 Indy TIdHTTPServer AuthRealm 中使用 Unicode 字符

json - 如何正确地将非字符串值添加到 TJSONObject?

delphi - 子窗体总是在父窗体之上

用于 IOT 项目的多个服务器通信的 Java 单客户端线程

delphi - Indy 10 TCP Client Server - 测试开放通信 channel

delphi - 如何使用 Indy 发送 DELETE 请求?

delphi - 在 Fiddler 中查看 Indy 流量

delphi - 有什么办法可以改变我的IDE的字体吗? (RAD 工作室 10.4)

java - 如何捕获 IOException "Connection reset by peer"?

delphi - 是否可以使用新的/不同版本的 Indy 重新编译 Delphi XE 中的 DataSnap 包?