程式扎記: [ Java代碼範本 ] 取得網卡IP address 的網路遮罩與廣播的位址

標籤

2011年4月12日 星期二

[ Java代碼範本 ] 取得網卡IP address 的網路遮罩與廣播的位址


轉載自 這理
前言 :
這裡將透過 JDK 提供的 API 取出某台 Machine 上所有網卡以及其具備的 IP Address 的網路遮罩與對應廣播的位址. 事實上你也可以透過OS 本身的命令完成同樣的功能. 共有兩種方法,後面會給出這兩種方法的簡單問題總結,以及對比.

取得子網掩碼,方法一 : (JavaAPI)
InterfaceAddress.getNetworkPrefixLength() ,這個方法會返回子網遮罩前面的位數. 為了直觀,下面的例子會將其轉換為String並顯示出來。
關於下面的例子,還有幾點要說明 :
1. 因為我們數據庫中的ip地址存的全是字符串,所以例子中也以字符串的形式來操作. 對於很多方法,直接拿int來移位會更簡單。
2. 為了讓例子獨立跑起來,沒有將共同的部分抽取成更小函數,也沒有使用apache.common這樣的第三方包. 這就使得函數比較冗長。

底下是範例代碼 :
- IPAddressTest.java :
  1. package john.test;  
  2.   
  3. import java.net.InetAddress;  
  4. import java.net.InterfaceAddress;  
  5. import java.net.NetworkInterface;  
  6. import java.net.UnknownHostException;  
  7. import java.util.Enumeration;  
  8. import java.util.Iterator;  
  9. import java.util.List;  
  10.   
  11. public class IPAddressTest {  
  12.     public static void main(String[] args) {  
  13.         printIpAddressAndSubnettest();  
  14.     }  
  15.   
  16.     public static void printIpAddressAndSubnettest() {  
  17.         try {             
  18.             Enumeration eni = NetworkInterface  
  19.                     .getNetworkInterfaces();  
  20.             while (eni.hasMoreElements()) {  
  21.   
  22.                 NetworkInterface networkCard = eni.nextElement();  
  23.                 List ncAddrList = networkCard  
  24.                         .getInterfaceAddresses();  
  25.                 Iterator ncAddrIterator = ncAddrList  
  26.                         .iterator();  
  27.                 while (ncAddrIterator.hasNext()) {  
  28.                     InterfaceAddress networkCardAddress = ncAddrIterator.next();  
  29.                     InetAddress address = networkCardAddress.getAddress();  
  30.                     if (!address.isLoopbackAddress()) {  
  31.                         String hostAddress = address.getHostAddress();                        
  32.                           
  33.                         if (hostAddress.indexOf(":") > 0) {  
  34.                             // case : ipv6  
  35.                             continue;  
  36.                         } else {                              
  37.                             // case : ipv4  
  38.                             System.out.println("address = " + hostAddress);  
  39.                               
  40.                             String maskAddress = calcMaskByPrefixLength(networkCardAddress  
  41.                                     .getNetworkPrefixLength());  
  42.                             String subnetAddress = calcSubnetAddress(  
  43.                                     hostAddress, maskAddress);  
  44.                             String broadcastAddress = networkCardAddress.getBroadcast().toString();  
  45.   
  46.                             System.out.println("subnetmask = " + maskAddress);  
  47.                             System.out.println("subnet = " + subnetAddress);  
  48.                             System.out.println("broadcast = "  
  49.                                     + broadcastAddress + "\n");  
  50.                         }  
  51.                     } else {  
  52.                         String loopback = networkCardAddress.getAddress()  
  53.                                 .getHostAddress();  
  54.                         System.out  
  55.                                 .println("loopback addr = " + loopback + "\n");  
  56.                     }  
  57.                 }  
  58.                 System.out.println("----- NetworkInterface Separator ----\n\n");  
  59.   
  60.             }  
  61.         } catch (Exception e) {  
  62.             e.printStackTrace();  
  63.         }  
  64.     }  
  65.   
  66.     public static String calcMaskByPrefixLength(int length) {  
  67.         System.out.println("Test>" + length);  
  68.         int mask = -1 << (32 - length);  
  69.         int partsNum = 4;  
  70.         int bitsOfPart = 8;  
  71.         int maskParts[] = new int[partsNum];  
  72.         int selector = 0x000000ff;  
  73.   
  74.         for (int i = 0; i < maskParts.length; i++) {  
  75.             int pos = maskParts.length - 1 - i;  
  76.             maskParts[pos] = (mask >> (i * bitsOfPart)) & selector;  
  77.         }  
  78.   
  79.         String result = "";  
  80.         result = result + maskParts[0];  
  81.         for (int i = 1; i < maskParts.length; i++) {  
  82.             result = result + "." + maskParts[i];  
  83.         }  
  84.         return result;  
  85.     }  
  86.   
  87.     public static String calcSubnetAddress(String ip, String mask) {  
  88.         String result = "";  
  89.         try {  
  90.             // calc sub-net IP  
  91.             InetAddress ipAddress = InetAddress.getByName(ip);  
  92.             InetAddress maskAddress = InetAddress.getByName(mask);  
  93.   
  94.             byte[] ipRaw = ipAddress.getAddress();  
  95.             byte[] maskRaw = maskAddress.getAddress();  
  96.   
  97.             int unsignedByteFilter = 0x000000ff;  
  98.             int[] resultRaw = new int[ipRaw.length];  
  99.             for (int i = 0; i < resultRaw.length; i++) {  
  100.                 resultRaw[i] = (ipRaw[i] & maskRaw[i] & unsignedByteFilter);  
  101.             }  
  102.   
  103.             // make result string  
  104.             result = result + resultRaw[0];  
  105.             for (int i = 1; i < resultRaw.length; i++) {  
  106.                 result = result + "." + resultRaw[i];  
  107.             }  
  108.         } catch (UnknownHostException e) {  
  109.             e.printStackTrace();  
  110.         }  
  111.   
  112.         return result;  
  113.     }  
  114. }  

取得子網掩碼,方法二 : (解析系統命令)
windows的命令ipconfig,linux的ifconfig,都會返回網絡的配置信息. 我們可以利用System.exec(...)對其調用,並捕獲輸出進行分析,便可以得到ip地址與子網遮罩的配對關係.

兩種方法的總結與對比 :
- 對比
上述兩種方法對比起來,自然是API的方式更直接一些,平台也更通用一些

- 問題點
但是這兩種方法都存在著一個問題 :
那就是,當雙網卡中的某一塊網卡的網線被拔掉的時候,便無法解析出該取得該網卡對應的IP以及子網掩碼. 因為我們的系統的near me視圖,管理的是數據庫中的歷史數據,而無需去網上進行即時搜索. 那麼此時,上述的那個網線掉了的網卡,所對應的數據庫中的歷史數據,便無法在near me畫面中顯示出來

- .總結
網卡這塊是這樣的(這是我以前不知道的一個認識) :
一個OS可以有多塊網卡,每塊網卡可以有多個IP地址,每個IP地址可以有自己獨有的一個子網掩碼(彼此可以不同)
所以java api的方法也是 :
先取得網卡的迭代器,然後再取得這個網卡對應的IP地址的迭代器,然後去的該地址對應的名字,掩碼,廣播地址等...
起初查找java api的時候,我是從InetAddress,Inet4Address,Inet6Address開始, 現在看來這些類是為了整個網絡上的所有ip所設計的, 尋找本機ip的相關信息應該從NetworkInterface以及他對應的InterfaceAddress找起.

補充說明 :
How to get subnet mask using java ?
The network prefix length is 128 (not 24, which it is on my network), and the broadcast address returned is 255.255.255.255 (not 192.168.1.255, which it is on my network).
You need to prevent Java from using IPv6, so that it isn't getting to IPv4 via IPv6. Adding -Djava.net.preferIPv4Stack=true to the command line fixes the results from InterfaceAddress.getNetworkPrefixLength() and InterfaceAddress.getBroadcast() for me.
This message was edited 4 times. Last update was at 13/04/2011 13:54:08

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!