如何在有多个网卡时确定所有IP地址?

46 投票
14 回答
55886 浏览
提问于 2025-04-11 09:35

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

当我在Python的内置socket模块中使用gethostbyname(gethostname())时,它只会返回其中一个IP地址。我该怎么才能获取到其他的IP地址呢?

14 个回答

16

为了完整性,另一个选择是使用 psutil

简而言之;

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:子网掩码地址(可能为None)。
  • broadcast:广播地址(可能为None)。
  • ptp:代表“点对点”;这是在点对点接口上的目标地址(通常是VPN)。广播和ptp是互斥的(可能为None)。
24

在编程中,有时候我们需要让程序在特定的条件下执行某些操作。这就像给程序设定了一些规则,只有当这些规则被满足时,程序才会继续运行。

比如说,你可能希望程序在用户输入正确的密码后才能进入系统。这种情况下,你就需要用到条件判断。条件判断就像是在问一个问题:如果用户输入的密码是对的,那就让他进来;如果不对,就告诉他再试一次。

在代码中,这种判断通常用“if”语句来实现。你可以把“if”想象成一个门,只有当条件满足时,这扇门才会打开,让程序继续执行后面的代码。

总之,条件判断是编程中非常重要的一部分,它帮助我们控制程序的运行流程,让程序能够根据不同的情况做出不同的反应。

import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
65

使用 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),而每个网络接口卡又可以有多个地址,每个地址还有自己的一些选项。

撰写回答