Arbeiten mit Netzwerkschnittstellen in Java

1. Übersicht

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

Einfach ausgedrückt ist eine Netzwerkschnittstelle der Verbindungspunkt zwischen einem Gerät und einer seiner Netzwerkverbindungen .

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 IP 127.0.0.1 , die wir häufig zum Testen von Web- und Netzwerkanwendungen verwenden, die Loopback-Schnittstelle - keine direkte Hardwareschnittstelle.

Natürlich haben Systeme häufig mehrere aktive Netzwerkverbindungen, wie z. B. kabelgebundenes Ethernet, WIFI, Bluetooth usw.

In Java ist die Haupt-API, mit der wir direkt mit ihnen interagieren können, die Klasse java.net.NetworkInterface . 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 über mehrere Karten verfügt und Sie die Freiheit haben möchten , eine bestimmte Schnittstelle für die Verwendung eines Sockels auszuwählen . 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 möchten:

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

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

Wir werden hier eine Annahme machen; Wir kennen die Adresse nicht, aber wir kennen den Namen. Nehmen wir nur zu Demonstrationszwecken an, wir möchten die Verbindung über die Loopback-Schnittstelle herstellen. Konventionell lautet der Name lo , zumindest auf Linux- und Windows-Systemen, unter 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));

Also rufen wir zuerst die an lo angeschlossene Netzwerkschnittstelle ab , rufen die damit verbundenen 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.

Ein NetworkInterface- Objekt enthält einen Namen und eine Reihe von ihm zugewiesenen IP-Adressen. Die Bindung an eine dieser Adressen garantiert also die Kommunikation über diese Schnittstelle.

Dies sagt nichts Besonderes über die API aus. Wir wissen, dass, wenn wir möchten, dass unsere lokale Adresse localhost ist, das erste Snippet ausreicht, 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önnte lo 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 zum Zeitpunkt der Kompilierung nicht.

3. Abrufen von Netzwerkschnittstellen

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 getByName () .

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

Die API, die wir uns bisher angesehen haben, wird verwendet, um eine Netzwerkschnittstelle unter dem angegebenen Namen zu durchsuchen:

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

Es gibt null 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 ist getByInetAddress () . Außerdem muss ein bekannter Parameter angegeben werden. 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 sich für localhost interessieren:

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

Eine andere Alternative besteht 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 anhand ihres Index zu erhalten:

NetworkInterface nif = NetworkInterface.getByIndex(int index);

Der letzte Ansatz beinhaltet die Verwendung der getNetworkInterfaces- API. Es gibt eine Aufzählung aller verfügbaren Netzwerkschnittstellen im System zurück. Es liegt an uns, die zurückgegebenen Objekte in einer Schleife abzurufen. Die Standardsprache verwendet eine Liste :

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

4. Netzwerkschnittstellenparameter

Es gibt viele wertvolle Informationen, die wir von einem erhalten können, nachdem wir sein Objekt abgerufen haben. Eine der nützlichsten ist die Liste der ihr zugewiesenen IP-Adressen .

Wir können IP-Adressen mit zwei APIs erhalten. Die erste API ist getInetAddresses () . Es gibt eine Aufzählung von InetAddress- Instanzen zurück, die wir nach eigenem Ermessen 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 ist getInterfaceAddresses () . Es wird eine Liste von InterfaceAddress- Instanzen zurückgegeben, die leistungsfähiger als InetAddress- Instanzen sind. Abgesehen von der IP-Adresse könnte 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 auf Netzwerkparameter einer Schnittstelle zugreifen, die über den Namen und die ihr zugewiesenen IP-Adressen hinausgehen. So überprü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 seine physische 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. Schlussfolgerung

In diesem Artikel haben wir Netzwerkschnittstellen gezeigt, wie man programmgesteuert auf sie zugreift und warum wir auf sie zugreifen müssen.

Der vollständige Quellcode und die in diesem Artikel verwendeten Beispiele sind im Github-Projekt verfügbar.