同一 Windows 主机上的多个应用程序使用同一多播组

同一 Windows 主机上的多个应用程序使用同一多播组

我是一名软件工程师,正在设计一款分布式软件,其中启动过程利用 IP 多播来发现其对等点。软件本身作为不同的可执行模块进行分发,因此,有时在同一台主机上运行多个可执行模块是有意义的。这就是我的问题开始的地方,因为在我看来,Windows 不擅长管理订阅单个多播组的多个进程。

我的目标是能够自由选择在哪些主机上启动哪些可执行文件,并且这些可执行文件将在无需任何预定义知识的情况下发现其所有对等体(无论是在同一主机上还是在其他主机上)。

到目前为止,故障排除表明问题在于 Windows 如何处理多个进程订阅相同多播组的情况,因为:

  1. 如果在同一台机器上使用相同的进程来发送和接收多播数据报,则它会按预期工作。该进程可以拆分为多个线程而不会出现任何问题。

  2. 如果我运行不同的发送和接收进程,则接收进程不会收到任何内容,尽管组加入消息和所有数据报都显示在 Wireshark 中。

  3. 如果我在监听之前还使用相同的套接字向我加入的多播组发送数据包,则 2 中描述的场景可行。然后后者在一段未指定的时间内接收多播数据报,然后完全停止接收数据报(它一直等待数据报到来)。使用 Wireshark 确认数据报正在发送到网络/从网络发送。

  4. 我的最新发现表明,如果我定期向我订阅的组发送多播消息,我会收到发送到该多播组的数据报,也会收到来自同一主机上的其他进程的数据报。

据我所知,IP 多播仅定义主办方而操作系统的责任是将传入的数据包重定向到适当的应用。由于数据包似乎总是出现在 Wireshark 中,即使应用程序没有接收它们,Windows 似乎无法处理传入的包,或者至少无法将它们传送给适当的应用程序。

如果有人能证实或否定我的推理,并指出如何解决这个问题的正确方向,我将不胜感激。目标是让同一主机上的多个应用程序能够加入单个多播组通道并接收消息,而无需向多播组发送“垃圾”消息才能接收数据(解决方法如第 4 点所述)。

我使用 Java 进行实现,如果需要,可以在此处发布 MWE。但是,我担心这可能会将重点从场景转移到编程,而这并不是这里要关注的问题(据我推断)。

答案1

OP 要求提供一种方法来发现网络上的对等点,而无需了解这些对等点,根据此来源mDNS 至少需要了解每个对等体的主机名。

因此,mDNS并不是解决该问题的可行方案。

根据对此答案关于多播,您所要求的是可能的。它涉及使用 SO_REUSEADDR 套接字选项。当您使用此套接字选项时,它允许多个套接字侦听相同的地址:端口组合,只要;

  1. 该地址是多播地址;并且
  2. 在每个尝试绑定到地址:端口组合的套接字上设置 SO_RESUSEADDR。

然而,根据这个答案,您可能还需要在每个套接字上设置 SO_BROADCAST,这当然不直观!但是,在我自己在 Windows 上使用 Python 进行的测试中,我不需要这样做,因此不确定该答案是否合理,我提到它以防您遇到困难,尽管我认为这可能已经过时了,但我目前正在 Windows 10 上进行测试。

下面是一个我测试过的代码示例,没有产生任何错误,它是使用信标将工作进程连接到代理进程的自动测试的一部分。此测试在单个节点上运行时通过,尚未在多个节点上测试

import socket, struct

MCAST_GRP = ''
MCAST_PORT = 1234

MULTICAST_TTL = 2

def create_beacon_listener():

    sock = socket.socket(
        socket.AF_INET,
        socket.SOCK_DGRAM,
        socket.IPPROTO_UDP
    )
    sock.setsockopt(
        socket.SOL_SOCKET,
        socket.SO_REUSEADDR,
        1
    )
    sock.setblocking(0)

    sock.bind(( MCAST_GRP, MCAST_PORT ))

    multicast_grp_req = struct.pack(
        "4sl",
        socket.inet_aton(MCAST_GRP),
        socket.INADDR_ANY
    )

    sock.setsockopt(
        socket.IPPROTO_IP,
        socket.IP_ADD_MEMBERSHIP,
        multicast_grp_req
    )
    
    return sock

上述代码返回所需组地址 MCAST_GRP 和端口 MCAST_PORT 上的监听套接字。是的,MCAST_GRP 设置为空字符串,这意味着您实际上监听所有多播地址组。这似乎是 Windows 或我的知识中的限制,可能是后者。

无论哪种方式,使用 MCAST_GRP 变量中的特定地址都会导致错误,因此使用空白字符串至少可以让代码解决 OP 描述的问题。

此代码可在同一进程中多次重复使用,但我尚未测试多个进程是否可以成功使用它。我已经使用 python 的 asyncio 框架进行了测试,因此多个套接字在同一个线程中监听。

顺便说一句,为了跨平台支持,您还可以让此代码在 nix 操作系统上运行,并且在这些系统上您可以将 MCAST_GRP 设置为特定地址。

编辑:

我最近遇到了来自 MS 的此资源它给出了一个示例,该示例似乎允许使用特定的多播组地址,而不是绑定到所有地址。它涉及在 multicast_grp_req 变量中设置更多选项。此外,该示例是 C++ 代码,而不是 Python,但它们足够相似,可以理解要点。

希望这可以帮助 :)

相关内容