簡體   English   中英

在 Python 中選擇多播網絡接口

[英]Choosing multicast network interface in Python

我有一個帶有兩個獨立以太網連接的服務器。 當我在 python 中綁定一個套接字時,它默認為兩個網絡之一。 如何從 Python 中的第二個網絡中提取多播流? 我曾嘗試在第二個網絡上使用服務器的 IP 地址調用 bind,但這沒有用。

我建議您不要使用 INADDR_ANY。 在生產多播環境中,您希望對多播套接字非常具體,並且不想做諸如發送 igmp 加入所有接口之類的事情。 當事情不能像“route add -host 239.1.1.1 dev eth3”那樣根據相關系統正確進行多播連接時,這會導致hack-job解決方法。 改用這個:

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

在 mcastsock.bind 中,您也可以使用 '' 代替地址字符串,但我建議不要這樣做。 使用'',如果您有另一個使用相同端口的套​​接字,則兩個套接字都將獲取彼此的數據。

bind套接字時,請嘗試此處提到的值:

對於 IPv4 地址,接受兩種特殊形式而不是主機地址:空字符串表示 INADDR_ANY,字符串 '' 表示 INADDR_BROADCAST。

INADDR_ANY也稱為通配符地址:

具有通配本地地址的套接字可以接收定向到指定端口號並尋址到分配給主機的任何可能地址的消息

更多在這里

我想通了。 事實證明,我缺少的部分是將接口添加到 mreq 結構中,該結構用於向多播組添加成員資格。

對於 IPv4,網絡接口的索引是 IP 地址; 對於 IPv6,網絡接口的索引由方法 socket.getaddrinfo 返回。

下面的代碼顯示了如何在所有網絡接口上監聽多播:

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