Travailler avec les interfaces réseau en Java

Travailler avec des interfaces réseau en Java

1. Vue d'ensemble

Dans cet article, nous allons nous concentrer sur les interfaces réseau et sur la manière d'y accéder par programmation en Java.

En termes simples, unnetwork interface is the point of interconnection between a device and any of its network connections.

Dans le langage courant, nous les désignons par le terme de cartes d'interface réseau (NIC) - mais elles ne doivent pas toutes être de forme matérielle.

Par exemple, le populaire localhost IP127.0.0.1, que nous utilisons beaucoup pour tester les applications Web et réseau, est l'interface de bouclage - qui n'est pas une interface matérielle directe.

Bien entendu, les systèmes ont souvent plusieurs connexions réseau actives, telles que Ethernet filaire, WIFI, Bluetooth, etc.

En Java, la principale API que nous pouvons utiliser pour interagir directement avec eux est la classejava.net.NetworkInterface. Et donc, pour commencer rapidement, importons le package complet:

import java.net.*;

2. Pourquoi accéder aux interfaces réseau?

La plupart des programmes Java n'interagiront probablement pas directement avec eux; il existe cependant des scénarios particuliers où nous avons besoin de ce type d'accès de bas niveau.

Le plus remarquable est celui où un système a plusieurs cartes et que vous souhaitez avoir lesfreedom to choose a specific interface to use a socket with. Dans un tel scénario, nous connaissons généralement le nom mais pas nécessairement l'adresse IP.

Normalement, lorsque nous voulons établir une connexion de socket à une adresse de serveur spécifique:

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

De cette façon, le système choisit une adresse locale appropriée, s'y lie et communique avec le serveur via son interface réseau. Cependant, cette approche ne nous permet pas de choisir la nôtre.

Nous ferons une hypothèse ici; nous ne connaissons pas l’adresse mais nous connaissons le nom. Juste à des fins de démonstration, supposons que nous voulons la connexion via l'interface de bouclage, par convention, son nom estlo, au moins sur les systèmes Linux et Windows, sur OSX c'estlo0:

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

Nous récupérons donc l’interface réseau attachée àlo en premier, récupérons les adresses qui lui sont attachées, créons un socket, le lions à l’une des adresses énumérées que nous ne connaissons même pas au moment de la compilation, puis nous nous connectons.

Un objetNetworkInterface contient un nom et un ensemble d'adresses IP qui lui sont attribués. Ainsi, la liaison à l'une de ces adresses garantira la communication via cette interface.

Cela ne dit vraiment rien de spécial à propos de l'API. Nous savons que si nous voulons que notre adresse locale soit localhost, le premier extrait suffira si nous venons d'ajouter le code de liaison.

De plus, nous n'aurions jamais vraiment à passer par toutes les étapes car localhost a une adresse bien connue,127.0.0.1 et nous pouvons facilement y lier le socket.

Cependant, dans votre cas,lo aurait peut-être pu représenter d'autres interfaces comme Bluetooth -net1, le réseau sans fil -net0 ou ethernet -eth0. Dans de tels cas, vous ne sauriez pas l'adresse IP au moment de la compilation.

3. Récupération des interfaces réseau

Dans cette section, nous allons explorer les autres API disponibles pour extraire les interfaces disponibles. Dans la section précédente, nous n'avons vu qu'une de ces approches; la méthode statique degetByName().

Il est intéressant de noter que la classeNetworkInterface n'a pas de constructeur public, nous ne sommes donc bien sûr pas en mesure de créer une nouvelle instance. À la place, nous allons utiliser les API disponibles pour en récupérer une.

L'API que nous avons examinée jusqu'à présent est utilisée pour rechercher une interface réseau sous le nom spécifié:

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

    assertNotNull(nif);
}

Il renvoienull si aucun n'est pour le nom:

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

    assertNull(nif);
}

La deuxième API estgetByInetAddress(), elle nécessite également que nous fournissions un paramètre connu, cette fois nous pouvons fournir l'adresse IP:

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

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

    assertNotNull(nif);
}

Ou nom de l'hôte:

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

    assertNotNull(nif);
}

Ou si vous êtes spécifique à propos de localhost:

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

    assertNotNull(nif);
}

Une autre alternative consiste également à utiliser explicitement l'interface de bouclage:

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

    assertNotNull(nif);
}

La troisième approche, disponible seulement depuis Java 7, consiste à obtenir une interface réseau par son index:

NetworkInterface nif = NetworkInterface.getByIndex(int index);

L'approche finale consiste à utiliser l'APIgetNetworkInterfaces. Il renvoie unEnumeration de toutes les interfaces réseau disponibles dans le système. C'est à nous de récupérer les objets retournés dans une boucle, l'idiome standard utilise unList:

Enumeration nets = NetworkInterface.getNetworkInterfaces();

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

4. Paramètres d'interface réseau

Il y a beaucoup d'informations précieuses que l'on peut obtenir après avoir récupéré son objet. One of the most useful is the list of IP addresses assigned to it.

Nous pouvons obtenir des adresses IP en utilisant deux API. La première API estgetInetAddresses(). Il renvoie unEnumeration d'instancesInetAddress que nous pouvons traiter comme nous le jugeons approprié:

@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 deuxième API estgetInterfaceAddresses(). Il renvoie unList d'instancesInterfaceAddress qui sont plus puissantes que les instancesInetAddress. Par exemple, outre l'adresse IP, l'adresse de diffusion peut vous intéresser:

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

Nous pouvons accéder aux paramètres de réseau concernant une interface au-delà du nom et des adresses IP qui lui sont attribués. Pour vérifier s'il est opérationnel:

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

    assertTrue(nif.isUp());
}

Pour vérifier si c'est une interface de bouclage:

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

    assertTrue(nif.isLoopback());
}

Pour vérifier si cela représente une connexion réseau point à point:

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

    assertFalse(nif.isPointToPoint());
}

Ou s'il s'agit d'une interface virtuelle:

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

Pour vérifier si la multidiffusion est prise en charge:

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

    assertTrue(nif.supportsMulticast());
}

Ou pour récupérer son adresse physique, généralement appelée adresse MAC:

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

    assertNotNull(bytes);
}

Un autre paramètre est l'unité de transmission maximale, qui définit la plus grande taille de paquet pouvant être transmise via cette interface:

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

    assertEquals(1500, mtu);
}

5. Conclusion

Dans cet article, nous avons montré les interfaces réseau, comment y accéder par programme et pourquoi nous en aurions besoin.

Le code source complet et les exemples utilisés dans cet article sont disponibles dans lesGithub project.