Рубрики
Без рубрики

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

Узнайте, как программно взаимодействовать с сетевыми интерфейсами с помощью Java.

Автор оригинала: baeldung.

1. Обзор

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

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

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

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

Конечно, системы часто имеют несколько активных сетевых подключений, таких как проводной Ethernet, Wi-Fi, 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 nifAddresses = nif.getInetAddresses();

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

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

Объект Сетевой интерфейс содержит имя и набор назначенных ему 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);

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

Enumeration nets = NetworkInterface.getNetworkInterfaces();

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

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

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

Мы можем получить IP-адреса, используя два API. Первый API-это getInetAddresses() . Он возвращает Перечисление экземпляров 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() . Он возвращает Список адресов интерфейсов экземпляров, которые являются более мощными, чем 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);
}

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

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

    assertEquals(1500, mtu);
}

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

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

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