简体   繁体   English

在 Python 中选择多播网络接口

[英]Choosing multicast network interface in Python

I have a server with two separate Ethernet connections.我有一个带有两个独立以太网连接的服务器。 When I bind a socket in python it defaults to one of the two networks.当我在 python 中绑定一个套接字时,它默认为两个网络之一。 How do I pull a multicast stream from the second network in Python?如何从 Python 中的第二个网络中提取多播流? I have tried calling bind using the server's IP address on the second network, but that hasn't worked.我曾尝试在第二个网络上使用服务器的 IP 地址调用 bind,但这没有用。

I recommend you don't use INADDR_ANY.我建议您不要使用 INADDR_ANY。 In production multicast environments you want to be very specific with your multicast sockets and don't want to be doing things like sending igmp joins out all interfaces.在生产多播环境中,您希望对多播套接字非常具体,并且不想做诸如发送 igmp 加入所有接口之类的事情。 This leads to hack-job workarounds when things aren't working like "route add -host 239.1.1.1 dev eth3" to get multicast joins going correctly depending on the system in question.当事情不能像“route add -host 239.1.1.1 dev eth3”那样根据相关系统正确进行多播连接时,这会导致hack-job解决方法。 Use this instead:改用这个:

def joinMcast(mcast_addr,port,if_ip):
    """
    Returns a live multicast socket
    mcast_addr is a dotted string format of the multicast group
    port is an integer of the UDP port you want to receive
    if_ip is a dotted string format of the interface you will use
    """

    #create a UDP socket
    mcastsock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    #allow other sockets to bind this port too
    mcastsock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

    #explicitly join the multicast group on the interface specified
    mcastsock.setsockopt(socket.SOL_IP,socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(mcast_addr)+socket.inet_aton(if_ip))

    #finally bind the socket to start getting data into your socket
    mcastsock.bind((mcast_addr,port))

    return mcastsock

In the mcastsock.bind you can also use '' instead of the address string, but I advise against this.在 mcastsock.bind 中,您也可以使用 '' 代替地址字符串,但我建议不要这样做。 With '', if you have another socket using the same port, both sockets will get eachothers data.使用'',如果您有另一个使用相同端口的套​​接字,则两个套接字都将获取彼此的数据。

When bind ing your socket, try the values mentioned here : bind套接字时,请尝试此处提到的值:

For IPv4 addresses, two special forms are accepted instead of a host address: the empty string represents INADDR_ANY, and the string '' represents INADDR_BROADCAST.对于 IPv4 地址,接受两种特殊形式而不是主机地址:空字符串表示 INADDR_ANY,字符串 '' 表示 INADDR_BROADCAST。

INADDR_ANY is also known as the wildcard address: INADDR_ANY也称为通配符地址:

Sockets with wildcarded local addresses may receive messages directed to the specified port number and addressed to any of the possible addresses assigned to a host`具有通配本地地址的套接字可以接收定向到指定端口号并寻址到分配给主机的任何可能地址的消息

More here .更多在这里

I figured it out.我想通了。 It turns out that the piece I was missing was adding the interface to the mreq structure that is used in adding membership to a multicast group.事实证明,我缺少的部分是将接口添加到 mreq 结构中,该结构用于向多播组添加成员资格。

For IPv4, the index of the network interface is the IP address;对于 IPv4,网络接口的索引是 IP 地址; for IPv6 the index of the network interface is returned by the method socket.getaddrinfo.对于 IPv6,网络接口的索引由方法 socket.getaddrinfo 返回。

The code below shows how to listen to multicast on all network interfaces:下面的代码显示了如何在所有网络接口上监听多播:

from socket import AF_INET6, AF_INET
import socket
import struct

# Bugfix for Python 3.6 for Windows ... missing IPPROTO_IPV6 constant
if not hasattr(socket, 'IPPROTO_IPV6'):
    socket.IPPROTO_IPV6 = 41

multicast_address = {
    AF_INET: ["224.0.1.187"],
    AF_INET6: ["FF00::FD"]
}
multicast_port = 5683

addr_info = socket.getaddrinfo('', None)  # get all ip
for addr in addr_info:
    family = addr[0]
    local_address = addr[4][0]

    sock = socket.socket(family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((local_address, multicast_port))
    if family == AF_INET:
        for multicast_group in multicast_address[family]:
            sock.setsockopt(
                socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(multicast_group) + socket.inet_aton(local_address)
            )
    elif family == AF_INET6:
        for multicast_group in multicast_address[family]:
            ipv6mr_interface = struct.pack('i', addr[4][3])
            ipv6_mreq = socket.inet_pton(socket.AF_INET6, multicast_group) + ipv6mr_interface
            sock.setsockopt(
                socket.IPPROTO_IPV6,
                socket.IPV6_JOIN_GROUP,
                ipv6_mreq
            )
# _transport, _protocol = await loop.create_datagram_endpoint(
#     lambda: protocol_factory(), sock=sock)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM