前言
引出問題:
在參考網上獲取 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)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
