`

Java:取得子网掩码

阅读更多

这个讨论的起因是最近修改的一个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找起。

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics