用python有效获取本机IP地址

2020年4月4日 / 32次阅读 / Last Modified 2020年4月4日

编写网络程序,免不了要获取本机的IP地址,在python中,这也是一个技术活。因为似乎直接的方式总是有些让是失望,要采用迂回的方式来获取。

下面我们先看看直接的方式,使用 socket. gethostbyname 这样的函数来获取IP地址:

>>> import socket
>>> hostname = socket.gethostname()
>>> hostname
'DESKTOP-LKS6V4S'
>>> socket.gethostbyname(hostname)
'169.254.237.239'

能获取到IP地址,不过,仔细看看,这个地址有问题,169开头的,不是本机用来访问网络的地址。

原来本机上有好几个IPv4的地址,socket.gethostbyname 只是返回了排在最前面的那一个:

C:\Users\xinli>ipconfig

Windows IP 配置


以太网适配器 Npcap Loopback Adapter:

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::ed1c:eb46:6409:edef%6
   自动配置 IPv4 地址  . . . . . . . : 169.254.237.239
   子网掩码  . . . . . . . . . . . . : 255.255.0.0
   默认网关. . . . . . . . . . . . . :

无线局域网适配器 WLAN:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . :

无线局域网适配器 本地连接* 1:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . :

无线局域网适配器 本地连接* 2:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . :

以太网适配器 以太网:

   连接特定的 DNS 后缀 . . . . . . . : DHCP HOST
   本地链接 IPv6 地址. . . . . . . . : fe80::c5a8:e679:2b80:5668%11
   IPv4 地址 . . . . . . . . . . . . : 192.168.2.105
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : 192.168.2.1

以太网适配器 VMware Network Adapter VMnet1:

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::d554:a5b1:d09:d566%19
   IPv4 地址 . . . . . . . . . . . . : 192.168.153.1
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . :

以太网适配器 VMware Network Adapter VMnet8:

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::8cbf:c78a:18a1:4705%17
   IPv4 地址 . . . . . . . . . . . . : 192.168.78.1
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . :

python的socket模块,还有一个 gethostbyname_ex 函数,可以试一下:

>>> socket.gethostbyname_ex(hostname)
('DESKTOP-LKS6V4S', [], ['169.254.237.239', '192.168.78.1', '192.168.153.1', '192.168.2.105'])

稍微好一点,这个函数把本机所有的IPv4地址都返回了,但是我们要使用哪一个区访问Internet,还是不得而知。gethostbyname* 函数只能支持IPv4,如果要支持IPv6,需要用到 getaddrinfo 函数:

>>> socket.getaddrinfo(hostname, None)
[(, 0, 0, '', ('fe80::ed1c:eb46:6409:edef', 0, 0, 6)), (, 0, 0, '', ('fe80::8cbf:c78a:18a1:4705', 0, 0, 17)), (, 0, 0, '', ('fe80::d554:a5b1:d09:d566', 0, 0, 19)), (, 0, 0, '', ('fe80::c5a8:e679:2b80:5668', 0, 0, 11)), (, 0, 0, '', ('169.254.237.239', 0)), (, 0, 0, '', ('192.168.78.1', 0)), (, 0, 0, '', ('192.168.153.1', 0)), (, 0, 0, '', ('192.168.2.105', 0))]

getaddrinfo将本地所有IP地址都返回了。

回到正题,我们应该如何有效获取本机的可以连接Internet的IPv4地址呢?我的答案是,可以采用一个迂回的方式,通过创建一个UDP socket,来间接获取可以访问Internet的那个地址(python底层是怎么运作的,我还没搞清楚):

def get_network_ip():
    """get the local network ip, not loopback 127.*"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('1.1.1.1',80))
    ip = s.getsockname()[0]
    s.close()
    return ip

创建一个UDP,随便去连接一个什么地址,然后获取本机的IPv4地址!此方法在Window和Linux下都很好用,亲测!

-- EOF --

本文链接:https://www.pynote.net/archives/1841

相关文章

    留言区

    电子邮件地址不会被公开。 必填项已用*标注


    前一篇:
    后一篇:

    More


    ©Copyright 麦新杰 Since 2019 Python笔记

    go to top