«1. Обзор

В этой статье мы сосредоточимся на сетевых интерфейсах и на том, как получить к ним программный доступ в Java.

Проще говоря, сетевой интерфейс — это точка взаимодействия между устройством и любым его сетевым соединением.

На повседневном языке мы называем их сетевыми интерфейсными картами (NIC), но они не обязательно должны быть аппаратными.

Например, популярный локальный IP-адрес 127.0.0.1, который мы часто используем при тестировании веб-приложений и сетевых приложений, является петлевым интерфейсом, который не является прямым аппаратным интерфейсом.

Конечно, системы часто имеют несколько активных сетевых подключений, таких как проводной Ethernet, WIFI, Bluetooth и т. д.

В Java основным API, который мы можем использовать для прямого взаимодействия с ними, является класс java.net.NetworkInterface. . Итак, чтобы быстро приступить к работе, давайте импортируем полный пакет:

import java.net.*;

2. Зачем нужен доступ к сетевым интерфейсам?

Большинство Java-программ, вероятно, не будут взаимодействовать с ними напрямую; однако есть особые сценарии, когда нам действительно нужен такой низкоуровневый доступ.

Наиболее выдающимся из них является случай, когда в системе имеется несколько плат, и вы хотели бы иметь свободу выбора определенного интерфейса для использования сокета. В таком сценарии мы обычно знаем имя, но не обязательно IP-адрес.

Обычно, когда мы хотим установить сокетное соединение с определенным адресом сервера:

Socket socket = new Socket();
socket.connect(new InetSocketAddress(address, port));

Таким образом, система выберет подходящий локальный адрес, привяжется к нему и свяжется с сервером через его сетевой интерфейс. . Однако такой подход не позволяет нам выбирать самостоятельно.

Здесь мы сделаем предположение; мы не знаем адрес, но мы знаем имя. Просто для демонстрационных целей предположим, что нам нужно соединение через петлевой интерфейс, по соглашению его имя lo, по крайней мере, в системах Linux и Windows, в OSX это lo0:

NetworkInterface nif = NetworkInterface.getByName("lo");
Enumeration<InetAddress> nifAddresses = nif.getInetAddresses();

Socket socket = new Socket();
socket.bind(new InetSocketAddress(nifAddresses.nextElement(), 0));
socket.connect(new InetSocketAddress(address, port));

Итак, мы получаем сетевой интерфейс сначала прикрепляется к lo, получает прикрепленные к нему адреса, создает сокет, привязывает его к любому из перечисленных адресов, которые мы даже не знаем во время компиляции, а затем подключаемся.

Объект NetworkInterface содержит имя и набор назначенных ему IP-адресов. Таким образом, привязка к любому из этих адресов гарантирует связь через этот интерфейс.

Это ничего особенного не говорит об API. Мы знаем, что если мы хотим, чтобы наш локальный адрес был локальным, первого фрагмента было бы достаточно, если бы мы просто добавили код привязки.

Кроме того, нам никогда не придется выполнять все несколько шагов, поскольку у localhost есть один хорошо известный адрес, 127.0.0.1, и мы можем легко привязать к нему сокет.

Однако в вашем случае lo мог бы представлять другие интерфейсы, такие как Bluetooth — net1, беспроводная сеть — net0 или ethernet — eth0. В таких случаях вы не будете знать IP-адрес во время компиляции.

3. Получение сетевых интерфейсов

В этом разделе мы рассмотрим другие доступные API для получения доступных интерфейсов. В предыдущем разделе мы видели только один из этих подходов; статический метод getByName().

Стоит отметить, что у класса NetworkInterface нет публичных конструкторов, поэтому мы, конечно, не можем создать новый экземпляр. Вместо этого мы собираемся использовать доступные API для его извлечения.

API, который мы рассмотрели до сих пор, используется для поиска сетевого интерфейса по указанному имени:

@Test
public void givenName_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertNotNull(nif);
}

Возвращает null, если имя не указано:

@Test
public void givenInExistentName_whenReturnsNull_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("inexistent_name");

    assertNull(nif);
}

Второй API — getByInetAddress( ), это также требует, чтобы мы предоставили известный параметр, на этот раз мы можем указать IP-адрес:

@Test
public void givenIP_whenReturnsNetworkInterface_thenCorrect() {
    byte[] ip = new byte[] { 127, 0, 0, 1 };

    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getByAddress(ip));

    assertNotNull(nif);
}

Или имя хоста:

@Test
public void givenHostName_whenReturnsNetworkInterface_thenCorrect()  {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getByName("localhost"));

    assertNotNull(nif);
}

Или, если вы конкретно указываете локальный хост:

@Test
public void givenLocalHost_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getLocalHost());

    assertNotNull(nif);
}

Другой альтернативой является явное использование интерфейса loopback:

@Test
public void givenLoopBack_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getLoopbackAddress());

    assertNotNull(nif);
}

Третий подход, который стал доступен только после Java 7, заключается в получении сетевого интерфейса по его индексу:

NetworkInterface nif = NetworkInterface.getByIndex(int index);

«

Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();

for (NetworkInterface nif: Collections.list(nets)) {
    //do something with the network interface
}

«Последний подход предполагает использование API getNetworkInterfaces. Он возвращает перечисление всех доступных сетевых интерфейсов в системе. Мы должны извлечь возвращенные объекты в цикле, стандартная идиома использует список:

4. Параметры сетевого интерфейса

После извлечения объекта мы можем получить много ценной информации. Одним из наиболее полезных является список присвоенных ему IP-адресов.

@Test
public void givenInterface_whenReturnsInetAddresses_thenCorrect()  {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    Enumeration<InetAddress> addressEnum = nif.getInetAddresses();
    InetAddress address = addressEnum.nextElement();

    assertEquals("127.0.0.1", address.getHostAddress());
}

Мы можем получить IP-адреса, используя два API. Первый API — это getInetAddresses(). Он возвращает перечисление экземпляров InetAddress, которые мы можем обрабатывать по своему усмотрению:

@Test
public void givenInterface_whenReturnsInterfaceAddresses_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    List<InterfaceAddress> addressEnum = nif.getInterfaceAddresses();
    InterfaceAddress address = addressEnum.get(0);

    InetAddress localAddress=address.getAddress();
    InetAddress broadCastAddress = address.getBroadcast();

    assertEquals("127.0.0.1", localAddress.getHostAddress());
    assertEquals("127.255.255.255",broadCastAddress.getHostAddress());
}

Второй API — getInterfaceAddresses(). Он возвращает список экземпляров InterfaceAddress, которые являются более мощными, чем экземпляры InetAddress. Например, помимо IP-адреса вас может интересовать широковещательный адрес:

@Test
public void givenInterface_whenChecksIfUp_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.isUp());
}

Мы можем получить доступ к сетевым параметрам интерфейса помимо имени и назначенных ему IP-адресов. Чтобы проверить, работает ли он:

@Test
public void givenInterface_whenChecksIfLoopback_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.isLoopback());
}

Чтобы проверить, является ли он петлевым интерфейсом:

@Test
public void givenInterface_whenChecksIfPointToPoint_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertFalse(nif.isPointToPoint());
}

Чтобы проверить, представляет ли он сетевое соединение точка-точка:

@Test
public void givenInterface_whenChecksIfVirtual_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    assertFalse(nif.isVirtual());
}

Или, если это виртуальный интерфейс:

@Test
public void givenInterface_whenChecksMulticastSupport_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.supportsMulticast());
}

Чтобы проверить, поддерживается ли многоадресная рассылка:

@Test
public void givenInterface_whenGetsMacAddress_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    byte[] bytes = nif.getHardwareAddress();

    assertNotNull(bytes);
}

Или чтобы получить его физический адрес, обычно называемый MAC-адресом:

@Test
public void givenInterface_whenGetsMTU_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("net0");
    int mtu = nif.getMTU();

    assertEquals(1500, mtu);
}

Другим параметром является Максимальная единица передачи, которая определяет максимальный размер пакета, который может быть передан через этот интерфейс:

5. Заключение

В этой статье мы показали сетевые интерфейсы, как получить к ним доступ программно и зачем нам нужен доступ к ним.