在多網卡環境(如同時擁有乙太網路、Wi-Fi、VPN、虛擬網卡)下,精確獲取指定網卡的 IP 位址是常見需求。本文提供完整實作,支援 IPv4/IPv6、子介面遍歷、回環/虛擬網卡過濾。
package com.example.network;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.List;
public class NetworkInterfaceUtil {
/**
* 根據網卡名稱獲取所有 IP 位址
* @param interfaceName 網卡名稱 (如 "eth0", "wlan0", "lo")
* @return IP 位址列表,包含 IPv4 與 IPv6
*/
public static List<InetAddress> getInetAddressesByName(String interfaceName) {
List<InetAddress> addresses = new ArrayList<>();
try {
NetworkInterface ni = NetworkInterface.getByName(interfaceName);
if (ni == null) {
System.err.println("找不到網卡: " + interfaceName);
return addresses;
}
Enumeration<InetAddress> inetAddresses = ni.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
addresses.add(inetAddresses.nextElement());
}
// 遞迴處理子介面
addresses.addAll(getSubInterfaceAddresses(ni));
} catch (SocketException e) {
e.printStackTrace();
}
return addresses;
}
/**
* 遞迴獲取子介面 IP
*/
private static List<InetAddress> getSubInterfaceAddresses(NetworkInterface parent) {
List<InetAddress> subAddresses = new ArrayList<>();
try {
Enumeration<NetworkInterface> subInterfaces = parent.getSubInterfaces();
while (subInterfaces.hasMoreElements()) {
NetworkInterface sub = subInterfaces.nextElement();
Enumeration<InetAddress> addrs = sub.getInetAddresses();
while (addrs.hasMoreElements()) {
subAddresses.add(addrs.nextElement());
}
// 繼續遞迴更深層子介面
subAddresses.addAll(getSubInterfaceAddresses(sub));
}
} catch (SocketException e) {
e.printStackTrace();
}
return subAddresses;
}
/**
* 列出所有網卡與 IP(除錯用)
*/
public static void listAllInterfaces() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
System.out.println("=== 網卡: " + ni.getName() + " (顯示名: " + ni.getDisplayName() + ") ===");
Enumeration<InetAddress> addrs = ni.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
System.out.println(" IP: " + addr.getHostAddress() + " [" + (addr instanceof java.net.Inet4Address ? "IPv4" : "IPv6") + "]");
}
}
} catch (SocketException e) {
e.printStackTrace();
}
}
// 測試主程式
public static void main(String[] args) {
// 列出所有網卡供參考
listAllInterfaces();
System.out.println("\n--- 獲取 lo (回環) 介面 IP ---");
List<InetAddress> loAddresses = getInetAddressesByName("lo");
for (InetAddress addr : loAddresses) {
System.out.println("IP: " + addr.getHostAddress());
}
}
}
| 方法 | 用途 | 備註 |
|---|---|---|
NetworkInterface.getByName(name) | 直接按名稱查找網卡 | 比遍歷所有網卡效率高 |
getInetAddresses() | 獲取該介面綁定的所有 IP | 包含 IPv4、IPv6、連結本地址 |
getSubInterfaces() | 獲取虛擬/子介面 (如 eth0:1) | Linux 常見多 IP 配置場景 |
isUp() / isLoopback() / isVirtual() | 過濾啟用/回環/虛擬介面 | 實際部署建議加入過濾邏輯 |
| 作業系統 | 實體網卡 | 回環介面 | 虛擬介面 |
|---|---|---|---|
| Linux | eth0, ens33, enp3s0 | lo | eth0:1, docker0, vethxxx |
| Windows | 乙太網路, Wi-Fi, 本地連線* | Loopback Pseudo-Interface 1 | Hyper-V, VMware Network Adapter |
| macOS | en0, en1 | lo0 | bridge0, utun0 |
/**
* 獲取第一個非回環、非虛擬、已啟用的 IPv4 位址
*/
public static String getPrimaryIpv4Address() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) continue;
Enumeration<InetAddress> addrs = ni.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
if (addr instanceof java.net.Inet4Address) {
return addr.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
fe80::% 開頭為連結本地址,非對外可路由 IP,商業邏輯通常需過濾getDisplayName() 回傳友善名稱,getName() 為系統內部名,API 參數需用後者Java NetworkInterface API 提供完整網卡資訊存取能力。實務上建議封裝工具類,支援按名稱查詢、子介面遞迴、IP 版本過濾、介面狀態過濾。跨平台部署時務必實測目標環境網卡命名規則,並加入適當異常處理與日誌記錄。