MultiCast消息到同一台机器上的多个客户端

时间:2010-04-09 02:50:14

标签: delphi delphi-7 indy multicast indy-9

我试图编写一个服务器/服务,在局域网上大约播放一条消息,有点像服务发现。

  

需要收到消息   多个客户端程序可能   在同一台机器上或不同   机器。但可能还有更多   运行在每台机器上的一个程序   同时。

我使用delphi7,使用indy 9.0.18

如果我应该使用UDP(TIdUDPClient / Server)或IP MultiCast(TIdIPMCastClient / Server),或者如果它甚至可能......我会陷入困境......

我设法让它与IP Multi Cast一起使用,每个机器有一个客户端,但即使经过多次使用不同绑定的端口..最大/最小端口等,我似乎无法找到解决方案。

5 个答案:

答案 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.GetBinding
if 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;