我试图编写一个服务器/服务,在局域网上大约播放一条消息,有点像服务发现。
需要收到消息 多个客户端程序可能 在同一台机器上或不同 机器。但可能还有更多 运行在每台机器上的一个程序 同时。
我使用delphi7,使用indy 9.0.18
如果我应该使用UDP(TIdUDPClient / Server)或IP MultiCast(TIdIPMCastClient / Server),或者如果它甚至可能......我会陷入困境......
我设法让它与IP Multi Cast一起使用,每个机器有一个客户端,但即使经过多次使用不同绑定的端口..最大/最小端口等,我似乎无法找到解决方案。
答案 0 :(得分:8)
我认为您正在寻找SO_REUSEADDR套接字选项。在套接字上设置该选项允许多个套接字在同一端口上侦听。对于多播,Windows保证将消息传递到所有套接字(否则消息只会随机传递到一个套接字)。
您通常通过调用setsockopt来执行此操作,但我不是Delphi开发人员,因此我不确定您的API是什么样的。这个question似乎显示了某人在Delphi中做类似事情的例子。
答案 1 :(得分:5)
我从来没有这样做,但似乎“mailslots”就是你所需要的。它将在本地网络上广播一条消息,并接收其他知道如何回复的工作站的回复。这就是流行的“犰狳”许可管理器的工作方式(确保注册密钥不会“超额认购”)。 我的应用程序(ClipMate)使用Armadillo作为保护包装器(共享软件包装器)。当注册用户运行应用程序时,它会检查同一网络上的其他计算机是否正在使用相同的密钥。它基本上说:“我正在使用1234号许可证,你呢?”它等待回复(我在启动期间在一个单独的线程中执行此操作,因此我不会阻止我的启动)。如果其他工作站报告他们使用相同的密钥,我会根据许可证中包含的席位数来核对计数。我并不完全确定它在Windows7上是如此强大......
答案 2 :(得分:2)
这绝对是可能的。
重新“UDP或多播”,你说的是苹果和橘子。多播是一种IP概念,因此您可以愉快地通过 over 多播IP或广播IP。
如果您对所有客户端链接本地(路由器等通常不转发广播数据包)的限制没有问题,我会说只是广播。 TIdUdpBase.Broadcast将成为你的朋友。
更新:使用多播或广播,您只能将一个套接字绑定到任何特定的IP /端口对。因此,如果您希望多个客户端都收听SAME广播/多播,我认为您将需要一个额外的调度程序客户端。此调度程序客户端接收广播并通知计算机上的每个客户端。
在每个客户端中,您都有一个注册程序,说“尝试绑定到广播发送到的端口。如果可以,请在该端口上设置调度程序客户端。如果不能,调度程序已经创建并注册给调度员。“
注册过程可以像绑定到本地主机IP上的任何可用端口一样简单,并向调度员说“请将广播发送到此IP /端口。”
更新: Christopher Chase有正确的想法。我刚刚完成了与他完全相同的解决方案,除了我修补了IdIPMCastClient,添加了一个属性ReuseAddr:Boolean并通过添加
来改变TIdIPMCastClient.GetBindingif Self.ReuseAddr then begin
SetReuseAddr := Id_SO_True;
Bindings[i].SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, @SetReuseAddr, Sizeof(SetReuseAddr));
end;
调用AllocateSocket和Bind(其中SetReuseAddr:Integer)之间的。
答案 3 :(得分:1)
RemObjects有一个很好的解决方案:ROZeroConf
在此之前,我使用TROBroadcastChannel
RemObjects SDK(基于UDP和Indy)自己创建了类似的东西。
在该组件内部,它会调用TIdUDPBase.Broadcast
来发送,TIdUDPClient.ReceiveBuffer
来接收回复。
(顺便说一下,UDP广播只能在同一网络/子网上工作,ROZeroConf是更好的解决方案)
答案 4 :(得分:1)
使用shf301中的提示,这是我使用它的代码
我创建了一个新的TIdIPMCastClient
TIdReUseIPMCastClient = class(TIdIPMCastClient)
private
procedure SetReUseAddr(InBinding: TIdSocketHandle; const Value: boolean);
protected
function GetBinding: TIdSocketHandle; override;
public
end;
添加了程序
procedure TIdReUseIPMCastClient.SetReUseAddr(InBinding: TIdSocketHandle; const Value: boolean);
var
tempi: integer;
begin
if Assigned(InBinding) and InBinding.HandleAllocated then
begin
tempi := iif(Value, 1, 0);
InBinding.SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, PChar(@tempi), SizeOf(tempi));
end;
end;
从TIdIPMCastClient复制了 GetBinding 代码,并在绑定之前添加了SetReUseAddr
Bindings[i].AllocateSocket(Id_SOCK_DGRAM);
SetReUseAddr(Bindings[i], True);
Bindings[i].Bind;