單網卡與多網卡環境下獲取MAC地址


一、場景引入

       前一段時間寫的一個服務需要獲取服務所在的服務器mac地址當做唯一身份標識去更新當前服務器的信息。因為生產環境的系統是centos,故按照linux的系統去獲取mac地址。java在JDK1.6以后 java.net.NetworkInterface提供了

整的方法用於獲取網絡設備信息,調用 NetworkInterface.getNetworkInterfaces()可以返回所有網絡設備,調用NetworkInterface.getHardwareAddress()就可以獲取指定網卡的MAC。但是有一種情況需要考慮,也就是要提前知道服

務所在的機器是單網卡還是多網卡,為什么這么說呢?因為我在寫完服務之后放到測試環境一切正常,部署到生產環境的時候出問題了,獲取mac地址異常,后來發現生產環境是雙網卡。

二、解決方法

     在此說一下單網卡如何獲取mac地址,windows系統和linux系統獲取的方法也不一樣,如果提前不知道系統的情況下還需要對系統進行區分(這種情況應該很少會有),故現在只針對linux系統獲取mac地址的方法,獲取后轉換為大寫,並用“-”連接

/**
* 獲取本機mac地址
* @return
* @throws Exception
*/
public static String getLinuxMacAdress() throws UnknownHostException, SocketException{
InetAddress ia = InetAddress.getLocalHost();
byte[] mac = NetworkInterface.getByInetAddress(ia).getHardwareAddress();
StringBuilder sb = new StringBuilder("");
for(int i=0; i<mac.length; i++) {
if(i!=0) {
sb.append("-");
}
//字節轉換為整數
int temp = mac[i]&0xff;
String str = Integer.toHexString(temp);
if(str.length()==1) {
sb.append("0"+str);
}else {
sb.append(str);
}
}
return sb.toString().toUpperCase();
}

       針對多網卡環境,可以有兩種方法去解決,以上單網卡mac地址的獲取操作在多網卡環境下會拋異常,所以第一種方式可以在拋異常的時候讀取配置文件中提前配置的mac地址,也就是說,在部署的時候就人工將mac地址寫到配置文件中,一旦mac讀取失敗就去讀取配置文件。這么做看起來沒有實際解決獲取mac的實際問題,但是可以解決一部分場景下的需求了

       第二種方法就是真正的去獲取多網卡環境下的mac地址,仍然寫成工具類的形式,直接上代碼:

    

public class NetworkUtil {
    public static enum Radix{
    /** 二進制 */BIN(2),
    /** 十進制 */DEC(10),
    /** 十六進制 */HEX(16);
   final int value;
   Radix(int radix){
   this.value = radix;
}
}
public static enum Filter implements Predicate<NetworkInterface>{
/** 過濾器: 所有網卡 */ALL,
/** 過濾器: 在線設備,see also {@link NetworkInterface#isUp()} */UP,
/** 過濾器: 虛擬接口,see also {@link NetworkInterface#isVirtual()} */VIRTUAL,
/** 過濾器:LOOPBACK, see also {@link NetworkInterface#isLoopback()} */LOOPBACK,
/** 過濾器:物理網卡 */PHYICAL_ONLY;

@Override
public boolean apply(NetworkInterface input) {
if(null == input ){
return false;
}
try{
byte[] hardwareAddress;
switch(this){
case UP:
return input.isUp();
case VIRTUAL:
return input.isVirtual();
case LOOPBACK:
return input.isLoopback();
case PHYICAL_ONLY :
hardwareAddress = input.getHardwareAddress();
return null != hardwareAddress
&& hardwareAddress.length > 0
&& !input.isVirtual()
&& !isVMMac(hardwareAddress);
case ALL:
default :
return true;
}
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
}
/**
* 根據過濾器{@code filters}指定的條件(AND)返回網卡設備對象
* @param filters
* @return
*/
@SafeVarargs
@SuppressWarnings("unchecked")
public static Set<NetworkInterface> getNICs(Predicate<NetworkInterface> ...filters) {
if(null == filters){
filters = new Predicate[]{Filter.ALL};
}
try {
Iterator<NetworkInterface> filtered = Iterators.filter(
Iterators.forEnumeration(NetworkInterface.getNetworkInterfaces()),
Predicates.and(filters));
return ImmutableSet.copyOf(filtered);
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
/**
* 返回所有物理網卡
* @return
*/
public static Set<NetworkInterface> getPhysicalNICs() {
return getNICs(Filter.PHYICAL_ONLY,Filter.UP);
}
/**
* 將{@code byte[]} 轉換為{@code radix}指定格式的字符串
*
* @param source
* @param separator 分隔符
* @param radix 進制基數
* @return {@code source}為{@code null}時返回空字符串
*/
public static final String format(byte[] source,String separator, final Radix radix) {
if (null == source){
return "";
}
if(null == separator){
separator = "";
}
List<String> hex = Lists.transform(Bytes.asList(source),new Function<Byte,String>(){
@Override
public String apply(Byte input) {
return String.copyValueOf(new char[]{
Character.forDigit((input & 240) >> 4, radix.value),
Character.forDigit(input & 15, radix.value)
});
}});
return Joiner.on(separator).join(hex);
}
/**
* MAC地址格式(16進制)格式化{@code source}指定的字節數組
* @see #format(byte[], String, int)
*/
public static final String formatMac(byte[] source,String separator) {
return format(source,separator,Radix.HEX);
}
/**
* 以IP地址格式(點分位)格式化{@code source}指定的字節數組<br>
* @see #format(byte[], String, int)
*/
public static final String formatIp(byte[] source) {
return format(source,".",Radix.DEC);
}
/**
* 返回指定{@code address}綁定的網卡的物理地址(MAC)
* @param address
* @return 指定的{@code address}沒有綁定在任何網卡上返回{@code null}
* @see {@link NetworkInterface#getByInetAddress(InetAddress)}
* @see {@link NetworkInterface#getHardwareAddress()}
*/
public static byte[] getMacAddress(InetAddress address) {
  try {
       NetworkInterface nic = NetworkInterface.getByInetAddress(address);
       return null == nic ? null : nic.getHardwareAddress();
  } catch (SocketException e) {
        throw new RuntimeException(e);
  }
}
/**
* @param nic 網卡對象
* @param separator 格式化分隔符
* @return 表示MAC地址的字符串
*/
public static String getMacAddress(NetworkInterface nic,String separator) {
try {
return format(nic.getHardwareAddress(),separator, Radix.HEX);
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
/**
* 參見 {@link #getMacAddress(InetAddress)}
* @param address
* @param separator 格式化分隔符
* @return 表示MAC地址的字符串
*/
public static String getMacAddress(InetAddress address,String separator) {
      return format(getMacAddress(address),separator, Radix.HEX);
}
private static byte invalidMacs[][] = {
    {0x00, 0x05, 0x69}, // VMWare
    {0x00, 0x1C, 0x14}, // VMWare
    {0x00, 0x0C, 0x29}, // VMWare
    {0x00, 0x50, 0x56}, // VMWare
    {0x08, 0x00, 0x27}, // Virtualbox
    {0x0A, 0x00, 0x27}, // Virtualbox
    {0x00, 0x03, (byte)0xFF}, // Virtual-PC
    {0x00, 0x15, 0x5D} // Hyper-V
};
private static boolean isVMMac(byte[] mac) {
    if(null == mac) {
    return false;
}

for (byte[] invalid: invalidMacs){
      if (invalid[0] == mac[0] && invalid[1] == mac[1] && invalid[2] == mac[2]) {
       return true;
      }
  }

   return false;
 }

}

    

 

 

       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM