Работа с сетевыми интерфейсами в Java

Работа с сетевыми интерфейсами в Java

1. обзор

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

Проще говоря, anetwork interface is the point of interconnection between a device and any of its network connections.

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

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

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

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

import java.net.*;

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

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

Самый выдающийся из них - когда в системе есть несколько карт, и вы хотели бы иметьfreedom to choose a specific interface to use a socket with. В таком случае мы обычно знаем имя, но не обязательно IP-адрес.

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

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

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

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

NetworkInterface nif = NetworkInterface.getByName("lo");
Enumeration 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, первого фрагмента было бы достаточно, если бы мы просто добавили код привязки.

Кроме того, нам никогда бы не пришлось проходить все несколько шагов, поскольку у 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);
}

Или, если вы конкретно о localhost:

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

    assertNotNull(nif);
}

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

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

    assertNotNull(nif);
}

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

NetworkInterface nif = NetworkInterface.getByIndex(int index);

Последний подход предполагает использование APIgetNetworkInterfaces. Он возвращаетEnumeration всех доступных сетевых интерфейсов в системе. Нам предстоит получить возвращенные объекты в цикле, стандартная идиома используетList:

Enumeration nets = NetworkInterface.getNetworkInterfaces();

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

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

Существует много ценной информации, которую мы можем получить после получения ее объекта. One of the most useful is the list of IP addresses assigned to it.

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

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

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

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

@Test
public void givenInterface_whenReturnsInterfaceAddresses_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    List 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());
}

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

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

    assertTrue(nif.isUp());
}

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

@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());
}

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

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

    assertNotNull(bytes);
}

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

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

    assertEquals(1500, mtu);
}

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

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

Полный исходный код и примеры, использованные в этой статье, доступны в папкеGithub project.