关于这段代码使用套接字接收多播,我的结论是否合理?

Are my conclusions reasonable about this code receiving multicast using socket?

基于堆栈中的 MC 接收器: How do you UDP multicast in Python?

我想彻底明白是怎么回事。以下是我理解的和不理解的:

据我了解:socket_name = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) => 使用 IP proto ver 4 创建套接字,它将使用 UDP 接收 MC 数据报。

socket_name.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)这个负责设置socket可以使用相同地址的参数

(在本例中为端口,因为 SO_REUSEADDR = SO_REUSEPORT 用于多播)。 (段落多播~How do SO_REUSEADDR and SO_REUSEPORT differ?)

if IS_ALL_GROUPS: socket_name.bind(('', MCAST_PORT)) 表示如果 IS_ALL_GROUPS 为真,则将套接字绑定到任何地址,否则: socket_name.bind((MCAST_GRP, MCAST_PORT)) 表示将套接字绑定到特定给定的 IP 地址。

mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) 表示将 IP 地址转换为二进制形式,socket_name.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) 表示我们添加组成员,此行之后数据包开始到达。

但是有些事情我不明白。

为什么当我使用 IS_ALL_GROUPS = trueMCAST_GRP = '239.0.1.104' 程序可以启动并且当参数为 false 时,它​​没有绑定到特定的多播地址?我的逻辑是,当参数为真时,它绑定到他从 IGMP 加入消息中获得的任何 MCast 地址,当参数为假时,它绑定到特定的给定地址。我说得对吗?

我有一个分析比特率的多线程程序,我以列表的形式提供了多个地址。当我将 IS_ALL_GROUPS 设置为 false 时,程序可以正确打印出来,例如10.5, 4.5, 5.0 其中每个结果都是来自唯一地址的一个流的比特率,但是所有地址共享同一个端口 12345。当我将 IS_ALL_GROUPS 设置为 true 时,程序汇总结果给出 20.0, 20.0, 20.0,你知道可能是什么原因吗?

import socket
import struct

MCAST_GRP = '239.0.1.104'
MCAST_PORT = 12345
IS_ALL_GROUPS = True

socket_name = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
    # on this port, receives ALL multicast groups
    socket_name.bind(('', MCAST_PORT))
else:
    # on this port, listen ONLY to MCAST_GRP
    socket_name.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

while True:
  print socket_name.recv(10240)

我认为有几件事正在发生:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

应该probably be:

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)

唯一基于 "datagram" 的 IPv4 协议是 UDP,协议字段仅用于 "raw sockets",它被 tcpdump 等程序使用。不过这不重要。

下一个:

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

只允许多个套接字一次绑定到相同的 (addr, port) 组合,即您可以一次 运行 程序的多个副本,或快速连续。我想这就是你所说的

下一个:

sock.bind((host, port))

表示您想接收发往给定地址和端口的数据包。特殊地址 ''(即 INADDR_ANY)意味着内核不应该对地址进行任何过滤。您可能不想在此阶段绑定到任何特定地址,请参阅 What does it mean to bind a multicast (UDP) socket?

下一个:

mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

我建议使用 "=4sl" 作为格式字符串,因为这给了我 8 个字节,而不是 16 个字节,并且与我 (Linux) 上的 C struct ip_mreq 一致系统。这种填充差异可能只是一个 32/64 位问题,而且似乎没有什么坏处

总结一下:1. setsockopt(IP_ADD_MEMBERSHIP) 调用要求您的内核网络堆栈安排(通过 IGMP)将发送到 MCAST_GRP 的多播数据包传送到连接到您主机的网络接口. 2. bind() 调用已安排通过网络接口接收到的数据包到达进程中的套接字。 bind() 指定 INADDR_ANY 可能很重要,这样所有到达的多播数据包都会传送到您的进程而不是被过滤

一些可能有用的命令行是:

tcpdump -i eth0 -vv 'ip proto 2'

转储 IGMP 数据包,并且:

tcpdump -i eth0 -vv 'net 224.0.0.0/4'

转储多播数据包

我不确定你的 "multithread analysis program" 在做什么,因为你没有提供任何相关信息。你发的代码也不对,大概socket_namesock是一样的吧?我还建议使用 Python 3 因为 Python 2 快死了