首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 獲取本機主機名與 IP 地址的多種方法詳解

     編輯:更多程式設計知識

在網路程式設計、分散式系統、日誌記錄、授權驗證等場景中,獲取本機主機名與 IP 地址是基礎需求。Python socket 模組提供了多種介面,但行為在不同作業系統、網路環境 (多網卡、VPN、Docker、IPv6) 下差異較大。本文系統梳理各方法適用場景與注意事項。

核心 API 對照表

方法返回值適用場景注意事項
socket.gethostname()主機名 (短名稱)獲取系統設定的主機名可能非 FQDN,不含域名
socket.getfqdn()完全合格域名 (FQDN)需完整域名時依賴 /etc/hosts 或 DNS 反向解析
socket.gethostbyname(hostname)單一 IPv4 地址簡單獲取主 IP僅返回第一個 IPv4,忽略 IPv6
socket.gethostbyname_ex(hostname)(hostname, aliases, ip_list)獲取所有 IPv4返回元組,含別名列表與所有 IPv4
socket.getaddrinfo()詳細地址資訊列表支援 IPv4/IPv6、TCP/UDP、完整解析最底層、功能最全、參數最複雜

方法一:基礎組合 —— 獲取主機名與單一 IP

import socket

# 1. 獲取主機名
hostname = socket.gethostname()
print(f'主機名: {hostname}')  # 例: my-laptop

# 2. 解析為 IPv4 地址
ip = socket.gethostbyname(hostname)
print(f'IPv4 地址: {ip}')  # 例: 192.168.1.100

# 3. 獲取完整解析資訊 (含別名、所有 IPv4)
info = socket.gethostbyname_ex(hostname)
print(f'完整資訊: {info}')
# 輸出: ('my-laptop', [], ['192.168.1.100', '10.0.0.5'])

適用場景:單網卡、純 IPv4 環境、快速腳本。

局限:多網卡時只返回第一個 IP;無法獲取 IPv6;若 /etc/hosts 配置不當,可能返回 127.0.0.1。

方法二:獲取 FQDN (完全合格域名)

import socket

# 直接獲取 FQDN
fqdn = socket.getfqdn()
print(f'FQDN: {fqdn}')  # 例: my-laptop.example.com

# 或基於主機名獲取
fqdn2 = socket.getfqdn(socket.gethostname())
print(f'FQDN (from hostname): {fqdn2}')

適用場景:需要完整域名的分散式系統、SSL 憑證驗證、Kerberos 認證。

依賴:/etc/hosts 正確配置 127.0.0.1 短名稱 FQDN,或 DNS 反向解析區正常。

方法三:getaddrinfo —— 現代、完整、跨協定

import socket

# 獲取所有地址資訊 (IPv4 + IPv6, TCP + UDP)
infos = socket.getaddrinfo(socket.gethostname(), None)
# None 表示不指定埠,返回所有協定

for info in infos:
    family, socktype, proto, canonname, sockaddr = info
    ip = sockaddr[0]
    family_name = 'IPv4' if family == socket.AF_INET else 'IPv6' if family == socket.AF_INET6 else 'Other'
    type_name = 'TCP' if socktype == socket.SOCK_STREAM else 'UDP' if socktype == socket.SOCK_DGRAM else 'Other'
    print(f'{family_name} {type_name}: {ip}')

# 輸出範例:
# IPv4 TCP: 192.168.1.100
# IPv4 UDP: 192.168.1.100
# IPv6 TCP: fe80::1a2b:3c4d:5e6f:7890
# IPv6 UDP: fe80::1a2b:3c4d:5e6f:7890

進階用法:過濾特定協定族

# 僅獲取 IPv4 TCP
infos = socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET, socket.SOCK_STREAM)
ips = list(set(info[4][0] for info in infos))  # 去重
print(f'所有 IPv4: {ips}')

方法四:跨平台獲取「真實」外網/區域網 IP (推薦生產環境)

上述方法在 Docker、雲主機、多網卡、VPN 環境下常返回內部/回環地址。更可靠的做法:建立 UDP 連線到公共 DNS,取得本地外發介面 IP。

import socket

def get_local_ip():
    """獲取本機用於外部通訊的主要 IP (IPv4)"""
    try:
        # 建立 UDP socket,連線到公共 DNS (不實際發送資料)
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect(('8.8.8.8', 80))  # Google DNS
            return s.getsockname()[0]
    except Exception:
        return '127.0.0.1'

def get_all_local_ips():
    """獲取所有非回環 IPv4 地址"""
    ips = []
    try:
        infos = socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET)
        for info in infos:
            ip = info[4][0]
            if not ip.startswith('127.'):
                ips.append(ip)
    except Exception:
        pass
    return list(set(ips))

print(f'主要外發 IP: {get_local_ip()}')
print(f'所有區域網 IP: {get_all_local_ips()}')

原理:UDP connect() 不發送封包,只讓核心根據路由表選擇來源介面,getsockname() 即可取得該介面 IP。這在容器、雲環境極其可靠。

方法五:使用 psutil 獲取完整網卡資訊 (生產級監控首選)

import psutil

# 獲取所有網卡介面資訊
addrs = psutil.net_if_addrs()
stats = psutil.net_if_stats()

for iface, addr_list in addrs.items():
    iface_stat = stats.get(iface)
    print(f'\n介面: {iface} ({iface_stat.speed}Mbps, {"Up" if iface_stat.isup else "Down"})')
    for addr in addr_list:
        family_name = {socket.AF_INET: 'IPv4', socket.AF_INET6: 'IPv6', psutil.AF_LINK: 'MAC'}.get(addr.family, str(addr.family))
        print(f'  {family_name}: {addr.address}'
              f'{" Netmask: " + addr.netmask if addr.netmask else ""}'
              f'{" Broadcast: " + addr.broadcast if addr.broadcast else ""}')

輸出包含:介面名稱、狀態、速度、IPv4/IPv6/MAC、子網掩碼、廣播地址。可精確過濾實體網卡、虛擬網卡、VPN 介面。

常見問題排查表

現象可能原因解決方案
返回 127.0.0.1/etc/hosts 將主機名指向 127.0.0.1修改 /etc/hosts:127.0.0.1 localhost、實際IP 主機名 FQDN
返回 Docker 內部 IP (172.17.x.x)容器內 hostname 解析到 docker0使用 UDP 連線法 (方法四) 或 psutil 過濾介面
多網卡返回錯誤 IP路由表優先級、gethostbyname 只取第一個用 getaddrinfo 或 psutil 遍歷所有介面
IPv6 地址顯示為 fe80:: (鏈路本地)未配置全域 IPv6正常現象,全域 IPv6 需 ISP/路由器支援
權限錯誤某些系統限制 raw socket使用 psutil (無需特權) 或 UDP 連線法

最佳實踐建議

  1. 簡單腳本/單機工具:socket.gethostname() + gethostbyname()
  2. 需 FQDN 的分散式系統:socket.getfqdn(),確保 /etc/hosts 正確
  3. 容器/雲/多網卡/VPN 環境:UDP 連線法 (connect(('8.8.8.8', 80))) 獲取主外發 IP
  4. 生產監控/資產掃描/網路診斷:psutil.net_if_addrs() 獲取完整介面資訊
  5. 需支援 IPv6:socket.getaddrinfo(host, None, socket.AF_INET6)

總結

Python 獲取主機名與 IP 看似簡單,實則暗藏網路環境複雜性。理解 gethostname、getfqdn、gethostbyname*、getaddrinfo 的語義差異,掌握 UDP 連線法與 psutil 這兩大「殺手鐧」,能讓你的網路程式在從筆電到雲端叢集的各種環境中都能準確獲取身份資訊。記住:沒有萬能的單一 API,只有適合當前網路拓撲的正確選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved