Java: 获取MAC网络地址

0. 背景

最近由于课程设计,需要使用MAC物理地址作为软件注册码的唯一标识,故成此博客!

从各个网络适配器的作用 → \rightarrow → 确定哪一个MAC地址作为唯一标识。在此记录,与诸君分享。

1. 首先打开cmd → \rightarrow →ipconfig/all ,查看所有MAC地址:

Java: 获取MAC网络地址_java
Java: 获取MAC网络地址_MAC地址_02
Java: 获取MAC网络地址_network_03
Java: 获取MAC网络地址_mac地址_04

2. 博主的ipconfig存在8个网络适配器,故而存在 8 个MAC物理地址。

其中每个网络适配器的含义如下:

Mac网卡地址解析:

  1. Sangfor SSL VPN CS Support System VNIC

    EasyConnect,远程连接VPN应用产生的虚拟网卡。

    具备Mac网卡地址

  2. Realtek PCIe GBE Family Controller

    realtek公司生产的PCIe接口的千兆以太网家用控制器,即 有线网卡

    具备Mac网卡地址

  3. VirtualBox Host-Only Ethernet Adapter

    用于对 虚拟机 进行 通信 的适配器。

    具备Mac网卡地址

  4. Npcap Loopback Adapter

    虚拟网卡,不涉及硬件,它被用于在网络连接不可用时,作为测试虚拟网络环境的工具。

    此外,如果 网络适配器(network adapter) / 网络适配器驱动程序(network adapter driver) 发生冲突,则必须使用 Loopback适配器

    Java中的NetworkInterface中定义:

    loopback interface that only supports communication between entities on this machine.

    本地环回测试的MAC地址,全球所有的计算机相同!!!

    具备Mac网卡地址

  5. Microsoft Wi-Fi Direct Virtual Adapter

    虚拟网卡,使用PC的便携式 热点(hotspot) 共享PC的互联网连接。

    当PC连接无线网,并开启移动热点后,PC会向外发射信号给其他终端,其他终端检测到PC发射出的网络信号,并且可以连接PC发出的无线网络!

    多用于移动设备投屏PC。

    具备Mac网卡地址

  6. Microsoft Wi-Fi Direct Virtual Adapter #2

    Virual Wi-Fi是一种虚拟化网络适配器的技术,一旦被虚拟化,那么,即可将 1 个物理无线适配器转换为 2 个虚拟适配器。

    具备Mac网卡地址

  7. Intel® Dual Band Wireless-AC 3168

    无线网卡

    具备Mac网卡地址

  8. Bluetooth Device (Personal Area Network)

    一种允许创建一个以太网(Ethernet network) 的技术,其允许计算机与移动设备等进行连接。

    局域网(PAN)网卡

    具备Mac网卡地址

3. 了解了各网络适配器的MAC物理地址后,我们可以更加 自信 的编程了!

因为MAC地址数量很多,而虚拟网卡的MAC地址又可以后期修改,只有 无线网卡(Intel® Dual Band Wireless-AC 3168)有限网卡(Realtek PCIe GBE Family Controller) 的MAC物理地址始终无法被改变并且绝大多数电脑都具备这两种网卡;

故,此处选择 无线网卡(Intel® Dual Band Wireless-AC 3168) 的MAC地址作为软件注册码的唯一标识。

ps:这里说明下,为什么不选择 Npcap Loopback Adapter 的MAC地址作为唯一标识的原因:

因为,全球所有的计算机的 Loopback AdapterMAC地址 都为 02-00-4C-4F-4F-50

而且,因为 MAC地址 位于计算机网络OSI参考模型的 数据链路层 中,其 作用在网络中唯一标示一个网卡,并由此标识唯一计算机,这样,该计算机才能准确的与其他计算机通信

然而, Loopback作用 是用于 本地环回测试,不需要与其他计算机通信,因此其物理地址全球性一致,不具有唯一标识性。

ps: 感兴趣的小朋友可以cmd → \rightarrow → ipconfig/all 看下,是不是你的 Loopback 的MAC地址 是否与博主一致!????
Java: 获取MAC网络地址_MAC地址_05

获取MAC地址程序如下:


import sun.encryption.constants.Constants;
import sun.encryption.domain.Network;

import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.List;

import java.util.Enumeration;

public class NetworkUtil {

    /**
     * 一台机器可能存在多个网卡, 故返回数组
     * 网络接口地址(NetworkInterface) ----> 接口地址(InterfaceAddress) ----> IP地址(InetAddress)
     *  ----> 网络接口地址(NetworkInterface)
     * @return
     * @throws Exception
     */
    public static List<Network> getMacAddresses() throws Exception {

        // 获取机器上的所有网络接口, 返回结果至少包含一项(即, loopback本地环回测试)
        // getNetworkInterfaces() + getInetAddresses()可以获取到所有IP地址
        Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();

        StringBuilder sb = new StringBuilder();

        ArrayList<String> networkNames = new ArrayList<String>();

        // 保存mac地址列表
        ArrayList<Network> macList = new ArrayList<Network>();

        // 使用标签, 跳出多重循环
        here:
        while (en.hasMoreElements()) {

            NetworkInterface iface = en.nextElement();

            // 获取对应网络接口的所有接口地址(InterfaceAddress)
            List<InterfaceAddress> addrs = iface.getInterfaceAddresses();

            for (InterfaceAddress addr : addrs) {

                // InetAddress: Internet Protocol (IP) address: IP地址
                // 返回网络接口地址对应的IP地址
                InetAddress ip = addr.getAddress();

                // 由IP地址获取网络接口(NetworkInterface)
                // 方便方法搜索到绑定到其的具体IP地址的网络接口(NetworkInterface)
                NetworkInterface networkInterface = NetworkInterface.getByInetAddress(ip);

                // 若为空, 跳过
                if (networkInterface == null) {

                    continue;

                }

                // 获取以太网等名称, 如:eth0, eth1, wlan1
                String name = networkInterface.getName();

                // 获取描述
                String displayName = networkInterface.getDisplayName();

                // 当网络接口有权限连接, 并且其具有MAC地址时, 返回二进制MAC硬件地址
                byte[] mac = networkInterface.getHardwareAddress();

                // 是否为虚拟网络接口
                boolean virtual = networkInterface.isVirtual();

                // 网络接口是否启动
                boolean up = networkInterface.isUp();

                if (mac == null) {

                    continue;

                }

                // 清空StringBuffer中的内容
                sb.delete(0, sb.length());

                // 转换MAC地址格式
                for (int i = 0; i < mac.length; i++) {

                    sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));

                }

                Network network = new Network();

                // 若网络接口首次出现, 避免返回结果重复
                if (!networkNames.contains(name)) {

                    networkNames.add(name);

                    network.setName(name);
                    network.setDisplayName(displayName);
                    network.setMac(sb.toString());
                    network.setVirtual(virtual);
                    network.setUp(up);

                    macList.add(network);

                }

                // 若找到, 跳出
                if (networkNames.contains(Constants.WIRELESS)) {

                    break here;

                }

            }

        }

        return macList;

    }


    // 根据字符串"wireless"找到对应mac地址
    // 方法有效的条件是: PC机具有无线网卡
    // linux再议
    public static String getWirelessMacAddress() throws Exception {

        List<Network> macs = getMacAddresses();

        for (Network mac: macs) {

            if (mac.getDisplayName().toLowerCase().contains(Constants.WIRELESS)) {

                return mac.getMac();

            }

        }

        return null;

    }




    public static void main(String[] args) throws Exception {

        String wirelessMac = getWirelessMacAddress();
        System.out.println(wirelessMac);

    }

}