前言
引出问题:
在参考网上获取 IP 地址的代码,具体实现如下:
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915,
struct.pack('256s', ifname[:15]))[20:24])
get_ip_address('eth0')
由于使用的是 Window 系统,所以不可避免的出现了错误,报错如下:
from fcntl import ioctl
ImportError: No module named fcntl
网上搜索相关解决方法,因为 Python 缺少对应的 fcntl 模块,这个模块是 Python 自带的,但 Windows 好像没有。由于代码实现比较简单,网上就给了该模块的实现代码,如下:
def fcntl(fd, op, arg=0):
return 0
def ioctl(fd, op, arg=0, mutable_flag=True):
if mutable_flag:
return 0
else:
return ""
def flock(fd, op):
return
def lockf(fd, operation, length=0, start=0, whence=0):
return
将该部分代码保存为 fcntl.py,并存放在 Python 的安装目录下,类似于:
D:\Soft_Install\Python37\Lib
。
终于不再报上述的错误后,但是获取 IP 仍然存在问题。
struct.pack('256s', IFNAME[:15])
struct.error: argument for 's' must be a bytes object
修改成如下模式:
struct.pack(b'256s', ifname[:15].encode("utf-8"))
,再次执行,得到如下结果:
TypeError: 'int' object is not subscriptable
究其原因,是因为
fcntl.ioctl(s.fileno(),0x8915,struct.pack(b'256s', ifname[:15].encode("utf-8")))
返回结果为 0。
网上再次搜索了很久,都没有找到合适的解决方案,因此决定换个方式实现获取 IP 地址。
分析
IP 获取
通过 Python 代码实现 IP 地址获取,首先需要在本机上找到需要获取的 IP 地址。获取 IP 的手段简单分为两种,分别在百度搜 ip 查询,查询到本机的 public ip 如第一张图,再利用 ipconfig 查询到自身 ip 如第二张图。我们能看到这两个地址是不同的,那么不是说,每个主机都只有一个 ip 地址么,为什么我们查到的两个 Ip 地址不一样呢。
ipconfig 查出来的是你本机的 IP 地址,也就是内网私有地址,此类地址仅在局域网使用,不能联通外网。
百度查出来的地址是你上网的共有地址,也许并不是你主机的地址,而是电信或联通分给你的地址,用于连接互联网。
Python 代码实现
一、Windows 上实现
import os
import socket
def get_window_ip1():
"""
查询本机ip地址
:return: ip
"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
print(s.getsockname())
ip = s.getsockname()[0]
finally:
s.close()
return ip
def get_window_ip2():
# 获取本机计算机名称
hostname = socket.gethostname()
# 需要注意的是,如果本机有多网卡,比如说安装的有虚拟机,则此ip可能是虚拟机VMnet8的ip,而你真正的内网IP可能是物理无线适配器wlan的IP
ip = socket.gethostbyname(hostname)
#这种情况下,首先需要获取所有网卡的ip地址,然后人为进行筛选
ipList = socket.gethostbyname_ex(hostname)
return ipList[2][-1]
def get_window_ip3():
return [a for a in os.popen('route print').readlines() if ' 0.0.0.0 ' in a][0].split()[-2]
def get_window_ip4():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('baidu.com', 0))
ipaddr = s.getsockname()[0]
return ipaddr
二、Linux 上实现
1、
def get_ip_address():
# 获取本机计算机名称
hostname = socket.gethostname()
ip = socket.gethostbyname(hostname)
#下面这两种种方法同样可以
# fqdnName = socket.getfqdn(socket.gethostname())
# ip = socket.gethostbyname(fqdnName)
# s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# s.connect(('baidu.com', 0))
# ip = s.getsockname()[0]
return ip
同在 Windows 上使用,上述方法在 Linux 下也可以获取到正确的 IP 地址。
2、
import socket
import fcntl
import struct
IFNAME = 'eth0'
def get_ip_address(ifname=IFNAME):
'''获取网卡的ip地址'''
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915,
struct.pack('256s', ifname[:15])
)[20:24])
如果遇到报错
IOError: [Errno 19] No such device
,说明当前 Linux 系统没有 eth0 网卡。在之前的博客 Centos7 使用 yum 命令遇到的问题总结一文中,为了联网修改网卡配置文件,当时修改的是叫做 ens33 的网卡,可以通过
ip addr
命令查看当前系统的网卡信息。在验证 IP 获取代码的过程中,将“enth0”改为“ens33”,即可获取正确 IP 信息。如果想要将 ens33 网卡改为 eth0 网卡,可以参考 CentOS7没有eth0网卡一文进行修改。
3、
def get_ip_address():
# Use ip route list
import subprocess
arg = 'ip route list'
p = subprocess.Popen(arg, shell=True, stdout=subprocess.PIPE)
data = p.communicate()
sdata = data[0].split()
ipaddr = sdata[sdata.index('src') + 1] #ip
netdev = sdata[sdata.index('dev') + 1] #网卡信息
return (ipaddr, netdev)