【问题标题】:How do I determine all of my IP addresses when I have multiple NICs?当我有多个 NIC 时,如何确定我的所有 IP 地址?
【发布时间】:2010-09-21 05:00:07
【问题描述】:

我的计算机上有多个网络接口卡,每个都有自己的 IP 地址。

当我使用 Python(内置)socket 模块中的gethostbyname(gethostname()) 时,它只会返回其中一个。我如何获得其他人?

【问题讨论】:

  • 您能否将标题中的“地址”更改为“地址”,因为它更好地反映了问题(乍一看;多个 NIC -> 多个 IP 地址)。
  • 我本来打算将评论设为“完成”,但它太短了,最多需要 10 个字符。
  • 依赖主机名解析是错误的。您不需要主机名来列出 IP 地址,这些概念只是远程相关的。

标签: python sockets ip-address


【解决方案1】:

为了完整起见,另一种选择是使用psutil

tldr;

import socket
import psutil

def get_ip_addresses(family):
    for interface, snics in psutil.net_if_addrs().items():
        for snic in snics:
            if snic.family == family:
                yield (interface, snic.address)

ipv4s = list(get_ip_addresses(socket.AF_INET))
ipv6s = list(get_ip_addresses(socket.AF_INET6))

说明

你需要的函数是net_if_addrs。即:

import psutil
psutil.net_if_addrs()

这会导致这样的事情(Python 3):

{'br-ae4880aa80cf': [snic(family=<AddressFamily.AF_INET: 2>, address='172.18.0.1', netmask='255.255.0.0', broadcast='172.18.0.1', ptp=None),
                     snic(family=<AddressFamily.AF_PACKET: 17>, address='02:42:e5:ae:39:94', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'docker0': [snic(family=<AddressFamily.AF_INET: 2>, address='172.17.0.1', netmask='255.255.0.0', broadcast='172.17.0.1', ptp=None),
             snic(family=<AddressFamily.AF_PACKET: 17>, address='02:42:38:d2:4d:77', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'eno1': [snic(family=<AddressFamily.AF_PACKET: 17>, address='54:be:f7:0b:cf:a9', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
        snic(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
 'wlp2s0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.4', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
            snic(family=<AddressFamily.AF_PACKET: 17>, address='00:21:27:ee:d6:03', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}

(Python 2):

{'br-ae4880aa80cf': [snic(family=2, address='172.18.0.1', netmask='255.255.0.0', broadcast='172.18.0.1', ptp=None),
                     snic(family=17, address='02:42:e5:ae:39:94', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'docker0': [snic(family=2, address='172.17.0.1', netmask='255.255.0.0', broadcast='172.17.0.1', ptp=None),
             snic(family=17, address='02:42:38:d2:4d:77', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'eno1': [snic(family=17, address='54:be:f7:0b:cf:a9', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)],
 'lo': [snic(family=2, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None),
        snic(family=17, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)],
 'wlp2s0': [snic(family=2, address='192.168.1.4', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
            snic(family=17, address='00:21:27:ee:d6:03', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}

注意:由于每个接口可以有多个同族的地址,因此字典值是列表。

每个snic 是一个namedtuple,其中包括5 个字段:

  • family:地址族,AF_INETAF_INET6psutil.AF_LINK,指的是 MAC 地址。
  • address:主网卡地址(始终设置)。
  • netmask:网络掩码地址(可能为无)。
  • broadcast:广播地址(可以是None)。
  • ptp:代表“点对点”;它是点对点接口(通常是 VPN)上的目标地址。 broadcast 和 ptp 是互斥的(可能是 None)。

【讨论】:

  • 因为 netifaces 在树莓派上的 conda 发行版中不可用,我非常喜欢这种方法。跨平台(在 windows 和 raspberry pi raspbian 8 上测试)。
【解决方案2】:

使用netifaces 模块。因为网络很复杂,所以使用 netifaces 可能有点棘手,但这里是你想做的事情:

>>> import netifaces
>>> netifaces.interfaces()
['lo', 'eth0']
>>> netifaces.ifaddresses('eth0')
{17: [{'broadcast': 'ff:ff:ff:ff:ff:ff', 'addr': '00:11:2f:32:63:45'}], 2: [{'broadcast': '10.0.0.255', 'netmask': '255.255.255.0', 'addr': '10.0.0.2'}], 10: [{'netmask': 'ffff:ffff:ffff:ffff::', 'addr': 'fe80::211:2fff:fe32:6345%eth0'}]}
>>> for interface in netifaces.interfaces():
...   print netifaces.ifaddresses(interface)[netifaces.AF_INET]
...
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]
[{'broadcast': '10.0.0.255', 'netmask': '255.255.255.0', 'addr': '10.0.0.2'}]
>>> for interface in netifaces.interfaces():
...   for link in netifaces.ifaddresses(interface)[netifaces.AF_INET]:
...     print link['addr']
...
127.0.0.1
10.0.0.2

可以像这样使它更具可读性:

from netifaces import interfaces, ifaddresses, AF_INET

def ip4_addresses():
    ip_list = []
    for interface in interfaces():
        for link in ifaddresses(interface)[AF_INET]:
            ip_list.append(link['addr'])
    return ip_list

如果您需要 IPv6 地址,请使用 AF_INET6 而不是 AF_INET。如果您想知道为什么netifaces 到处都使用列表和字典,那是因为一台计算机可以有多个 NIC,每个 NIC 可以有多个地址,每个地址都有自己的一组选项。

【讨论】:

  • 附言。与我给出的另一个高度评价的答案不同,这个答案已经过全面测试。那个(非常错误的)答案已被删除。
  • 简短说明:在我的本地计算机上,netifaces.ifaddresses 在某些情况下可能会返回空字典,因此在尝试访问 AF_INET 密钥时代码将失败。小修小补,但其他方面的答案很好。
  • 我已将其更改为for link in ifaddresses(interface).get(AF_INET, ())。我也忽略了一些像'lo0'这样的接口。
  • @HarleyHolcombe - 如果 NIC 没有 IPv4 地址,ip4_addresses() 函数会抛出 KeyError,并且应该对 AF_INET 和“addr”使用 dict.get(key) 形式。我通过修复提交了对您的答案的修改,但我的修改被拒绝了 (???)。
【解决方案3】:

正如这个线程所表明的,有很多方法可以达到相同的结果,我建议的方法是利用getaddrinfo() 中的内置家庭过滤器并像这样解析标准化的元组:

from socket import getaddrinfo, AF_INET, gethostname

for ip in getaddrinfo(host=gethostname(), port=None, family=AF_INET):   
    print(ip[4][0])

示例输出:

192.168.55.1
192.168.170.234

【讨论】:

  • 不回我127.0.0.1s
【解决方案4】:

我认为@Harley Holcombe 的答案是可行的,但如果你有一些没有 ip 的虚拟网卡,它会出错。所以这是我修改的:

def get_lan_ip():
for interface in interfaces():
    try:
        for link in ifaddresses(interface)[AF_INET]:
            if str(link['addr']).startswith("172."):
                return str(link['addr'])
    except:
        pass

这只会返回你的局域网 ipv4

【讨论】:

    【解决方案5】:

    这个 sn-p 将给出系统中所有可用 IPV4 地址的列表。

    import itertools
    from netifaces import interfaces, ifaddresses, AF_INET
    
    links = filter(None, (ifaddresses(x).get(AF_INET) for x in interfaces()))
    links = itertools.chain(*links)
    ip_addresses = [x['addr'] for x in links]
    

    【讨论】:

      【解决方案6】:

      你可以很容易地做到这一点:

      import netifaces
      
      for interface in netifaces.interfaces():
          print netifaces.ifaddresses(interface)
      

      有关更多信息,您可以查找netifaces documentation

      【讨论】:

        【解决方案7】:

        netifaces 模块的帮助下将所有地址放在一行中:

        [netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for iface in netifaces.interfaces() if netifaces.AF_INET in netifaces.ifaddresses(iface)]
        

        【讨论】:

        • 感谢@Elemag。这适用于 Python 解释器,但当我将其保存到 .py stackoverflow.com/questions/49195864/…
        • @Sabrina 你必须对结果做点什么,比如打印出来。代码执行,但没有以任何方式可视化
        【解决方案8】:

        https://docs.python.org/3.4/library/socket.html#socket.if_nameindex

        socket.if_nameindex()

        返回网络接口信息(索引 int,名称字符串)元组的列表。 OSError 如果系统调用失败。

        可用性:Unix。

        3.3 版中的新功能。


        制作了可在 Python 3.4、UNIX / Linux 上运行的代码

        #!/env/python3.4
        import socket
        import fcntl
        import struct
        
        def active_nic_addresses():
            """
            Return a list of IPv4 addresses that are active on the computer.
            """
        
            addresses = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1]
        
            return addresses
        
        def get_ip_address( NICname ):
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            return socket.inet_ntoa(fcntl.ioctl(
                s.fileno(),
                0x8915,  # SIOCGIFADDR
                struct.pack('256s', NICname[:15].encode("UTF-8"))
            )[20:24])
        
        
        def nic_info():
            """
            Return a list with tuples containing NIC and IPv4
            """
            nic = []
        
            for ix in socket.if_nameindex():
                name = ix[1]
                ip = get_ip_address( name )
        
                nic.append( (name, ip) )
        
            return nic
        
        if __name__ == "__main__":
        
            print( active_nic_addresses() )
            print( nic_info() )
        

        将打印如下内容:

        ['192.168.0.2']
        [('lo', '127.0.0.1'), ('enp3s0', '192.168.0.2')]
        

        【讨论】:

          【解决方案9】:
          import socket
          [i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
          

          【讨论】:

          • 无需额外模块即可工作; Python 2.7,Ubuntu Linux。
          • 这里只是列出了与外界相连的接口的地址,并不是所有的接口。
          • 这使用名称解析——期望它与网络接口匹配。这不是一个不合理的期望,但它可能会失败。提问者想要的——即使他没有意识到——相当于ifconfig 的输出。
          • 使用{i[4][0] for ... , None)} 获取一组唯一的地址。如果您需要一个列表,请将其包装在 list(...) 调用中。
          • 我希望我能对此表示赞同,但正如@PaulHoffman 指出的那样,它不会返回所有接口的 IP。我需要一个标准库方法来这样做,或者跳到解析ip a 的输出并称之为一天。
          【解决方案10】:

          这是查找所有 IPv4 和 IPv6 接口的例程。正如之前的发帖人所指出的,socket.gethostbyname_ex() 不适用于 IPv6,Python 文档建议改用 socket.getaddressinfo()

          此例程添加回调 IPv4 接口 (127.0.0.1),如果有任何 IPv6 接口,则它还添加回调 IPv6 接口 (::1)。在我的机器上,socket.getaddrinfo() 会给我一个或两个,但前提是我没有其他可用的接口。

          出于我的需要,我想尝试在每个可用接口上的指定端口上打开一个 UDP 套接字,这就是代码中包含“端口”和 socket.SOCK_DGRAM 的原因。更改这些是安全的,例如如果您没有考虑到端口。

          addrinfo_ipv4 = socket.getaddrinfo(hostname,port,socket.AF_INET,socket.SOCK_DGRAM)
          addrinfo_ipv6 = []
          try:
              addrinfo_ipv6 = socket.getaddrinfo(hostname,port,socket.AF_INET6,socket.SOCK_DGRAM)
          except socket.gaierror:
              pass
          addrinfo = [(f,t,a) for f,t,p,cn,a in addrinfo_ipv4+addrinfo_ipv6]
          addrinfo_local = [(socket.AF_INET,socket.SOCK_DGRAM,('127.0.0.1',port))]
          if addrinfo_ipv6: 
              addrinfo_local.append( (socket.AF_INET6,socket.SOCK_DGRAM,('::1',port)) )
          [addrinfo.append(ai) for ai in addrinfo_local if ai not in addrinfo]
          

          【讨论】:

            【解决方案11】:

            它只是 linux,但这里有一个非常简单的配方 http://code.activestate.com/recipes/439094/

            它可能使用与另一个答案中提到的netifaces package 类似的代码(但当前版本链接在这里)

            socket.getaddrinfo() 实际上并不返回设备绑定的 IP 地址。如果您的 hosts 文件包含“127.0.1.1 yourhost.example.com yourhost”行,这是一个常见配置,getaddrinfo 只会返回 127.0.1.1。

            【讨论】:

              【解决方案12】:

              您应该直接获取所有 IP 配置的 IP 地址,例如通过运行 ifconfig 并解析其输出(也可以执行 ifconfig does directly in Pythonsee how it is done in C 的操作)。如果您需要主机名,请使用 gethostbyaddr。

              【讨论】:

              • 解析ifconfig高度依赖操作系统,甚至版本依赖。
              猜你喜欢
              • 2013-08-24
              • 2012-01-07
              • 1970-01-01
              • 1970-01-01
              • 2015-12-25
              • 2018-05-03
              • 2010-10-04
              • 2015-01-24
              • 1970-01-01
              相关资源
              最近更新 更多