1、问题提出
在使用 Java 开发网络程序时,有时候我们需要知道本机在局域网中的 IP 地址。很常见的一种做法是调用本地命令(比如 Windows 上的 ipconfig 命令和 Linux 上的 ifconfig 命令),接着解析本地命令的输出,最后得到本机在局域网内的 IP 地址。很明显,这种做法不够方便,也不够 Java。于是引出了 Java 在 JDK1.4 的时候添加的一个类: NetworkInterface 。
2、寻找方法
顾名思义,NetworkInterface 用于表示一个网络接口,这可以是一个物理的网络接口,也可以是一个虚拟的网络接口,而一个网络接口通常由一个 IP 地址来表示。既然 NetworkInterface 用来表示一个网络接口,那么如果可以获得当前机器所有的网络接口(包括物理的和虚拟的),然后筛选出表示局域网的那个网络接口,那就可以得到机器在局域网内的 IP 地址。
查看 NetworkInterface 类的所有方法,发现如下两个方法:
public static Enumeration<NetworkInterface> getNetworkInterfaces()
通过 API 文档可知,使用 getNetworkInterfaces 方法即可得到当前机器上所有的网络接口。
public Enumeration<InetAddress> getInetAddresses()
通过 API 文档可知,getInetAddresses 方法返回绑定到该网卡的所有的 IP 地址。(虽然一个网络接口确实可以绑定多个 IP 地址,然而通常情况下,一个网络接口都是只对应一个 IP 地址)
在 JDK1.6 时 NetworkInterface 还添加了一个新的方法:
public List<InterfaceAddress> getInterfaceAddresses()
getInterfaceAddresses 方法的作用与 getInetAddresses 方法类似,但是返回的是一个绑定到该网络接口的所有 InterfaceAddress 的集合。InterfaceAddress 也是 JDK1.6 之后添加的类,相比于 InetAddress 的区别在于它除了具有一个 IP 地址(InetAddress),还包括了该地址对应的广播地址和掩码长度。
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 命令得到的结果:
可以发现
网卡接口名称: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,然后又使用该机器作为热点 —— 不过很容易对二者进行区分,请有兴趣的读者自己实践)
4、总结
当然,使用 NetworkInterface 类来获取机器在局域网内的 IP 地址还是有一定的平台相关性,但是相比于使用本地命令的方式来说,会更加方便和更加的 Java 。
PS:在 Android 平台上同样可以使用 NetworkInterface 来实现获得机器在局域网内 IP 地址的功能。