原文地址:https://segmentfault.com/a/1190000007462741
1、问题提出
在使用 Java 开发网络程序时,有时候我们需要知道本机在局域网中的 IP 地址。很常见的一种做法是调用本地命令(比如 Windows 上的 ipconfig 命令和 Linux 上的 ifconfig 命令),接着解析本地命令的输出,最后得到本机在局域网内的 IP 地址。很明显,这种做法不够方便,也不够 Java。于是引出了 Java 在 JDK1.4 的时候添加的一个类: NetworkInterface 。
2、寻找方法
顾名思义,NetworkInterface 用于表示一个网络接口,这可以是一个物理的网络接口,也可以是一个虚拟的网络接口,而一个网络接口通常由一个 IP 地址来表示。既然 NetworkInterface 用来表示一个网络接口,那么如果可以获得当前机器所有的网络接口(包括物理的和虚拟的),然后筛选出表示局域网的那个网络接口,那就可以得到机器在局域网内的 IP 地址。
查看 NetworkInterface 类的所有方法,发现如下两个方法:
public static Enumeration getNetworkInterfaces()
通过 API 文档可知,使用 getNetworkInterfaces 方法即可得到当前机器上所有的网络接口。
public Enumeration getInetAddresses()
通过 API 文档可知,getInetAddresses 方法返回绑定到该网卡的所有的 IP 地址。(虽然一个网络接口确实可以绑定多个 IP 地址,然而通常情况下,一个网络接口都是只对应一个 IP 地址)
3、实践
OK,现在让电脑连接上 WiFi,来实践下 NetworkInterface 的功能。
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
public class NetworkInterfaceTest {
public static void main(String[] args) throws Exception {
// 获得本机的所有网络接口
Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
while (nifs.hasMoreElements()) {
NetworkInterface nif = nifs.nextElement();
// 获得与该网络接口绑定的 IP 地址,一般只有一个
Enumeration<InetAddress> addresses = nif.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr instanceof Inet4Address) { // 只关心 IPv4 地址
System.out.println("网卡接口名称:" + nif.getName());
System.out.println("网卡接口地址:" + addr.getHostAddress());
System.out.println();
}
}
}
}
}
在我机器(Windows10)上运行结果:
运行结果
对比使用 ipconfig 命令得到的结果:
ipconfig的结果
可以发现
网卡接口名称:wlan1
网卡接口地址:192.168.3.5
即为机器在局域网内的 IP 地址 —— Windows 平台上局域网的网络接口以“wlan”开头。(lo 为本地回环地址,eth0 和 eth4 为 VMware 创建的虚拟地址)
于是我们可以写出一个简单的在 Windows 机器上获得局域网 IP 地址的方法:
public InetAddress getLANAddressOnWindows() {
try {
Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
while (nifs.hasMoreElements()) {
NetworkInterface nif = nifs.nextElement();
if (nif.getName().startsWith("wlan")) {
Enumeration<InetAddress> addresses = nif.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr.getAddress().length == 4) { // 速度快于 instanceof
return addr;
}
}
}
}
} catch (SocketException ex) {
ex.printStackTrace(System.err);
}
return null;
}
(该方法没有考虑 Windows 平台上有多个网络接口的名称以 “wlan” 开头的情况,比如你的机器连接了 WiFi,然后又使用该机器作为热点 —— 不过很容易对二者进行区分,请有兴趣的读者自己实践)
在 JDK1.6 时 NetworkInterface 还添加了一个新的方法:
public List getInterfaceAddresses()
getInterfaceAddresses 方法的作用与 getInetAddresses 方法类似,但是返回的是一个绑定到该网络接口的所有 InterfaceAddress 的集合。InterfaceAddress 也是 JDK1.6 之后添加的类,相比于 InetAddress 的区别在于它除了具有一个 IP 地址(InetAddress),还包括了该地址对应的广播地址和掩码长度。
4、总结
当然,使用 NetworkInterface 类来获取机器在局域网内的 IP 地址还是有一定的平台相关性,但是相比于使用本地命令的方式来说,会更加方便和更加的 Java 。
PS:在 Android 平台上同样可以使用 NetworkInterface 来实现获得机器在局域网内 IP 地址的功能。