这个讨论的起因是最近修改的一个bug,
我们的系统管理了一堆网络设备,为了方便用户,我们提供了一个视图叫做"Near me"。
在这个视图中会显示与本机在同一个网段内的所有网络设备。
以前这个功能不支持双网卡。而且子网掩码是写死的"255.255.255.0"
为此便进行了下面的调查。共有两种方法,后面会给出这两种方法的简单问题总结,以及对比。
取得子网掩码,方法一:JavaAPI
InterfaceAddress.getNetworkPrefixLength() , 这个方法会返回子网掩码前面的1的位数。
为了直观,下面的例子会将其转换为String并显示出来。
关于下面的例子,还有几点要说明:
1. 因为我们数据库中的ip地址存的全是字符串,所以例子中也已字符串的形式来操作。
对于很多方法,直接拿int来移位会更简单。
2. 为了让例子独立跑起来,没有将共同的部分抽取成更小函数,也没有使用apache.common这样的第三方包。
这就使得函数比较冗长。
不罗嗦了,代码大家将就着看,如下:
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
public class IPAddressTest {
public static void main(String[] args) {
printIpAddressAndSubnettest();
}
public static void printIpAddressAndSubnettest() {
try {
Enumeration<NetworkInterface> eni = NetworkInterface
.getNetworkInterfaces();
while (eni.hasMoreElements()) {
NetworkInterface networkCard = eni.nextElement();
List<InterfaceAddress> ncAddrList = networkCard
.getInterfaceAddresses();
Iterator<InterfaceAddress> ncAddrIterator = ncAddrList.iterator();
while (ncAddrIterator.hasNext()) {
InterfaceAddress networkCardAddress = ncAddrIterator.next();
InetAddress address = networkCardAddress.getAddress();
if (!address.isLoopbackAddress()) {
String hostAddress = address.getHostAddress();
System.out.println("address = " + hostAddress);
if (hostAddress.indexOf(":") > 0) {
// case : ipv6
continue;
} else {
// case : ipv4
String maskAddress = calcMaskByPrefixLength(networkCardAddress.getNetworkPrefixLength());
String subnetAddress = calcSubnetAddress(hostAddress, maskAddress);
String broadcastAddress = networkCardAddress.getBroadcast().getHostAddress();
System.out.println("subnetmask = "+ maskAddress);
System.out.println("subnet = "+ subnetAddress);
System.out.println("broadcast = "+ broadcastAddress+"\n");
}
} else {
String loopback = networkCardAddress.getAddress().getHostAddress();
System.out.println("loopback addr = " + loopback +"\n");
}
}
System.out.println("----- NetworkInterface Separator ----\n\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String calcMaskByPrefixLength(int length) {
int mask = -1 << (32 - length);
int partsNum = 4;
int bitsOfPart = 8;
int maskParts[] = new int[partsNum];
int selector = 0x000000ff;
for (int i = 0; i < maskParts.length; i++) {
int pos = maskParts.length - 1 - i;
maskParts[pos] = (mask >> (i * bitsOfPart)) & selector;
}
String result = "";
result = result + maskParts[0];
for (int i = 1; i < maskParts.length; i++) {
result = result + "." + maskParts[i];
}
return result;
}
public static String calcSubnetAddress(String ip, String mask) {
String result = "";
try {
// calc sub-net IP
InetAddress ipAddress = InetAddress.getByName(ip);
InetAddress maskAddress = InetAddress.getByName(mask);
byte[] ipRaw = ipAddress.getAddress();
byte[] maskRaw = maskAddress.getAddress();
int unsignedByteFilter = 0x000000ff;
int[] resultRaw = new int[ipRaw.length];
for (int i = 0; i < resultRaw.length; i++) {
resultRaw[i] = (ipRaw[i] & maskRaw[i] & unsignedByteFilter);
}
// make result string
result = result + resultRaw[0];
for (int i = 1; i < resultRaw.length; i++) {
result = result + "." + resultRaw[i];
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
return result;
}
}
取得子网掩码,方法二:解析系统命令
windows的命令ipconfig,linux的ifconfig,都会返回网络的配置信息。
我们可以利用System.exec(...)对其进行调用,并捕获其输出并进行分析,便可以得到ip地址与子网掩码的配对关系。
当然,如果能写个复杂一点的脚本,让他在java调用的时候,能够直接返回我们要的IP与子网掩码信息就更好了。
两种方法的总结与对比
1. 对比
上述两种方法对比起来,自然是API的方式更直接一些,平台也更通用一些。
2.问题点
但是这两种方法都存在着一个问题:
那就是,当双网卡中的某一块网卡的网线被拔掉的时候,便无法解析出该取得该网卡对应的IP以及子网掩码。
因为我们的系统的near me视图,管理的是数据库中的历史数据,而无需去网上进行即时搜索。
那么此时,上述的那个网线掉了的网卡,所对应的数据库中的历史数据,便无法在near me画面中显示出来。
3.总结
网卡这块是这样的(这是我以前不知道的一个认识):
一个OS可以有多块网卡,每块网卡可以有多个IP地址,每个IP地址可以有自己独有的一个子网掩码(彼此可以不同)
关于一个网卡上可以配多ip的方法,大家就各自放狗吧。
windows,linux上面都有"界面,命令,api方法"可以使用。
所以java api的方法也是:
先取得网卡的迭代器,然后再取得这个网卡对应的IP地址的迭代器,然后去的该地址对应的名字,掩码,广播地址等。
起初查找java api的时候,我是从InetAddress,Inet4Address,Inet6Address开始,
现在看来这些类是为了整个网络上的所有ip所设计的,
寻找本机ip的相关信息应该从NetworkInterface以及他对应的InterfaceAddress找起。