我正在创建一个仅在Windows操作系统上运行的客户端 - 服务器应用程序,每个Windows用户将一次连接到服务器。 LAN使用DHCP,因此IP不固定。
我需要从IdTCPServer向特定的IdTCPClient发送一个字符串消息。一开始我使用的是Listbox,所以我在连接时将主机名添加到列表框中,并在断开连接时删除。那时,Remy Lebeau给了我这个小费:
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;
但现在我正在使用带有预先添加的主机名的ListView。所以我只是在客户端连接或断开连接时更改列表框项目图像状态。现在我正在尝试这样的事情:
procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
begin
TThread.Queue(nil,
procedure
var
Host: String;
LItem: TListItem;
begin
Host := UpperCase(GStack.HostByAddress(Ctxt.Binding.PeerIP));
LItem := lvwPCList.FindCaption(0, Host, False, True, False);
if (LItem <> nil) then LItem.Data := AContext.Data;
end
);
end;
一旦我将Listview项目与Context数据相关联,我就会尝试将消息直接发送给客户端:
procedure TfrmMain.SendMessage(const Item: TListItem; const Msg: String);
var
Ctx: TIdContext;
begin
if (Trim(Msg) = '') then Exit;
Ctx := TIdContext(Item.Data);
try
Ctx.Connection.IOHandler.WriteLn(Msg);
except
end;
end;
我可以编译此代码,但消息永远不会到达客户端。请问,我做错了什么?
谢谢!
答案 0 :(得分:1)
您应该在TIdContext
继承中实现唯一的会话ID。在每个新连接上生成新的唯一ID。例如,您可以使用递增的整数,或生成GUID。在任何情况下,您都有自己唯一的“会话ID”,它唯一地标识客户端/服务器“会话”。这假设您希望完成某种会话管理。
至于循环,您仍然需要执行循环来查找相应的客户端。但是这样的循环是微不足道的,并且可以在几分之一毫秒内执行,即使有数千个连接的客户端(已经在任何服务器上进行了大修)。即使你依赖内置的东西,内置函数仍然会执行一个循环。
您不能简单地通过其主机名或IP地址引用已连接的客户端,因为同一客户端可能有多个连接。由于这一事实,没有“通过主机或IP向客户端发送消息”这样的事情。您需要明确引用特定连接。不要担心您的连接是否来自同一台计算机/设备,除非您打算严格禁止来自同一设备的多个连接但即便如此,如果您决定通过互联网使用此连接,则所有客户端背后相同的网络将被视为相同的IP。这同样适用于除Windows以外的其他设备。在将来,您可能希望支持其他设备,如果您计划在某一天走这条路线,您需要确保您的结构已经准备就绪。