Lavorare con le interfacce di rete in Java

1. Panoramica

In questo articolo, ci concentreremo sulle interfacce di rete e su come accedervi a livello di programmazione in Java.

In poche parole, un'interfaccia di rete è il punto di interconnessione tra un dispositivo e le sue connessioni di rete .

Nel linguaggio di tutti i giorni, ci riferiamo a loro con il termine schede di interfaccia di rete (NIC), ma non devono essere tutte di forma hardware.

Ad esempio, il popolare localhost IP 127.0.0.1 , che usiamo molto per testare le applicazioni web e di rete, è l'interfaccia di loopback, che non è un'interfaccia hardware diretta.

Naturalmente, i sistemi hanno spesso più connessioni di rete attive, come Ethernet cablata, WIFI, Bluetooth, ecc.

In Java, l'API principale che possiamo utilizzare per interagire direttamente con loro è la classe java.net.NetworkInterface . Quindi, per iniziare rapidamente, importiamo il pacchetto completo:

import java.net.*;

2. Perché accedere alle interfacce di rete?

La maggior parte dei programmi Java probabilmente non interagirà direttamente con loro; ci sono tuttavia scenari speciali in cui abbiamo bisogno di questo tipo di accesso di basso livello.

Il più eccezionale di questi è quando un sistema ha più schede e si desidera avere la libertà di scegliere un'interfaccia specifica con cui utilizzare un socket . In un tale scenario, di solito conosciamo il nome ma non necessariamente l'indirizzo IP.

Normalmente, quando vogliamo effettuare una connessione socket a un indirizzo server specifico:

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

In questo modo, il sistema sceglierà un indirizzo locale appropriato, si collegherà ad esso e comunicherà al server attraverso la sua interfaccia di rete. Tuttavia, questo approccio non ci consente di scegliere il nostro.

Faremo un'ipotesi qui; non conosciamo l'indirizzo ma conosciamo il nome. Solo a scopo dimostrativo, supponiamo di volere la connessione tramite l'interfaccia di loopback, per convenzione, il suo nome è lo , almeno su sistemi Linux e Windows, su 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));

Quindi recuperiamo prima l'interfaccia di rete collegata a lo , recuperiamo gli indirizzi ad essa collegati, creiamo un socket, lo colleghiamo a uno qualsiasi degli indirizzi enumerati che non conosciamo nemmeno in fase di compilazione e quindi ci colleghiamo.

Un oggetto NetworkInterface contiene un nome e una serie di indirizzi IP ad esso assegnati. Quindi il vincolo a uno qualsiasi di questi indirizzi garantirà la comunicazione attraverso questa interfaccia.

Questo in realtà non dice nulla di speciale sull'API. Sappiamo che se vogliamo che il nostro indirizzo locale sia localhost, il primo frammento sarebbe sufficiente se aggiungessimo solo il codice di associazione.

Inoltre, non dovremmo mai eseguire tutti i diversi passaggi poiché localhost ha un indirizzo ben noto, 127.0.0.1 e possiamo facilmente associare il socket ad esso.

Tuttavia, nel tuo caso, lo potrebbe forse aver rappresentato altre interfacce come Bluetooth - net1 , rete wireless - net0 o ethernet - eth0 . In questi casi, non conosceresti l'indirizzo IP in fase di compilazione.

3. Recupero delle interfacce di rete

In questa sezione, esploreremo le altre API disponibili per il recupero delle interfacce disponibili. Nella sezione precedente, abbiamo visto solo uno di questi approcci; il metodo statico getByName () .

Vale la pena notare che la classe NetworkInterface non ha alcun costruttore pubblico, quindi ovviamente non siamo in grado di creare una nuova istanza. Invece, useremo le API disponibili per recuperarne una.

L'API che abbiamo esaminato finora viene utilizzata per cercare un'interfaccia di rete con il nome specificato:

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

Restituisce null se nessuno è per il nome:

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

La seconda API è getByInetAddress () , richiede anche di fornire un parametro noto, questa volta possiamo fornire l'indirizzo IP:

@Test public void givenIP_whenReturnsNetworkInterface_thenCorrect() { byte[] ip = new byte[] { 127, 0, 0, 1 }; NetworkInterface nif = NetworkInterface.getByInetAddress( InetAddress.getByAddress(ip)); assertNotNull(nif); }

O nome dell'ospite:

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

O se sei specifico su localhost:

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

Un'altra alternativa è anche usare esplicitamente l'interfaccia di loopback:

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

Il terzo approccio disponibile solo da Java 7 è ottenere un'interfaccia di rete dal suo indice:

NetworkInterface nif = NetworkInterface.getByIndex(int index);

L'approccio finale prevede l' utilizzo dell'API getNetworkInterfaces . Restituisce un'enumerazione di tutte le interfacce di rete disponibili nel sistema. Sta a noi recuperare gli oggetti restituiti in un ciclo, l'idioma standard utilizza un elenco :

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

4. Parametri dell'interfaccia di rete

Ci sono molte informazioni preziose che possiamo ottenere da uno dopo aver recuperato il suo oggetto. Uno dei più utili è l'elenco degli indirizzi IP ad esso assegnati .

Possiamo ottenere indirizzi IP utilizzando due API. La prima API è getInetAddresses () . Restituisce un'enumerazione di istanze InetAddress che possiamo elaborare come riteniamo opportuno:

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

La seconda API è getInterfaceAddresses () . Restituisce un elenco di istanze InterfaceAddress che sono più potenti delle istanze InetAddress . Ad esempio, oltre all'indirizzo IP, potresti essere interessato all'indirizzo di trasmissione:

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

Possiamo accedere ai parametri di rete su un'interfaccia oltre al nome e agli indirizzi IP ad essa assegnati. Per verificare se è attivo e in esecuzione:

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

Per verificare se si tratta di un'interfaccia di loopback:

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

Per verificare se rappresenta una connessione di rete punto a punto:

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

O se è un'interfaccia virtuale:

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

Per verificare se il multicasting è supportato:

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

O per recuperare il suo indirizzo fisico, solitamente chiamato indirizzo MAC:

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

Un altro parametro è l'Unità di trasmissione massima che definisce la dimensione del pacchetto più grande che può essere trasmessa attraverso questa interfaccia:

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

5. conclusione

In questo articolo abbiamo mostrato le interfacce di rete, come accedervi a livello di programmazione e perché avremmo bisogno di accedervi.

Il codice sorgente completo e gli esempi utilizzati in questo articolo sono disponibili nel progetto Github.