Arbeiten mit Netzwerkschnittstellen in Java

Arbeiten mit Netzwerkschnittstellen in Java

1. Überblick

In diesem Artikel konzentrieren wir uns auf Netzwerkschnittstellen und den programmgesteuerten Zugriff auf diese in Java.

Einfach ausgedrückt, anetwork interface is the point of interconnection between a device and any of its network connections.

In der Alltagssprache bezeichnen wir sie mit dem Begriff "Network Interface Cards" (NICs) - aber sie müssen nicht alle Hardware-Form haben.

Zum Beispiel ist die beliebte localhost IP127.0.0.1, die wir häufig zum Testen von Web- und Netzwerkanwendungen verwenden, die Loopback-Schnittstelle - keine direkte Hardwareschnittstelle.

Natürlich verfügen Systeme häufig über mehrere aktive Netzwerkverbindungen, z. B. kabelgebundenes Ethernet, WIFI, Bluetooth usw.

In Java ist die Haupt-API, mit der wir direkt mit ihnen interagieren können, diejava.net.NetworkInterface-Klasse. Um schnell loszulegen, importieren wir das vollständige Paket:

import java.net.*;

2. Warum auf Netzwerkschnittstellen zugreifen?

Die meisten Java-Programme werden wahrscheinlich nicht direkt mit ihnen interagieren. Es gibt jedoch spezielle Szenarien, in denen wir diese Art von Zugriff auf niedriger Ebene benötigen.

Das herausragendste davon ist, wenn ein System mehrere Karten hat und Sie diefreedom to choose a specific interface to use a socket with haben möchten. In einem solchen Szenario kennen wir normalerweise den Namen, aber nicht unbedingt die IP-Adresse.

Normalerweise, wenn wir eine Socket-Verbindung zu einer bestimmten Serveradresse herstellen wollen:

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

Auf diese Weise wählt das System eine geeignete lokale Adresse aus, bindet sie und kommuniziert über seine Netzwerkschnittstelle mit dem Server. Dieser Ansatz erlaubt es uns jedoch nicht, unsere eigene zu wählen.

Wir werden hier eine Annahme machen; Wir kennen die Adresse nicht, aber den Namen. Nehmen wir nur zu Demonstrationszwecken an, wir möchten die Verbindung über die Loopback-Schnittstelle herstellen. Konventionell lautet der Namelo, zumindest auf Linux- und Windows-Systemen, unter OSXlo0:

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));

Also rufen wir zuerst die anlo angeschlossene Netzwerkschnittstelle ab, rufen die daran angehängten Adressen ab, erstellen einen Socket, binden sie an eine der aufgezählten Adressen, die wir zum Zeitpunkt der Kompilierung noch nicht einmal kennen, und stellen dann eine Verbindung her.

Das ObjektNetworkInterfaceenthält einen Namen und eine Reihe von ihm zugewiesenen IP-Adressen. Das Binden an eine dieser Adressen garantiert also die Kommunikation über diese Schnittstelle.

Dies sagt eigentlich nichts Besonderes über die API aus. Wir wissen, dass, wenn wir möchten, dass unsere lokale Adresse localhost ist, das erste Snippet ausreichen würde, wenn wir nur den Bindungscode hinzufügen.

Außerdem müssten wir nie wirklich alle Schritte durchlaufen, da localhost eine bekannte Adresse hat,127.0.0.1, und wir den Socket einfach daran binden können.

In Ihrem Fall könntenlo jedoch möglicherweise andere Schnittstellen wie Bluetooth -net1, drahtloses Netzwerk -net0 oder Ethernet -eth0 dargestellt haben. In solchen Fällen kennen Sie die IP-Adresse beim Kompilieren nicht.

3. Netzwerkschnittstellen abrufen

In diesem Abschnitt werden die anderen verfügbaren APIs zum Abrufen der verfügbaren Schnittstellen untersucht. Im vorherigen Abschnitt haben wir nur einen dieser Ansätze gesehen. die statische Methode vongetByName().

Es ist erwähnenswert, dass dieNetworkInterface-Klasse keine öffentlichen Konstruktoren hat, daher können wir natürlich keine neue Instanz erstellen. Stattdessen werden wir die verfügbaren APIs verwenden, um eine abzurufen.

Die API, die wir bisher angesehen haben, wird verwendet, um eine Netzwerkschnittstelle mit dem angegebenen Namen zu suchen:

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

    assertNotNull(nif);
}

Es gibtnull zurück, wenn keiner für den Namen steht:

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

    assertNull(nif);
}

Die zweite API istgetByInetAddress(). Außerdem müssen wir einen bekannten Parameter angeben. Diesmal können wir die IP-Adresse angeben:

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

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

    assertNotNull(nif);
}

Oder Name des Gastgebers:

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

    assertNotNull(nif);
}

Oder wenn Sie spezifisch über localhost sind:

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

    assertNotNull(nif);
}

Eine andere Alternative besteht auch darin, die Loopback-Schnittstelle explizit zu verwenden:

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

    assertNotNull(nif);
}

Der dritte Ansatz, der erst seit Java 7 verfügbar ist, besteht darin, eine Netzwerkschnittstelle über ihren Index abzurufen:

NetworkInterface nif = NetworkInterface.getByIndex(int index);

Der letzte Ansatz beinhaltet die Verwendung dergetNetworkInterfaces API. Es gibtEnumeration aller verfügbaren Netzwerkschnittstellen im System zurück. Es liegt an uns, die zurückgegebenen Objekte in einer Schleife abzurufen. Die Standardsprache verwendetList:

Enumeration nets = NetworkInterface.getNetworkInterfaces();

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

4. Netzwerkschnittstellenparameter

Es gibt eine Menge wertvoller Informationen, die wir nach dem Abrufen des Objekts von einem erhalten können. One of the most useful is the list of IP addresses assigned to it.

Wir können IP-Adressen mit zwei APIs erhalten. Die erste API istgetInetAddresses(). Es gibtEnumeration vonInetAddress Instanzen zurück, die wir nach Belieben verarbeiten können:

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

Die zweite API istgetInterfaceAddresses(). Es gibtList vonInterfaceAddress Instanzen zurück, die leistungsfähiger sind alsInetAddress Instanzen. Abgesehen von der IP-Adresse könnten Sie beispielsweise an der Broadcast-Adresse interessiert sein:

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

Wir können über eine Schnittstelle auf Netzwerkparameter zugreifen, die über den zugewiesenen Namen und die zugewiesenen IP-Adressen hinausgehen. So prüfen Sie, ob es funktioniert:

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

    assertTrue(nif.isUp());
}

So überprüfen Sie, ob es sich um eine Loopback-Schnittstelle handelt:

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

    assertTrue(nif.isLoopback());
}

So überprüfen Sie, ob es sich um eine Punkt-zu-Punkt-Netzwerkverbindung handelt:

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

    assertFalse(nif.isPointToPoint());
}

Oder wenn es sich um eine virtuelle Schnittstelle handelt:

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

So überprüfen Sie, ob Multicasting unterstützt wird:

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

    assertTrue(nif.supportsMulticast());
}

Oder um die physikalische Adresse abzurufen, die normalerweise als MAC-Adresse bezeichnet wird:

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

    assertNotNull(bytes);
}

Ein weiterer Parameter ist die Maximum Transmission Unit, die die größte Paketgröße definiert, die über diese Schnittstelle übertragen werden kann:

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

    assertEquals(1500, mtu);
}

5. Fazit

In diesem Artikel haben wir Netzwerkschnittstellen gezeigt, wie programmgesteuert auf sie zugegriffen werden kann und warum auf sie zugegriffen werden muss.

Der vollständige Quellcode und die in diesem Artikel verwendeten Beispiele sind inGithub project verfügbar.