我最近开始玩 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;
当然,这不是最安全的方法,但它可以帮助您入门。您需要考虑的事项:
在您将其从 ListBox 中删除之前,客户端可能会断开连接并释放其
TIdContext
对象。在使用该对象之前,您应该确保该对象仍然位于服务器的Contexts
列表中。从按钮事件(或任何其他非服务器事件)发送未经请求的数据不是线程安全的。如果您从其他线程(特别是从服务器的事件)向同一客户端发送数据而不同步线程,则您的通信可能会被损坏。您应该仅管理服务器事件内的通信。如果您需要从事件外部发送数据,则更安全的做法是将数据放入每个客户端线程安全的队列中,然后让
OnExecute
事件发送队列的内容。这样做是安全的。我之前已经在几个不同的论坛上发布过很多次这样的例子,所以在网上找到它们应该不难。OnDisconnect
事件不是查找客户端详细信息(例如其主机名)的好地方。您应该在OnConnect
和/或OnExecute
事件中执行此操作,然后缓存该值以供以后使用。您可以使用TIdContext.Data
属性来实现此目的,或者从TIdServerContext
派生一个新类,并在激活服务器之前设置服务器的ContextClass
属性.
关于Delphi 和 Indy - 如何从 IdTCPServer 发送某些内容到特定的 IdTCPClient,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27218701/