【发布时间】:2024-01-19 11:59:01
【问题描述】:
我正在创建一个仅在 Windows 操作系统上运行的客户端-服务器应用程序,每个 Windows 用户将一次连接到服务器。局域网使用DHCP,所以IP不固定。
我需要将字符串消息从 IdTCPServer 发送到特定的 IdTCPClient。一开始我使用的是列表框,所以我在连接时将主机名添加到列表框,并在断开连接时删除。当时,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 项与上下文数据链接起来,我就会尝试将消息直接发送给客户端:
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;
我可以编译这段代码,但消息永远不会到达客户端。请问,我做错了什么?
谢谢!
【问题讨论】:
-
如果同一个客户端多次连接到您的服务器会怎样?还是同一互联网连接后面的两台计算机(因此使用相同的 IP)?你想让你的信息传达给所有人吗?还是只是其中之一?无论哪种方式,您都必须循环,就像您在代码中所做的那样。 Indy 中没有内置这样的东西供服务器向特定 IP 或主机(作为连接的客户端之一)发送消息。这就是服务器上下文的全部内容。你可能想考虑继承你自己的
TIdServerContext。 -
@Jerry,OP 已经使用了
TClient对象(可以唯一标识客户端连接)。我认为是时候给该对象某种唯一的会话标识符了。 -
@TLama 我确实看到了,但不清楚 OP 是否熟悉如何使用它,因为从它获得的唯一东西是您可以直接从上下文本身获得的东西。是的,引入一个唯一的会话标识符是合适的做法,但仍然需要一个循环,例如 OP 的代码,只需匹配此会话 ID 而不是主机名。
-
感谢您的回复!我忘了说我是 Indy 和 TCP 通信的新手。好吧,我的应用程序将在 LAN 内运行,并且每个客户端(计算机)都可以连接到服务器一次。在服务器端,我有一个列表视图,上面有预先添加的主机名。每次客户端连接时,列表视图中的主机名都会更改其状态。我的想法是为每个主机提供一些连接 ID,以便可以轻松识别/访问它。我想在列表视图中选择一个主机名,然后单击一个按钮以发送一个简单的字符串。感谢您的帮助!
-
您的实际实现可能不允许一个客户端多次连接,但您必须假设它仍然是可能的。没有办法绕过这个概念。假设一个 Windows 设备,其中一个用户登录并连接到您的服务器,然后另一个用户另外登录到同一台计算机(通过 Windows),从而产生第二个连接。这仍然很有可能,您应该考虑到这一点。