10

我试图编写一个服务器/服务,它每秒左右在局域网上广播一条消息,有点像服务发现。

该消息需要由可能位于同一台机器或不同机器上的多个客户端程序接收。但是每台机器上可能同时运行多个程序。

我使用 delphi7,带有 indy 9.0.18

我卡住的地方是我是否应该使用 UDP(TIdUDPClient/Server) 或 IP MultiCast (TIdIPMCastClient/Server) 或者如果它甚至可能......

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

4

5 回答 5

8

我认为您正在寻找SO_REUSEADDR套接字选项。在套接字上设置该选项允许多个套接字在同一个端口上侦听。对于多播,Windows 保证消息将被传递到所有套接字(否则消息只会随机发送到一个套接字)。

您通常通过调用 setsockopt 来执行此操作,但我不是 Delphi 开发人员,所以我不确定您的 API 是什么样的。这个问题似乎显示了某人在 Delphi 中做类似事情的例子。

于 2010-04-14T03:19:21.610 回答
5

我从来没有这样做过,但似乎“邮槽”是你需要的。它将在本地网络上广播一条消息,并从其他知道如何回复的工作站接收任何回复。这就是流行的“犰狳”许可证管理器的工作方式(以确保注册密钥不会“超额订阅”)。我的应用程序(ClipMate)使用犰狳作为保护包装(共享软件包装)。当注册用户运行该应用程序时,它会检查同一网络上的其他计算机是否正在使用相同的密钥。它基本上说:“我使用的是 1234 许可证,你呢?” 它等待回复(我在启动期间在单独的线程中执行此操作,因此我不会阻止我的启动)。如果其他工作站报告他们正在使用相同的密钥,我根据许可证中包含的席位数量检查计数。我不完全确定它在 Windows7 上是否强大......

于 2010-04-09T03:16:10.680 回答
2

这绝对是可能的。

关于“UDP 或多播”,您说的是苹果和橘子。多播是一个 IP 概念,因此您可以愉快地通过多播 IP 或广播 IP 进行 UDP。

如果您对让所有客户端链接本地(路由器等通常不转发广播数据包)的限制感到满意,我会说只使用广播。TIdUdpBase.Broadcast 将成为您的朋友。

更新:使用多播或广播,您只能将一个套接字绑定到任何特定的 IP/端口对。因此,如果您希望多个客户端都收听相同的广播/多播,我认为您将需要一个额外的调度程序客户端。此调度程序客户端接收广播并通知机器上的每个客户端。

在您的每个客户端中,您都有一个小的注册程序,上面写着“尝试绑定到发送广播的端口。如果可以,请在该端口上设置调度程序客户端。如果不能,调度程序已经创建,并且向那个调度员登记。”

该注册过程可以简单到绑定到本地主机 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)。

于 2010-04-12T14:35:31.973 回答
1

RemObjects 对此有一个很好的解决方案:ROZeroConf

在此之前,我自己使用TROBroadcastChannelRemObjects SDK(基于 UDP 和 Indy)制作了类似的东西。在该组件内部,它调用TIdUDPBase.Broadcast发送和TIdUDPClient.ReceiveBuffer接收响应。

(顺便说一句,UDP 广播仅适用于同一网络/子网,ROZeroConf是更好的解决方案)

于 2010-04-09T06:22:44.037 回答
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;
于 2010-04-14T06:23:49.060 回答