【问题标题】:How to select a network interface for multicasting with python如何使用python选择用于多播的网络接口
【发布时间】:2020-04-17 07:32:56
【问题描述】:

我正在使用此代码通过 Wlan 网络使用我的设备无线 IP 地址多播一条消息:

import socket

MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
IP=''

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('8.8.8.8', 1))
    IP = s.getsockname()[0]
    print(IP)
    s.close()

except:
    print("Could not get IP")
    s.close()

try:
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    udp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
    udp_socket.sendto(IP, (MCAST_GRP, MCAST_PORT))
    udp_socket.close()
    print("Multicast Message sent.")

except:
    print("Message not sent")
    udp_socket.close()

所有设备都通过无线路由器连接,路由器允许多播消息。该代码正在获取设备的 IP 并将其以多播消息的形式发送。这些设备混合了 Linux Debian 8 和 Windows 10 操作系统。

在设备中唯一活动的网络接口是无线的,这可以立即工作。问题是有时有多个活动网络接口可用,如果可用,我需要选择无线网络接口。我尝试降低无线接口的 metric 参数的值,它起作用了,但我必须手动执行此操作,我需要它自动化。

我想知道如何补充我的代码,以便能够选择所需的网络接口(希望以可移植的方式)、多播消息并接收它们。提前致谢。

【问题讨论】:

    标签: python networking network-programming


    【解决方案1】:

    在连接之前从所需接口绑定到 IP 似乎对大多数人都有效。诀窍是获得该IP。 更多细节在 Can Python select what network adapter when opening a socket?

    还有How can I get the IP address of eth0 in Python?

    【讨论】:

    • 感谢您带领我朝着正确的方向前进。在获得所需接口的 IP 地址后,在 windows 和 Linux 中,我对 windows 接口使用了 bind 选项,对 Linux 接口使用了 SO_BINDTODEVICE(需要 su 或 sudo 权限才能执行)。现在一切正常,通过所需的接口在所需的 IP 中发送和接收多播消息。
    【解决方案2】:

    您可以尝试使用以下脚本检索 IPv4/IPv6 网络接口的信息。希望这会有所帮助。

    # -*- coding: utf-8 -*-
    from ctypes import *
    from sys import platform
    from socket import AF_INET, AF_INET6, inet_ntop
    
    try:
        from socket import AF_PACKET
    except ImportError:
        AF_PACKET = -1
    
    if platform.startswith("darwin") or platform.startswith("freebsd"):
        AF_LINK = 18
        IFT_ETHER = 0x6
    else:
        AF_LINK = -1
        IFT_ETHER = -1
    
    
    def get_if_addresses():
        """
        Retrieve all relevant information of IPv4/IPv6 network interfaces.
        Based on http://pastebin.com/wxjai3Mw (Author unknown)
        """
    
        # getifaddr structs
        class ifa_ifu_u(Union):
            _fields_ = [
                ("ifu_broadaddr", c_void_p),
                ("ifu_dstaddr", c_void_p)
            ]
    
        class ifaddrs(Structure):
            _fields_ = [
                ("ifa_next", c_void_p),
                ("ifa_name", c_char_p),
                ("ifa_flags", c_uint),
                ("ifa_addr", c_void_p),
                ("ifa_netmask", c_void_p),
                ("ifa_ifu", ifa_ifu_u),
                ("ifa_data", c_void_p)
            ]
    
        # AF_UNKNOWN / generic
        if platform.startswith("darwin") or platform.startswith("freebsd"):
            class sockaddr(Structure):
                _fields_ = [
                    ("sa_len", c_uint8),
                    ("sa_family", c_uint8),
                    ("sa_data", (c_uint8 * 14))
                ]
        else:
            class sockaddr(Structure):
                _fields_ = [
                    ("sa_family", c_uint16),
                    ("sa_data", (c_uint8 * 14))
                ]
    
        # AF_INET / IPv4
        class in_addr(Union):
            _fields_ = [
                ("s_addr", c_uint32),
            ]
    
        if platform.startswith("darwin") or platform.startswith("freebsd"):
            class sockaddr_in(Structure):
                _fields_ = [
                    ("sin_len", c_uint8),
                    ("sin_family", c_uint8),
                    ("sin_port", c_ushort),
                    ("sin_addr", in_addr),
                    ("sin_zero", (c_char * 8))  # padding
                ]
        else:
            class sockaddr_in(Structure):
                _fields_ = [
                    ("sin_family", c_short),
                    ("sin_port", c_ushort),
                    ("sin_addr", in_addr),
                    ("sin_zero",  (c_char * 8))  # padding
                ]
    
        # AF_INET6 / IPv6
        class in6_u(Union):
            _fields_ = [
                ("u6_addr8", (c_uint8 * 16)),
                ("u6_addr16", (c_uint16 * 8)),
                ("u6_addr32", (c_uint32 * 4))
            ]
    
        class in6_addr(Union):
            _fields_ = [
                ("in6_u", in6_u),
            ]
    
        if platform.startswith("darwin") or platform.startswith("freebsd"):
            class sockaddr_in6(Structure):
                _fields_ = [
                    ("sin6_len", c_uint8),
                    ("sin6_family", c_uint8),
                    ("sin6_port", c_ushort),
                    ("sin6_flowinfo", c_uint32),
                    ("sin6_addr", in6_addr),
                    ("sin6_scope_id", c_uint32),
                ]
        else:
            class sockaddr_in6(Structure):
                _fields_ = [
                    ("sin6_family", c_short),
                    ("sin6_port", c_ushort),
                    ("sin6_flowinfo", c_uint32),
                    ("sin6_addr", in6_addr),
                    ("sin6_scope_id", c_uint32),
                ]
    
        # AF_PACKET / Linux
        class sockaddr_ll(Structure):
            _fields_ = [
                ("sll_family", c_uint16),
                ("sll_protocol", c_uint16),
                ("sll_ifindex", c_uint32),
                ("sll_hatype", c_uint16),
                ("sll_pktype", c_uint8),
                ("sll_halen", c_uint8),
                ("sll_addr", (c_uint8 * 8))
            ]
    
        # AF_LINK / BSD|OSX
        class sockaddr_dl(Structure):
            _fields_ = [
                ("sdl_len", c_uint8),
                ("sdl_family", c_uint8),
                ("sdl_index", c_uint16),
                ("sdl_type", c_uint8),
                ("sdl_nlen", c_uint8),
                ("sdl_alen", c_uint8),
                ("sdl_slen", c_uint8),
                ("sdl_data", (c_uint8 * 46))
            ]
    
        if platform.startswith("darwin"):
            libc = CDLL("libSystem.dylib")
    
        elif platform.startswith("freebsd"):
            libc = CDLL("libc.so")
    
        else:
            libc = CDLL("libc.so.6")
    
        ptr = c_void_p(None)
        result = libc.getifaddrs(pointer(ptr))
        if result:
            return None
        ifa = ifaddrs.from_address(ptr.value)
        result = []
    
        while ifa:
            # Python 2 gives us a string, Python 3 an array of bytes
            if type(ifa.ifa_name) is str:
                name = ifa.ifa_name
            else:
                name = ifa.ifa_name.decode()
    
            if ifa.ifa_addr:
                sa = sockaddr.from_address(ifa.ifa_addr)
    
                data = {}
    
                if sa.sa_family == AF_INET:
                    if ifa.ifa_addr is not None:
                        si = sockaddr_in.from_address(ifa.ifa_addr)
                        data['addr'] = inet_ntop(AF_INET, si.sin_addr)
                    if ifa.ifa_netmask is not None:
                        si = sockaddr_in.from_address(ifa.ifa_netmask)
                        data['netmask'] = inet_ntop(AF_INET, si.sin_addr)
    
                    # check if a valid broadcast address is set and retrieve it
                    # 0x2 == IFF_BROADCAST
                    if ifa.ifa_flags & 0x2:
                        si = sockaddr_in.from_address(ifa.ifa_ifu.ifu_broadaddr)
                        data['broadcast'] = inet_ntop(AF_INET, si.sin_addr)
    
                if sa.sa_family == AF_INET6:
                    if ifa.ifa_addr is not None:
                        si = sockaddr_in6.from_address(ifa.ifa_addr)
                        data['addr'] = inet_ntop(AF_INET6, si.sin6_addr)
    
                        if data['addr'].startswith('fe80:'):
                            data['scope'] = si.sin6_scope_id
    
                    if ifa.ifa_netmask is not None:
                        si = sockaddr_in6.from_address(ifa.ifa_netmask)
                        data['netmask'] = inet_ntop(AF_INET6, si.sin6_addr)
    
                if sa.sa_family == AF_PACKET:
                    if ifa.ifa_addr is not None:
                        si = sockaddr_ll.from_address(ifa.ifa_addr)
                        addr = ""
                        total = 0
                        for i in range(si.sll_halen):
                            total += si.sll_addr[i]
                            addr += "%02x:" % si.sll_addr[i]
                        addr = addr[:-1]
                        if total > 0:
                            data['addr'] = addr
    
                if sa.sa_family == AF_LINK:
                    dl = sockaddr_dl.from_address(ifa.ifa_addr)
    
                    if dl.sdl_type == IFT_ETHER:
                        addr = ""
                        for i in range(dl.sdl_alen):
                            addr += "%02x:" % dl.sdl_data[dl.sdl_nlen + i]
    
                        addr = addr[:-1]
                        data['addr'] = addr
    
                if len(data) > 0:
                    iface = {}
                    for interface in result:
                        if name in interface.keys():
                            iface = interface
                            break
                    if iface:
                        iface[name][sa.sa_family] = data
                    else:
                        iface[name] = { sa.sa_family : data }
                        result.append(iface)
    
            if ifa.ifa_next:
                ifa = ifaddrs.from_address(ifa.ifa_next)
            else:
                break
    
        libc.freeifaddrs(ptr)
        return result
    

    【讨论】:

      【解决方案3】:

      已在 Linux Debian 8 和 Windows 10 中使用 Python 2.7 进行测试。

      对于 Linux:

      def get_local_wireless_ip_linux():
          import fcntl
          import struct
          import socket
          ifname = 'wlan0'
          s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
          wlan0_ip = (socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24]))
          return wlan0_ip
      

      对于 Windows:

      def get_local_wireless_ip_windows():
          import subprocess
          arp = subprocess.check_output('arp -a')
          local_ipv4 = []
          for line in arp.split('\n'):
              if 'Interface' in line:
                  local_ipv4.append(line.split(':')[1].split('---')[0].strip())
          return local_ipv4[-1]
      

      【讨论】:

        猜你喜欢
        • 2011-04-21
        • 2011-02-13
        • 1970-01-01
        • 1970-01-01
        • 2012-11-16
        • 2015-07-12
        • 2019-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多