19

我的机器上有三个接口(eth0,Loopback,wlan0),我想使用 Java-API 来获取 mac 地址。

  • 我使用此代码。

    Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
        for (NetworkInterface netint : Collections.list(nets))
            displayInterfaceInformation(netint);
    }
    
    static void displayInterfaceInformation(NetworkInterface netint) 
      throws SocketException 
    {
        System.out.println("Display name: " 
           + netint.getDisplayName());
        System.out.println("Hardware address: " 
           + Arrays.toString(netint.getHardwareAddress()));
    }
    
  • 但是那个代码打印出来了wlan0,loopback却错过了 eth0

  • 我的操作系统 Ubuntu,任何帮助。

更新

  • o/p ( strace -f java Networks 2>&1| grep ioctl)..空白(空)

  • java -version

java 版本“1.7.0_21” Java(TM) SE 运行时环境(内部版本 1.7.0_21-b11) Java HotSpot(TM) 64 位服务器 VM(内部版本 23.21-b01,混合模式)

  • strace ifconfig 2>&1 | grep ioctl

ioctl(4, SIOCGIFCONF, {80, {{"lo", {AF_INET, inet_addr("127.0.0.1")}}, {"wlan0", {AF_INET, inet_addr("192.168.1.101")}}}}) = 0
ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_MULTICAST}) = 0
ioctl(5, SIOCGIFHWADDR, {ifr_name="eth0", ifr_hwaddr=-----------------}) = 0
ioctl(5, SIOCGIFMETRIC, {ifr_name="eth0", ifr_metric=0}) = 0
ioctl(5, SIOCGIFMTU, {ifr_name="eth0", ifr_mtu=1500}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0
ioctl(5, SIOCGIFTXQLEN, {ifr_name="eth0", ifr_qlen=1000}) = 0
ioctl(4, SIOCGIFADDR, {ifr_name="eth0", ???}) = -1 EADDRNOTAVAIL (Cannot assign requested address)
ioctl(5, SIOCGIFFLAGS, {ifr_name="lo", ifr_flags=IFF_UP|IFF_LOOPBACK|IFF_RUNNING}) = 0
ioctl(5, SIOCGIFHWADDR, {ifr_name="lo", ifr_hwaddr=00:00:00:00:00:00}) = 0
ioctl(5, SIOCGIFMETRIC, {ifr_name="lo", ifr_metric=0}) = 0
ioctl(5, SIOCGIFMTU, {ifr_name="lo", ifr_mtu=16436}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="lo", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="lo", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0
ioctl(5, SIOCGIFTXQLEN, {ifr_name="lo", ifr_qlen=0}) = 0
ioctl(4, SIOCGIFADDR, {ifr_name="lo", ifr_addr={AF_INET, inet_addr("127.0.0.1")}}) = 0
ioctl(4, SIOCGIFDSTADDR, {ifr_name="lo", ifr_dstaddr={AF_INET, inet_addr("127.0.0.1")}}) = 0
ioctl(4, SIOCGIFBRDADDR, {ifr_name="lo", ifr_broadaddr={AF_INET, inet_addr("0.0.0.0")}}) = 0
ioctl(4, SIOCGIFNETMASK, {ifr_name="lo", ifr_netmask={AF_INET, inet_addr("255.0.0.0")}}) = 0
ioctl(5, SIOCGIFFLAGS, {ifr_name="wlan0", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(5, SIOCGIFHWADDR, {ifr_name="wlan0", ifr_hwaddr=---------------}) = 0
ioctl(5, SIOCGIFMETRIC, {ifr_name="wlan0", ifr_metric=0}) = 0
ioctl(5, SIOCGIFMTU, {ifr_name="wlan0", ifr_mtu=1500}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="wlan0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0
ioctl(5, SIOCGIFMAP, {ifr_name="wlan0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0
ioctl(5, SIOCGIFTXQLEN, {ifr_name="wlan0", ifr_qlen=1000}) = 0
ioctl(4, SIOCGIFADDR, {ifr_name="wlan0", ifr_addr={AF_INET, inet_addr("192.168.1.101")}}) = 0
ioctl(4, SIOCGIFDSTADDR, {ifr_name="wlan0", ifr_dstaddr={AF_INET, inet_addr("192.168.1.101")}}) = 0
ioctl(4, SIOCGIFBRDADDR, {ifr_name="wlan0", ifr_broadaddr={AF_INET, inet_addr("192.168.1.255")}}) = 0
ioctl(4, SIOCGIFNETMASK, {ifr_name="wlan0", ifr_netmask={AF_INET, inet_addr("255.255.255.0")}}) = 0

如果配置

$ ifconfig
eth0      Link encap:Ethernet  HWaddr -------------  
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:1695 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1695 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:129949 (129.9 KB)  TX bytes:129949 (129.9 KB)

wlan0     Link encap:Ethernet  HWaddr -------------------  
          inet addr:192.168.1.101  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::-------------- Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8396 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5524 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:3959941 (3.9 MB)  TX bytes:1513934 (1.5 MB)
4

3 回答 3

11

显然,我一开始就错了:尽管两者ifconfig和 Java API 都使用相同的ioctl()系统调用,但它们的行为不同。

首先,SIOCGIFCONF ioctl()记录如下(参见http://linux.die.net/man/7/netdevice):

SIOCGIFCONF
    返回接口(传输层)地址列表。
    ...
    内核用所有当前的 L3 接口填充 ifreqs正在运行的
    地址。

因此,SIOCGIFCONF ioctl()两者都使用ifconfig的 JAVA API 只返回正在运行的接口。这也可以在strace ifconfig ...问题的输出中看到 - 第一个ioctl只返回loand wlan0,而不是eth0。

那么,从哪里ifconfig得到eth0呢?检查ifconfig源代码(来自net-toolsDebian/Ubuntu 上的软件包),我们看到它ifconfig没有使用来自 的结果ioctl()作为网络设备枚举的基础,而是首先读取/proc文件系统以确定所有网络接口。然后,它使用ioctl()系统调用来确定有关每个接口的更多信息。

不幸的是,java.net.NetworkInterface.getByName()如果我们显式传递名称,该方法甚至不会为未配置的接口返回网络接口对象,例如eth0.

本质上,仍然有三种不同的方法可以获取Linux上所有设备的硬件地址:

  • 调用ifconfig并解析输出(应该是最后的手段)
  • 实现一个 JNI 库来做同样的事情ifconfig(需要一个依赖于架构的共享库)
  • /proc直接从文件系统和文件系统读取数据/sys

所有这些方法都依赖于系统并且不可移植。第三种方法的好处是可以用纯 Java 实现。以下是在我的环境中运行良好的第三种方法的示例实现:

static void printHardwareAddresses() throws SocketException {
    if (System.getProperty("os.name").equals("Linux")) {

        // Read all available device names
        List<String> devices = new ArrayList<>();
        Pattern pattern = Pattern.compile("^ *(.*):");
        try (FileReader reader = new FileReader("/proc/net/dev")) {
            BufferedReader in = new BufferedReader(reader);
            String line = null;
            while( (line = in.readLine()) != null) {
                Matcher m = pattern.matcher(line);
                if (m.find()) {
                    devices.add(m.group(1));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // read the hardware address for each device
        for (String device : devices) {
            try (FileReader reader = new FileReader("/sys/class/net/" + device + "/address")) {
                BufferedReader in = new BufferedReader(reader);
                String addr = in.readLine();

                System.out.println(String.format("%5s: %s", device, addr));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    } else {
        // use standard API for Windows & Others (need to test on each platform, though!!)
        ...
    }
}
于 2013-07-18T09:27:28.870 回答
1

调用getNetworkInterfacesJava时会返回

all the interfaces on this machine. Returns null if no network interfaces could be found on this machine.

你不是唯一一个有这个问题的人。显然,在 Linux 上,Java 将只返回分配了 IP 地址的接口(即配置的适配器)。

但是从您的应用程序的角度来看(除非您正在构建一个网络配置应用程序),拥有一个没有 IP 地址的接口就像根本没有它一样。每次访问时,您都必须轮询接口或获取它们,例如应用程序中的“网络首选项”。

于 2013-07-17T17:37:29.027 回答
0

扩展@Andreas 的答案,我们可以编写一个小的 shell 脚本$ifconfg | grep "Link encap" > some_file,然后将有一个较小的(只有 3 行)文件来解析并选择每行的第一个标记。获取 HWaddress 的类似事情。我们将编写较少的 java 代码。

其他选项可能是使用 Apache commonsIOUtils.toString(new FileInputStream( <file_path>,US_ASCII))来读取设置。这将消除他的解决方案中重复的 java I/O 代码。

于 2013-07-22T15:05:11.120 回答