1. 基本知识
NetworkInterface 类是 Java 中用于表示网络接口的类,它提供了一系列方法来获取有关网络接口的信息
核心概念如下:
- 本地计算机的网络接口,可以是物理接口(如网卡)或虚拟接口(如回环接口)
- 每个网络接口都有一个唯一的名称和一个或多个 IP 地址,还可能有一个硬件地址(MAC 地址)
- 多个网络接口,每个网络接口都有一个唯一的索引,网络接口的名称是一个描述性的字符串
涉及的优缺点如下(针对自身功能,是否满足该类从而往下阅读):
- 优点:
- 跨平台性:无论应用程序运行在哪种操作系统上,都可以使用 NetworkInterface 类来获取网络接口信息
- 封装性:封装了底层操作系统提供的获取网络接口信息的细节,无需关心不同操作系统下的具体实现细节,只需要使用统一的 Java API 接口即可
- 简单易用:简单易用的方法来获取网络接口的名称、IP 地址和 MAC 地址等信息,可以轻松地集成网络接口信息获取功能到自己的应用程序中
- 缺点:
-
限制性:只能获取到一些基本的网络接口信息,如名称、IP 地址和 MAC 地址等,不能获取更多详细的网络配置信息
-
不足的操作系统支持:不同版本的操作系统可能支持的功能不同。例如,在某些旧版本的操作系统上可能无法获取到完整的网络接口信息
2. 源码解读
解读几个比较常用的API接口源码
一、 getNetworkInterfaces
函数
获取本地计算机的所有网络接口,并返回一个 Enumeration<NetworkInterface>
对象,包含了所有网络接口的实例
2. 调用 getAll()
方法获取本地计算机的所有网络接口,并将结果存储在 netifs 数组中
3. Enumeration 对象的 nextElement()
方法中,逐个返回 netifs 数组中的网络接口实例,hasMoreElements()
方法中,检查是否还有更多的网络接口未返回
4. 遍历所有网络接口:nextElement()
方法中,通过 i 记录当前遍历到的网络接口的索引等
public static Enumeration<NetworkInterface> getNetworkInterfaces()
throws SocketException {
final NetworkInterface[] netifs = getAll();
// specified to return null if no network interfaces
if (netifs == null)
return null;
return new Enumeration<NetworkInterface>() {
private int i = 0;
public NetworkInterface nextElement() {
if (netifs != null && i < netifs.length) {
NetworkInterface netif = netifs[i++];
return netif;
} else {
throw new NoSuchElementException();
}
}
public boolean hasMoreElements() {
return (netifs != null && i < netifs.length);
}
};
}
二、getInetAddresses
函数
获取网络接口的所有 IP 地址,并返回一个 Enumeration<InetAddress>
对象,包含了网络接口的所有 IP 地址的实例
- 权限检查:在构造方法中,
System.getSecurityManager()
获取安全管理器,检查是否有NetPermission("getNetworkInformation")
权限。如果存在安全管理器且没有相应的权限,则标记为不受信任。然后遍历所有的 IP 地址,如果安全管理器存在且不受信任,还需要检查是否有连接到这些 IP 地址的权限。只有通过了权限检查的 IP 地址才会被保存在 local_addrs 数组中 - 实现
nextElement()
方法:在 checkedAddresses 内部类中实现了nextElement()
方法,用于返回下一个 IP 地址 - 实现
hasMoreElements()
方法:在 checkedAddresses 内部类中实现了hasMoreElements()
方法,用于判断是否还有更多的 IP 地址未返回
public Enumeration<InetAddress> getInetAddresses() {
class checkedAddresses implements Enumeration<InetAddress> {
/**
* 初始化了一些变量,包括 i(当前遍历到的索引)
* count(实际有效的 IP 地址数量)
* local_addrs(存储 IP 地址的数组)
*/
private int i=0, count=0;
private InetAddress local_addrs[];
checkedAddresses() {
local_addrs = new InetAddress[addrs.length];
boolean trusted = true;
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
try {
sec.checkPermission(new NetPermission("getNetworkInformation"));
} catch (SecurityException e) {
trusted = false;
}
}
for (int j=0; j<addrs.length; j++) {
try {
if (sec != null && !trusted) {
sec.checkConnect(addrs[j].getHostAddress(), -1);
}
local_addrs[count++] = addrs[j];
} catch (SecurityException e) { }
}
}
public InetAddress nextElement() {
if (i < count) {
return local_addrs[i++];
} else {
throw new NoSuchElementException();
}
}
public boolean hasMoreElements() {
return (i < count);
}
}
return new checkedAddresses();
}
三、getHardwareAddress
函数
获取网络接口的硬件地址(MAC 地址)
- 权限检查:
System.getSecurityManager()
获取安全管理器 sec。如果安全管理器不为空,则尝试检查是否有NetPermission("getNetworkInformation")
权限。如果没有该权限,会捕获 SecurityException 异常。如果没有该权限,并且当前网络接口没有更多的 IP 地址,说明无法获取到任何本地地址的连接权限,则直接返回 null - 遍历 IP 地址:遍历网络接口的所有 IP 地址
- 调用底层方法:如果没有找到 IPv4 地址,则调用
getMacAddr0()
方法,并将参数传递为 null,意味着将尝试获取任意地址的 MAC 地址
public byte[] getHardwareAddress() throws SocketException {
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
try {
sec.checkPermission(new NetPermission("getNetworkInformation"));
} catch (SecurityException e) {
if (!getInetAddresses().hasMoreElements()) {
// don't have connect permission to any local address
return null;
}
}
}
for (InetAddress addr : addrs) {
if (addr instanceof Inet4Address) {
return getMacAddr0(((Inet4Address)addr).getAddress(), name, index);
}
}
return getMacAddr0(null, name, index);
}
四、getByInetAddress
函数
类中的 getByInetAddress(InetAddress addr)
方法的实现部分,用于根据给定的 InetAddress 地址获取对应的 NetworkInterface 实例
地址类型检查: instanceof
关键字判断传入的地址是 IPv4 地址还是 IPv6 地址。
public static NetworkInterface getByInetAddress(InetAddress addr) throws SocketException {
if (addr == null) {
throw new NullPointerException();
}
if (addr instanceof Inet4Address) {
Inet4Address inet4Address = (Inet4Address) addr;
if (inet4Address.holder.family != InetAddress.IPv4) {
throw new IllegalArgumentException("invalid family type: "
+ inet4Address.holder.family);
}
} else if (addr instanceof Inet6Address) {
Inet6Address inet6Address = (Inet6Address) addr;
if (inet6Address.holder.family != InetAddress.IPv6) {
throw new IllegalArgumentException("invalid family type: "
+ inet6Address.holder.family);
}
} else {
throw new IllegalArgumentException("invalid address type: " + addr);
}
return getByInetAddress0(addr);
}
3. 常用API
一、getNetworkInterfaces()
:
- NetworkInterface 类的静态方法,获取本地计算机的所有网络接口
- 返回一个
Enumeration<NetworkInterface>
对象,包含本地计算机的所有网络接口的实例
示例代码:
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
// 处理每个网络接口
}
二、getDisplayName()
:
- 获取网络接口的名称
- 返回值为 String 类型,表示网络接口的名称
示例代码:
String interfaceName = networkInterface.getDisplayName();
System.out.println("Network Interface Name: " + interfaceName);
三、getInetAddresses()
:
- 获取网络接口的所有 IP 地址
- 返回一个
Enumeration<InetAddress>
对象,其中包含了网络接口的所有 IP 地址的实例
示例代码:
Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
// 处理每个 IP 地址
}
四、getHardwareAddress()
:
- 获取网络接口的硬件地址(MAC 地址)
- 返回一个 byte[] 数组,表示 MAC 地址的字节表示形式
示例代码:
byte[] macAddress = networkInterface.getHardwareAddress();
if (macAddress != null) {
StringBuilder sb = new StringBuilder();
for (byte b : macAddress) {
sb.append(String.format("%02X-", b));
}
System.out.println("MAC Address: " + sb.toString());
}
五、getIndex()
:
- 获取网络接口的索引
- 返回一个 int 值,表示网络接口的索引
示例代码:
int index = networkInterface.getIndex();
System.out.println("Interface Index: " + index);
六、isUp()
:
- 判断网络接口是否已经启用(处于 up 状态)
- 返回一个 boolean 值,表示网络接口是否已经启用
示例代码:
boolean isUp = networkInterface.isUp();
System.out.println("Interface is Up: " + isUp);