Lorsque nous parlons de mise en réseau, nous ne pouvons manquer de mentionner le modèle OSI.

En termes de ce modèle, nous nous intéressons aujourd'hui surtout à la couche transport (4).

C'est le niveau auquel nous travaillons avec des données se déplaçant "du point A au point B". La tâche principale de la couche transport est de s'assurer qu'un message est livré à la destination, tout en maintenant la séquence correcte. Les deux protocoles de couche de transport les plus courants sont : TCP et UDP. Ils fonctionnent conceptuellement de différentes manières, mais chacun a ses propres avantages qui leur permettent de résoudre des problèmes spécifiques.

Voyons d'abord comment fonctionne TCP.

TCP (Transmission Control Protocol) est un protocole réseau qui garantit qu'une connexion entre les hôtes est établie avant l'échange de données.

C'est un protocole très fiable, car chaque fois qu'il envoie un autre paquet de données, il doit vérifier que le paquet précédent a été reçu.

Les paquets transmis sont ordonnés, et s'il y a des problèmes avec un certain paquet (c'est-à-dire que la partie réceptrice ne confirme pas que le paquet est arrivé), alors le paquet est renvoyé. En conséquence, le taux de transfert est relativement faible, car plus de temps est nécessaire pour la surveillance stricte et pour garantir la commande correcte.

C'est là que son "frère", le protocole UDP, entre en jeu. Contrairement à TCP, UDP ne se soucie pas vraiment de l'ordre et du statut de chaque paquet. Il envoie simplement des données sans confirmation de livraison. De plus, il n'établit pas de connexion et ne dépend en aucun cas de l'état de la connexion.

Son but est simplement d'envoyer des données à une adresse. Et cela donne lieu au principal inconvénient du protocole, sa faible fiabilité, car il peut tout simplement perdre des données. De plus, le destinataire doit être préparé au fait que les données peuvent arriver dans le désordre. Cela dit, le protocole a aussi un avantage, un taux de transfert plus élevé, dû au fait que le protocole se limite à envoyer des données.

Il existe également des différences dans la manière dont les données elles-mêmes sont transmises. Dans TCP, les données sont diffusées en continu, ce qui signifie que les données n'ont pas de frontières. Dans UDP, les données sont transmises sous forme de datagrammes et ont des limites, et le destinataire vérifie l'intégrité des données, mais uniquement si le message est reçu avec succès.

Résumons :

TCP est un protocole fiable et précis qui empêche la perte de données. Un message sera toujours livré avec une précision maximale, ou pas du tout. Le destinataire n'a pas besoin de logique pour trier les données, puisque les données entrantes seront déjà triées. UDP n'est pas aussi fiable, mais c'est un protocole de transfert de données plus rapide. Les parties émettrices et réceptrices ont besoin d'une logique supplémentaire pour travailler avec ce protocole. Mais regardons comment cela fonctionne en utilisant l'exemple d'un jeu sur ordinateur ou d'un jeu mobile joué sur le réseau. Nous ne nous soucions peut-être plus de ce qui aurait dû arriver il y a 5 secondes, et nous pouvons sauter quelques paquets s'ils n'arrivent pas à l'heure — le jeu peut prendre du retard, mais vous pouvez toujours jouer !

En Java, pour travailler avec des datagrammes transmis via UDP, nous utilisons des objets des classes DatagramSocket et DatagramPacket .

Pour échanger des données, l'expéditeur et le destinataire créent des sockets de datagramme, c'est-à-dire des instances de la classe DatagramSocket . La classe a plusieurs constructeurs. La différence entre eux est l'endroit où le socket créé se connectera :

DatagrammeSocket () Se connecte à n'importe quel port disponible sur la machine locale
DatagramSocket (port int) Se connecte au port spécifié sur la machine locale
DatagramSocket (port int, adresse InetAddress) Se connecte au port spécifié à une adresse sur la machine locale (addr)

La classe contient de nombreuses méthodes d'accès et de gestion des paramètres de socket (nous les verrons un peu plus tard), ainsi que des méthodes de réception et d'envoi de datagrammes :

envoyer (paquet DatagramPacket) Envoie des datagrammes emballés dans des paquets
recevoir (paquet DatagramPacket) Reçoit des datagrammes emballés dans des paquets

DatagramPacket est une classe qui représente un package de datagrammes. Les paquets de datagrammes sont utilisés pour mettre en œuvre un service de livraison de paquets sans connexion. Chaque message est acheminé d'une machine à une autre en fonction uniquement des informations contenues dans ce paquet. Plusieurs paquets envoyés d'une machine à une autre peuvent être acheminés différemment et peuvent arriver dans n'importe quel ordre. La livraison des paquets n'est pas garantie.

Constructeurs :

DatagramPacket(byte[] buf, int length) Crée un DatagramPacket pour accepter les paquets de longueur length .
DatagramPacket (octet [] buf, longueur int, adresse InetAddress, port int) Crée un paquet de datagrammes pour envoyer des paquets de longueur au numéro de port spécifié sur l'hôte spécifié.
DatagramPacket(byte[] buf, int offset, int length) Crée un DatagramPacket pour accepter les paquets de longueur length , en spécifiant un décalage dans la mémoire tampon.
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) Crée un paquet de datagrammes pour envoyer des paquets de longueur length avec offset offset au numéro de port spécifié sur l'hôte spécifié.
DatagramPacket (octet [] buf, décalage int, longueur int, adresse SocketAddress) Crée un paquet de datagrammes pour envoyer des paquets de longueur length avec offset offset au numéro de port spécifié sur l'hôte spécifié.
DatagramPacket (octet [] buf, longueur int, adresse SocketAddress) Crée un paquet de datagrammes pour envoyer des paquets de longueur au numéro de port spécifié sur l'hôte spécifié.

Nous rappelons que l'approche UDP n'établit pas de connexion. Les paquets sont envoyés dans l'espoir que le destinataire les attend. Mais vous pouvez établir une connexion en utilisant la méthode connect(InetAddress addr, int port) de la classe DatagramSocket .

Une connexion unidirectionnelle est établie avec l'hôte sur la base d'une adresse et d'un port : soit pour envoyer soit pour recevoir des datagrammes. La connexion peut être interrompue à l'aide de la méthode disconnect() .

Essayons d'écrire du code serveur basé sur DatagramSocket pour recevoir des données :


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

class Recipient {

   public static void main(String[] args) {
       try {
           DatagramSocket ds = new DatagramSocket(1050);

           while (true) {
               DatagramPacket pack = new DatagramPacket(new byte[5], 5);
               ds.receive(pack);
               System.out.println(new String(pack.getData()));
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

Nous créons un objet DatagramSocket pour écouter sur le port 1050. Lorsqu'il reçoit un message, il l'imprime sur la console. Nous transmettrons le mot "Hello", nous limitons donc la taille du tampon à cinq octets.

Nous allons maintenant créer la classe sender :


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

class Sender {
   private String host;
   private int port;

   Sender(String host, int port) {
       this.host = host;
       this.port = port;
   }

   private void sendMessage(String mes) {
       try {
           byte[] data = mes.getBytes();
           InetAddress address = InetAddress.getByName(host);
           DatagramPacket pack = new DatagramPacket(data, data.length, address, port);
           DatagramSocket ds = new DatagramSocket();
           ds.send(pack);
           ds.close();
       } catch (IOException e) {
           System.err.println(e);
       }
   }

   public static void main(String[] args) {
   Sender sender = new Sender("localhost", 1050);
   String message = "Hello";

   Timer timer = new Timer();
   timer.scheduleAtFixedRate(new TimerTask() {
       @Override
       public void run() {
           sender.sendMessage(message);
       }
   }, 1000, 1000);
}

}

Dans la méthode sendMessage , nous créons un DatagramPacket et un DatagramSocket et envoyons notre message. Notez que la méthode close() est utilisée pour fermer le DatagramSocket après l'envoi du message.

Chaque seconde, la console du destinataire affiche le message "Hello" entrant envoyé par l'expéditeur. Cela signifie que notre communication fonctionne correctement.