NetworkInterface.getNetworkInterfaces()未列出所有接口

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

  • 我用这个代码。

     Enumeration 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 ).. blank(空)

  • 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

 $ 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) 

显然,我首先错了:即使ifconfig和Java API都使用相同的ioctl()系统调用,它们的行为也不同。

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

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

因此, ifconfig和JAVA API使用的SIOCGIFCONF ioctl()仅返回正在运行的接口。 这也可以在strace ifconfig ...看到strace ifconfig ...问题的输出 – 第一个ioctl只返回lowlan0 ,但返回eth0。

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

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

从本质上讲,仍有三种不同的方法可以获取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 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!!) ... } } 

当调用getNetworkInterfaces Java将返回

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

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

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

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

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