我该如何获取仅IPv4地址?

5
我有以下代码,它应该只获取所有活动接口的IPv4地址,但在某些计算机上仍会返回IPv6地址。
public static List<List> getIpAddress() {
    List<String> ip = new ArrayList<>();
    List<List> ipRefined = new ArrayList<>();
    try {
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface iface = interfaces.nextElement();
            if (iface.isLoopback() || !iface.isUp())
                continue;
            Enumeration<InetAddress> addresses = iface.getInetAddresses();
            while(addresses.hasMoreElements()) {
                ip.add(addresses.nextElement().getHostAddress());
            }
        }
    } catch (SocketException e) {
        throw new RuntimeException(e);
    }
    for(int x = 0; x < ip.size(); x++){
        if(ip.get(x).contains("%")){
            try {
                if (ip.get(x + 1).contains(".")) {
                    List<String> tempList = new ArrayList<>();
                    tempList.add(ip.get(x).substring(ip.get(x).indexOf("%") + 1));
                    tempList.add(ip.get(x + 1));
                    ipRefined.add(tempList);
                }
            } catch (IndexOutOfBoundsException ae) {
            }
        }
    }
    return ipRefined;
}

我尝试使用System.setProperty("java.net.preferIPv4Stack" , "true");指定仅使用IPv4,但这只会导致getIpAddress()返回一个空列表。在不使用字符串操作的情况下,应该如何获取活动接口的IPv4地址? 编辑: 始终使用System.setProperty("java.net.preferIPv4Stack" , "true");将导致getIpAddress()返回一个空列表。

5
我不确定这一点,但是在阅读“addresses”枚举时,检查每个地址是否为“instanceof Inet4Address”怎么样? - yshavit
我可以尝试一下,但我认为问题在于接口只返回其IPv6地址。让我快速进行一些调试。 - Levi Muniz
看起来我已经在过滤IP地址时只返回IPv4了,因为我使用了 if (ip.get(x + 1).contains("."))。我重写了一些代码,使用 instanceof 替代了 contains(),但这不会改变任何东西。我将在问题中更新一个细节。 - Levi Muniz
只有IPv6地址包含“%”,因为IPv4没有范围ID。 - dave_thompson_085
2个回答

7

来自InterfaceAddress

该类表示网络接口地址。简而言之,它是一个IP地址、子网掩码和广播地址(当地址为IPv4时),或一个IPv6地址的IP地址和网络前缀长度。

这是我的代码:

Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
  NetworkInterface networkInterface = interfaces.nextElement();
  System.out.println(String.format("networkInterface: %s", networkInterface.toString()));

  if (!networkInterface.isUp()) {
    continue;
  }

  for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
    int npf = interfaceAddress.getNetworkPrefixLength();
    InetAddress address = interfaceAddress.getAddress();
    InetAddress broadcast = interfaceAddress.getBroadcast();
    if (broadcast == null && npf != 8) {
      System.out.println(String.format("IPv6: %s; Network Prefix Length: %s", address, npf));
    } else {
      System.out.println(String.format("IPv4: %s; Subnet Mask: %s; Broadcast: %s", address, npf, broadcast));
    }
  }
}

2
这个问题我通过结合使用这两种方法:substring和其他方法得到了解决。 - Levi Muniz

1

我的 Kotlin 解决方案:

  private suspend fun getAllActiveInterfaces(): Sequence<NetworkInterface> {
        return withContext(Dispatchers.IO) {
            NetworkInterface
                .getNetworkInterfaces()
                .asSequence()
                .filter { !it.isLoopback && it.isUp && !blackListedInterfaces.contains(it.name) }
        }
    }

    suspend fun getAllAddresses(): Result<Collection<InetAddress>> {
        return withContextCatching(Dispatchers.IO) {
            getAllActiveInterfaces()
                .map { networkInterface ->
                    networkInterface
                        .also { Timber.d("Interface: ${it.name}") }
                        .interfaceAddresses
                        .mapNotNull { it.address }
                        .filterIsInstance<Inet4Address>()
                }
                .flatten()
                .toSet()
        }
    }

private val blackListedInterfaces = setOf("dummy0")

1
不错!那个"timber"的代码行有点让我迷惑了。一些注释会很好。比我五年前写的干净多了! :P - Levi Muniz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接