在網路程式設計、分散式系統、日誌記錄、授權驗證等場景中,獲取本機主機名與 IP 地址是基礎需求。Python socket 模組提供了多種介面,但行為在不同作業系統、網路環境 (多網卡、VPN、Docker、IPv6) 下差異較大。本文系統梳理各方法適用場景與注意事項。
| 方法 | 返回值 | 適用場景 | 注意事項 |
|---|---|---|---|
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、完整解析 | 最底層、功能最全、參數最複雜 |
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。
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}')
上述方法在 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 連線法 |
socket.gethostname() + gethostbyname()socket.getfqdn(),確保 /etc/hosts 正確connect(('8.8.8.8', 80))) 獲取主外發 IPpsutil.net_if_addrs() 獲取完整介面資訊socket.getaddrinfo(host, None, socket.AF_INET6)Python 獲取主機名與 IP 看似簡單,實則暗藏網路環境複雜性。理解 gethostname、getfqdn、gethostbyname*、getaddrinfo 的語義差異,掌握 UDP 連線法與 psutil 這兩大「殺手鐧」,能讓你的網路程式在從筆電到雲端叢集的各種環境中都能準確獲取身份資訊。記住:沒有萬能的單一 API,只有適合當前網路拓撲的正確選擇。