最近完成一个实验,需要程序自动得到本机的IP地址。
Java.net中提供了InetAddress.getLocalHost()方法,来得到IP地址,但是由于主机网卡的复杂性,往往存在一部主机对应多个网卡的情况,此时getLocalHost()方法得到的IP地并不能够保证一定是正在使用的网卡的IP地址。
在查询相关资料后,发现相当部分的人采用了枚举每个网卡的每个IP地址,在对这些IP地址检查其是否为回环类型地址等方法进行判断,包括了以下三种方法:
isLoopbackAddress()
isLinkLocalAddress()
isSiteLocalAddress()
其中前两个返回false,最后一个返回true为正确网卡的期望值。
但是经过我的实际测试,发现这种情况并不能分辨出VMWare的虚拟网卡,还是无法得到正确的IP地址。如下图,真实的IP与两个虚拟IP的函数返回值是一致的。
为了得到正确的IP地址,不得不采用了一种比较笨的方法,就是通过某个IP地址直接向外网发送请求,如果得到回应的话,说明这个IP地址是可用的。
我们采用了调取CMD的命令的方法(不得已),构造了一条命令:
String pingCommand = "ping " + targetIp + " -n " + pingTimes + " -w " + timeOut + " -S " + sourceIp;
通过这种方式返回的才是正确的IP地址。
附完整代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Ip {
public static void main(String[] args) {
System.out.println(getExactlocalAddress().getHostAddress());
}
public static InetAddress getExactlocalAddress() {
try {
//网卡枚举
Enumeration<NetworkInterface> networkInterfaceEnumeration = NetworkInterface.getNetworkInterfaces();
while (networkInterfaceEnumeration.hasMoreElements()) {
//取出每一个网卡
NetworkInterface networkInterface = networkInterfaceEnumeration.nextElement();
//针对每个网卡进行IP地址枚举
Enumeration<InetAddress> inetAddressEnumeration = networkInterface.getInetAddresses();
while ((inetAddressEnumeration.hasMoreElements())) {
//取出每一个IP地址
InetAddress inetAddress = inetAddressEnumeration.nextElement();
//只关心IPV4地址
if (inetAddress instanceof Inet4Address) {
//排除loopback回环类型地址,确保 inetaddress 是否是站点本地地址
if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) {
//尝试使用该网卡ping外网,调研CMD命令,此部分仅使用于Windows系统
if (PingTest.ping("www.baidu.com", inetAddress.getHostAddress()) == true) {
//System.out.println(networkInterface.getName());
//System.out.println(inetAddress.getHostAddress());
return inetAddress;
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
class PingTest {
/*
* 指定网卡,测试网卡能否连接到目标IP
* targetIpAddress:目标IP
* localIpAddress:本机IP
*/
public static boolean ping(String targetIpAddress, String localIpAddress) {
int pingTimes = 2, timeOut = 3000;
BufferedReader in = null;
Runtime r = Runtime.getRuntime();// 将要执行的ping命令,此命令是windows格式的命令
String pingCommand = "ping " + targetIpAddress + " -n " + pingTimes + " -w " + timeOut + " -S " + localIpAddress;
try {
// 执行命令并获取输出
//System.out.println(pingCommand);
Process p = r.exec(pingCommand);
if (p == null) {
return false;
}
in = new BufferedReader(new InputStreamReader(p.getInputStream())); // 逐行检查输出,计算类似出现=23ms TTL=62字样的次数
int connectedCount = 0;
String line = null;
while ((line = in.readLine()) != null) {
connectedCount += getCheckResult(line);
}
// 如果出现类似=23ms TTL=62这样的字样,出现的次数=测试次数则返回真
return connectedCount == pingTimes;
} catch (Exception ex) {
ex.printStackTrace(); // 出现异常则返回假
return false;
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static int getCheckResult(String line) { // System.out.println("控制台输出的结果为:"+line);
Pattern pattern = Pattern.compile("(\\d+ms)(\\s+)(TTL=\\d+)", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
return 1;
}
return 0;
}
}