Как в java инстанцировать класс, описывающий IPv4 адрес?

Необходим метод, генерирующий случайный IPv4 адрес из заданного диапазона. Не получается задать сам диапазон. Моё гугление привело меня к тому, что для хранения адреса можно использовать класс Inet4Address, но:

  • Судя по документации, экземпляры этого класса инстанцируются с помощью метода getByName, который может бросить исключение UnknownHostException;
  • Т.е., проблема в том, что этот метод является швейцарским ножом, который может превратить классическую строку вида 192.168.0.1 в необходимый объект, либо выполнить резолюцию DNS-имени, если передали domain.com. И в случае с резолюцией, как я понимаю, и может возникнуть исключение выше;
  • Я хочу задать диапазоны адресов в классическом виде, без необходимости выполнения DNS-резолюций. Т.е. я точно знаю, что исключения UnknownHostException возникнуть не может, но я обязательно должен его обработать.

При таком варианте мой код получился такой:


import com.sun.tools.javac.util.Pair;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;


class IPRanges {
    static final ArrayList<Pair<InetAddress, InetAddress>> IPv4GlobalRanges = new ArrayList<Pair<InetAddress, InetAddress>>() {{
        try {
            add(new Pair<>(InetAddress.getByName("1.0.0.0"), InetAddress.getByName("9.255.255.255")));
            add(new Pair<>(InetAddress.getByName("11.0.0.0"), InetAddress.getByName("100.63.255.255")));
            add(new Pair<>(InetAddress.getByName("100.128.0.0"), InetAddress.getByName("126.255.255.255")));
            add(new Pair<>(InetAddress.getByName("128.0.0.0"), InetAddress.getByName("169.253.255.255")));
            add(new Pair<>(InetAddress.getByName("169.255.0.0"), InetAddress.getByName("172.15.255.255")));
            add(new Pair<>(InetAddress.getByName("172.32.0.0"), InetAddress.getByName("191.255.255.255")));
            add(new Pair<>(InetAddress.getByName("192.0.1.0"), InetAddress.getByName("192.0.1.255")));
            add(new Pair<>(InetAddress.getByName("192.0.3.0"), InetAddress.getByName("192.167.255.255")));
            add(new Pair<>(InetAddress.getByName("192.169.0.0"), InetAddress.getByName("198.17.255.255")));
            add(new Pair<>(InetAddress.getByName("198.20.0.0"), InetAddress.getByName("198.51.99.255")));
            add(new Pair<>(InetAddress.getByName("198.51.101.0"), InetAddress.getByName("203.0.112.255")));
            add(new Pair<>(InetAddress.getByName("203.0.114.0"), InetAddress.getByName("223.255.255.255")));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }};
}

Я не уверен, что так делать "правильно" и мне не нравится, что:

  1. наполнение диапазона надо делать через try ... catch, хотя я знаю, что исключения там точно не возникнет;
  2. Необходим диапазон именно IPv4 адресов. Если явно приводить получаемый InetAddress к Inet4Address, то получится ещё бОльшая простыня кода.

Так же все конструкторы Inet4Address являются private, что не даёт их вызывать напрямую (в моём случае это бы решило проблему).


Как "правильно" решить задачу (конкретно, инстанцировать экземпляр класса, описывающего IPv4 адрес без лишних обработок исключений)? Это не обязательно должен быть Inet4Address, но хотелось бы, чтобы это было что-то built-in, дабы не возникали лишние зависимости.


Ответы (2 шт):

Автор решения: Михаил Муругов

В итоге, пришёл к такому варианту:

import com.sun.tools.javac.util.Pair;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;


class IPRanges {
    static final ArrayList<Pair<Inet4Address, Inet4Address>> IPv4GlobalRanges = new ArrayList<Pair<Inet4Address, Inet4Address>>() {{
        try {
            add(new Pair<>((Inet4Address)InetAddress.getByName("1.0.0.0"), (Inet4Address)InetAddress.getByName("9.255.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("11.0.0.0"), (Inet4Address)InetAddress.getByName("100.63.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("100.128.0.0"), (Inet4Address)InetAddress.getByName("126.255.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("128.0.0.0"), (Inet4Address)InetAddress.getByName("169.253.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("169.255.0.0"), (Inet4Address)InetAddress.getByName("172.15.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("172.32.0.0"), (Inet4Address)InetAddress.getByName("191.255.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("192.0.1.0"), (Inet4Address)InetAddress.getByName("192.0.1.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("192.0.3.0"), (Inet4Address)InetAddress.getByName("192.167.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("192.169.0.0"), (Inet4Address)InetAddress.getByName("198.17.255.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("198.20.0.0"), (Inet4Address)InetAddress.getByName("198.51.99.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("198.51.101.0"), (Inet4Address)InetAddress.getByName("203.0.112.255")));
            add(new Pair<>((Inet4Address)InetAddress.getByName("203.0.114.0"), (Inet4Address)InetAddress.getByName("223.255.255.255")));
        } catch (UnknownHostException e) {
            throw new AssertionError(e);
        }
    }};
}

Т.е.:

  • Везде сделал явное приведение к Inet4Address;
  • В catch блоке заменил e.printStackTrace(); на throw new AssertionError(e);, т.к. на англоязычном SO нашёл ответ, в котором говорится, что AssertionError следует бросать в тех местах, где исключение произойти не может.
→ Ссылка
Автор решения: Jegors Čemisovs

Могу предложить задавать диапазон IPv4 не с помощью класса Inet4Address, а с помощью битового представления адреса. Таким образом, для задания диапазона нам нужно либо два числа int, либо одно число типа long.

Алгоритм преобразования: https://habr.com/ru/post/679008/

→ Ссылка