0% ont trouvé ce document utile (0 vote)
8 vues37 pages

Programmation Réeau Java

Ce document présente un cours sur la programmation réseau en Java, abordant des concepts clés tels que les différences entre TCP et UDP, l'utilisation des sockets, et les principes de multithreading. Il explore également les topologies réseau, les systèmes de messagerie, et la réimplantation de protocoles d'application. Enfin, des exemples pratiques en Java illustrent les concepts discutés, notamment à travers des applications de chat et des communications bloquantes et non bloquantes.

Transféré par

dp4453452
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
8 vues37 pages

Programmation Réeau Java

Ce document présente un cours sur la programmation réseau en Java, abordant des concepts clés tels que les différences entre TCP et UDP, l'utilisation des sockets, et les principes de multithreading. Il explore également les topologies réseau, les systèmes de messagerie, et la réimplantation de protocoles d'application. Enfin, des exemples pratiques en Java illustrent les concepts discutés, notamment à travers des applications de chat et des communications bloquantes et non bloquantes.

Transféré par

dp4453452
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Notes cours:

Programmation
Réseau - Java

Enseignant :
Dr. BADR EL KHALYLY
Année Universitaire
2024 - 2025
Table des matières
1. Introduction à la Programmation Réseau .............................................................................. 4
1.1. Concepts de base du réseau .............................................................................................. 4
1.2. Différences entre TCP et UDP .......................................................................................... 4
1.3. Notion de sockets ............................................................................................................. 4
Exemple avec un socket : .................................................................................................... 5
1.4. Sockets vs Datagramme : ................................................................................................. 5
Sockets orientés connexion (TCP) : .................................................................................... 5
Sockets sans connexion (UDP) : ......................................................................................... 6
1.5. Étapes de création et de gestion d'un socket (en Java) .................................................... 7
Exemple simple de communication client-serveur avec des sockets TCP : ........................ 7
Exemple simple de communication client-serveur avec des sockets UDP : ....................... 8
1.6. Communication entre deux Applications à travers la Stack TCP/IP .............................. 11
2. Multithreading en Java ........................................................................................................ 12
2.1. Introduction aux Threads .............................................................................................. 12
2.2. Concepts Clés du Multithreading .................................................................................. 12
2.3. Créer et Démarrer un Thread en Java ........................................................................... 12
2.3.1. Utiliser la classe Thread........................................................................................... 12
2.3.2. Utiliser l’interface Runnable ................................................................................... 13
2.3.3. Utiliser ExecutorService.......................................................................................... 13
2.4. Synchronisation et Ressources Partagées ..................................................................... 14
2.4.1. Utiliser synchronized ............................................................................................... 14
2.4.2. Bloc synchronized ................................................................................................... 14
2.5. Problèmes Communes avec le Multithreading .............................................................. 15
3. Communication Bloquante vs Non Bloquante ..................................................................... 16
3.1. Communication Bloquante ............................................................................................ 16
3.1.1. Exemple en Java ...................................................................................................... 16
3.1.2. Limitations de la Communication Bloquante ..........................................................17
3.2. Communication Non Bloquante .....................................................................................17
3.2.1. Exemple en Java avec NIO .......................................................................................17
3.2.2. Avantages de la Communication Non Bloquante ................................................... 18
3.2.3. Inconvénients .......................................................................................................... 19
3.3. Comparaison Bloquant vs Non Bloquant ...................................................................... 19
3.4. Bloquante vs Non-bloquante : ....................................................................................... 19
4. Topologies Réseau ................................................................................................................ 19
4.1. Topologie en Étoile (Star Topology) .............................................................................. 19
4.1.1. Exemple en Java (Serveur en Étoile) ....................................................................... 19
4.1.2. Avantages et Inconvénients ..................................................................................... 21
4.2. Topologie en Anneau (Ring Topology) .......................................................................... 21
4.2.1. Exemple en Java (Topologie en Anneau) ................................................................ 21

TITRE DU RAPPORT 2
4.2.2. Avantages et Inconvénients .................................................................................... 22
4.3. Topologie en Bus (Bus Topology) .................................................................................. 22
4.3.1. Exemple en Java ...................................................................................................... 22
4.3.2. Avantages et Inconvénients .................................................................................... 23
4.4. Topologie Maillée (Mesh Topology) .............................................................................. 23
4.4.1. Exemple en Java ...................................................................................................... 23
5. Introduction aux Brokers et Systèmes de Messagerie .......................................................... 25
5.1. Rôle du Broker de Messages........................................................................................... 25
5.2. Mécanismes de Communication .................................................................................... 25
5.3. Exemples d'Utilisation des Brokers ............................................................................... 25
5.4. Propriétés des Systèmes de Messagerie......................................................................... 26
5.5. Exemple d'Implémentation d'un Broker Simple en Java .............................................. 26
5.6. Conclusion ..................................................................................................................... 28
5.7. Notions de Topic, Queue et Exchange ........................................................................... 28
5.7.1. Queue ....................................................................................................................... 28
5.7.2. Topic ........................................................................................................................ 28
5.7.3. Exchange ................................................................................................................. 29
5.7.4. Comparaison entre Queue, Topic, et Exchange ...................................................... 30
6. Réimplantation des Protocoles de Couches d'Application en Sockets et Threads ............... 30
6.1. Principes Fondamentaux des Protocoles de Couches d'Application ............................. 30
6.2. Réimplantation du Protocole REST avec Sockets et Threads ....................................... 31
6.3. Réimplantation du Protocole MQTT avec Sockets et Threads ...................................... 31
6.4. Réimplantation du Protocole AMQP ............................................................................. 32
6.5. Réimplantation du Protocole CoAP ............................................................................... 32
6.6. Réimplantation du Protocole XMPP ............................................................................. 32
6.7. Réimplantation du Protocole STOMP ........................................................................... 33
7. Clone et Ré-implémentation d'Applications de Chat ........................................................... 34
a) WhatsApp : Messagerie instantanée avec chiffrement E2EE (End-to-End Encryption) 34
b) Telegram : Messagerie basée sur le cloud ........................................................................ 35
c) Signal : Chiffrement avancé et gestion des métadonnées ................................................ 35
d) Appels VoIP : Utilisation d'UDP pour les appels audio/vidéo en temps réel .................. 36

TITRE DU RAPPORT 3
1. Introduction à la
Programmation Réseau
1.1. Concepts de base du réseau
La programmation réseau repose sur l'échange de données entre différentes machines connectées
via un réseau. Il existe deux architectures fondamentales utilisées pour ces échanges :
 Client-Serveur : Une machine (le client) envoie des requêtes à une autre machine (le
serveur), qui les traite et renvoie des réponses.
 Peer-to-Peer (P2P) : Chaque machine peut jouer à la fois le rôle de client et de serveur, et
les échanges se font directement entre elles sans serveur centralisé.

1.2. Différences entre TCP et UDP


Lorsqu’on parle de programmation réseau, il est important de comprendre les deux principaux
protocoles utilisés pour le transport des données : TCP (Transmission Control Protocol) et
UDP (User Datagram Protocol).
 TCP est un protocole de transport fiable. Il garantit que toutes les données envoyées arrivent
dans le bon ordre sans perte. Pour ce faire, il utilise des mécanismes comme le contrôle de
flux, la gestion des connexions et la retransmission des paquets perdus. C'est pourquoi TCP
est utilisé dans des applications critiques où la perte de données n'est pas acceptable, comme
les transactions bancaires, le transfert de fichiers ou les protocoles HTTP.
 UDP, en revanche, est un protocole plus simple et rapide, qui n'assure ni le suivi ni la
retransmission des paquets perdus. Cela signifie que certaines données peuvent être perdues
ou arriver dans le désordre. Cependant, UDP est plus performant en termes de latence, et est
souvent utilisé dans des applications où la vitesse est plus importante que la fiabilité, comme
les jeux en ligne ou le streaming vidéo en temps réel.

1.3. Notion de sockets


Un socket est une interface de programmation réseau qui permet à deux machines de communiquer
sur un réseau (comme l'internet ou un réseau local). Il représente un point de communication entre
un programme en cours d'exécution sur une machine (le client ou le serveur) et un autre programme
sur une machine distante.
Une socket serveur permet à une application serveur d'être détectée et de recevoir des
connexions de clients.
Établir un point de communication signifie créer une connexion entre deux entités
(généralement des machines ou des processus) pour permettre l'échange de données sur un réseau.
C'est un concept fondamental en réseau, où une communication se déroule à travers des points
d'entrée et de sortie bien définis, appelés sockets.
Explication :
Lorsque nous parlons de point de communication, cela implique deux éléments essentiels :
1. L'identification des participants à la communication : Chaque machine ou processus
qui communique sur un réseau est identifié par :
o Une adresse IP (qui identifie l'ordinateur ou le dispositif sur le réseau).
o Un port (qui identifie une application ou un service spécifique sur cet ordinateur).

TITRE DU RAPPORT 4
2. La transmission des données : Une fois qu'un point de communication est établi entre
deux entités (par exemple, un client et un serveur), les données peuvent circuler dans les deux
sens via ce point.

Exemple avec un socket :


Imaginons que vous établissiez une connexion entre un client et un serveur :
 Le serveur écoute sur un socket TCP spécifique, identifié par son adresse IP (par exemple,
[Link]) et un port (par exemple, 8080).
 Le client se connecte à ce même socket en spécifiant l'adresse IP du serveur et son port,
ainsi que son propre socket local (qui a aussi une adresse IP et un port).
En établissant ce point de communication, les deux parties peuvent maintenant échanger des
données (sous forme de segments TCP). Le socket gère les détails de la connexion : établir,
maintenir et fermer la communication.
Établir un point de communication veut dire :
 Créer un chemin ou un lien qui permet à deux applications (ou machines) d'échanger des
données.
 Dans le cas de TCP/IP, cela signifie créer un socket (avec une adresse IP et un port) qui
permet d'envoyer et de recevoir des informations entre deux entités sur le réseau.
En Java, la bibliothèque [Link] offre les classes nécessaires pour créer et gérer des sockets, comme
Socket et ServerSocket.
1. Fonctionnement général
Un socket agit comme une extrémité d’une communication entre deux processus, l’un sur une
machine et l’autre sur une autre machine (ou parfois la même). La communication peut se faire sur
un réseau local ou un réseau étendu comme Internet. Chaque socket est défini par deux éléments
principaux :
 Adresse IP : L'identifiant unique de la machine sur le réseau.

 Port : Un numéro spécifique qui identifie une application ou un service sur cette machine.
En utilisant un socket, une application peut envoyer ou recevoir des données sur le réseau. Ces
données peuvent être des fichiers, des messages, des requêtes ou des réponses d'API, etc.
1.4. Sockets vs Datagramme :
Il existe principalement deux types de sockets :
Sockets orientés connexion (TCP) :
Utilisés pour établir une connexion fiable entre deux machines. Le protocole TCP assure que les
données arrivent dans le bon ordre et sans perte.
o Exemple : Une requête HTTP pour charger une page web utilise un socket TCP entre le
navigateur et le serveur.
 Caractéristiques du socket TCP :
 Connexion orientée : TCP établit une connexion formelle entre le client et le serveur avant
d'envoyer des données. Ce processus est appelé le handshake en trois étapes :
 Le client envoie une requête de connexion (SYN) au serveur.
 Le serveur répond avec une acceptation (SYN-ACK).
 Le client envoie une confirmation (ACK) pour finaliser la connexion.
 Fiabilité : TCP garantit que toutes les données envoyées par le client arrivent bien au
serveur, dans le bon ordre et sans duplication. Si un paquet est perdu, TCP le retransmet.
TITRE DU RAPPORT 5
 Contrôle de flux et de congestion : TCP régule le débit de transmission de données en
fonction des capacités du réseau pour éviter de le surcharger.
 Segmentation et réassemblage : Si les données envoyées sont trop volumineuses pour
être envoyées en un seul paquet, TCP les segmente en paquets plus petits. Il s'assure ensuite
que tous les paquets sont réassemblés dans le bon ordre à destination.
 Communication fiable : Une fois la connexion établie, les deux côtés (client et serveur)
peuvent envoyer et recevoir des données de manière bidirectionnelle et synchronisée. Si une
donnée n'arrive pas à destination, TCP le détecte et la retransmet.
 Exemple d'utilisation de TCP :
 HTTP/HTTPS : Pour le chargement des pages web.
 FTP (File Transfer Protocol) : Pour le transfert de fichiers.
 Email (SMTP/IMAP) : Pour envoyer ou recevoir des emails.

Sockets sans connexion (UDP) :


Utilisés pour envoyer des paquets de données sans établir une connexion formelle. UDP est rapide
mais ne garantit pas que les paquets arriveront ou seront dans le bon ordre.
 Caractéristiques du datagramme UDP :

 Sans connexion : Contrairement à TCP, UDP n’établit pas de connexion formelle entre le
client et le serveur. Il envoie simplement des datagrammes (paquets de données) sans
vérifier si le destinataire est prêt à les recevoir ou même si les paquets arrivent à destination.
 Pas de fiabilité garantie : UDP n’assure pas que les données arriveront à destination ni
qu’elles arriveront dans le bon ordre. Si des paquets sont perdus, ils ne seront pas retransmis.
 Pas de segmentation ou réassemblage : Si un datagramme est trop volumineux pour
être envoyé, il peut être perdu. Contrairement à TCP, UDP ne segmente pas les données
automatiquement.
 Faible latence : UDP est plus rapide que TCP car il n’y a pas de phase de connexion et de
contrôle de flux. Cela en fait un bon choix pour les applications qui privilégient la rapidité
plutôt que la fiabilité.
 Léger : UDP a moins de surcharge que TCP, ce qui en fait un protocole idéal pour des
communications rapides et courtes.
 Exemple d'utilisation de UDP :
 Streaming vidéo ou audio : Comme les vidéos ou les appels en ligne (VoIP), où une légère
perte de paquets n'a pas un impact critique sur la qualité globale.
 Jeux en ligne : Les jeux vidéo multijoueurs utilisent souvent UDP pour les mises à jour de
position des joueurs, car la rapidité prime sur la fiabilité.
Exemple : Le streaming vidéo ou les jeux en ligne peuvent utiliser des sockets UDP pour réduire la
latence.
3. Rôle des sockets dans une communication réseau
Les sockets sont responsables de l'envoi et de la réception des paquets de données entre deux
machines. Voici comment cela fonctionne généralement :
1. Le serveur crée un socket pour écouter les connexions entrantes sur un port spécifique. Il
attend qu'un client s'y connecte.

TITRE DU RAPPORT 6
2. Le client crée un socket et essaie de se connecter au serveur en utilisant son adresse IP et
le port d'écoute.
3. Une fois la connexion établie (pour les sockets TCP), le client et le serveur peuvent
échanger des données.
4. Les sockets UDP, quant à eux, envoient les données sans étape de connexion formelle, mais
le client et le serveur doivent toujours spécifier les adresses et ports respectifs.
4. Structure d’un socket
Chaque socket possède une paire d’identifiants :
 L’adresse IP de la machine qui exécute le programme (client ou serveur).

 Le numéro de port qui identifie le service ou l'application spécifique (par exemple, le


serveur HTTP utilise souvent le port 80).
Quand une machine veut communiquer avec une autre, le socket s'occupe de créer une "route" entre
ces deux points en fonction de l'adresse IP et du port. Une fois cette "route" établie, les données
peuvent circuler entre les deux machines.
1.5. Étapes de création et de gestion d'un socket (en
Java)
En Java, le processus pour établir une communication via des sockets se déroule en plusieurs étapes
:
1. Côté serveur :
o Créer un socket serveur qui écoute un certain port.
o Attendre les connexions entrantes.
o Créer un socket pour chaque connexion cliente.
2. Côté client :
o Créer un socket client en spécifiant l'adresse IP du serveur et le port d'écoute.
o Envoyer et recevoir des données.

Exemple simple de communication client-serveur


avec des sockets TCP :
1. Serveur TCP en Java :

import [Link].*;
import [Link].*;

public class ServeurTCP {


public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
[Link]("Serveur en attente de connexions...");
Socket clientSocket = [Link](); // Accepter la connexion du client
[Link]("Connexion acceptée de " + [Link]());

// Lire le message du client


BufferedReader in = new BufferedReader(new
InputStreamReader([Link]()));
String message = [Link]();

TITRE DU RAPPORT 7
[Link]("Message reçu du client : " + message);

// Répondre au client
PrintWriter out = new PrintWriter([Link](), true);
[Link]("Message bien reçu : " + message);

[Link](); // Fermer la connexion


} catch (IOException e) {
[Link]();
}
}
}
2. Client TCP en Java :

import [Link].*;
import [Link].*;

public class ClientTCP {


public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8080)) {
// Envoyer un message au serveur
PrintWriter out = new PrintWriter([Link](), true);
[Link]("Bonjour, serveur !");

// Lire la réponse du serveur


BufferedReader in = new BufferedReader(new
InputStreamReader([Link]()));
String response = [Link]();
[Link]("Réponse du serveur : " + response);
} catch (IOException e) {
[Link]();
}
}
}

Explication :
 Le serveur crée un objet ServerSocket qui écoute sur le port 8080. Il attend les connexions
entrantes à l'aide de la méthode accept().
 Le client se connecte au serveur en créant un Socket pointant sur localhost (c’est-à-dire la
machine locale) et le port 8080.
 Le client envoie un message au serveur via un flux de sortie (OutputStream), et le serveur le
lit à partir d’un flux d’entrée (InputStream).
 Le serveur renvoie une réponse au client, qui l'affiche.

Exemple simple de communication client-serveur


avec des sockets UDP :
Voici un exemple simple d'une communication client-serveur utilisant des sockets UDP en Java,
suivi d'une explication.
1. Côté Serveur :
Le serveur attend de recevoir des messages d'un client. Lorsqu'il reçoit un message, il renvoie une
réponse au client.

TITRE DU RAPPORT 8
Serveur UDP en Java

public class UDPServer {


public static void main(String[] args) throws Exception {
// Création du socket UDP, écoute sur le port 9876
DatagramSocket serverSocket = new DatagramSocket(9876);

// Buffer pour stocker les données reçues


byte[] receiveData = new byte[1024];
byte[] sendData;

[Link]("Serveur en attente de messages...");

while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, [Link]);
[Link](receivePacket); // Le serveur attend un paquet

String receivedMessage = new String([Link](), 0, [Link]());


[Link]("Message reçu: " + receivedMessage);

// Réponse à envoyer au client


String responseMessage = "Message reçu: " + receivedMessage;
sendData = [Link]();

// Récupération de l'adresse et du port du client


int clientPort = [Link]();
[Link] clientAddress = [Link]();

// Création et envoi du datagramme de réponse


DatagramPacket sendPacket = new DatagramPacket(sendData, [Link],
clientAddress, clientPort);
[Link](sendPacket); // Envoi de la réponse au client
}
}
}
2. Côté Client :
Le client envoie un message au serveur et attend de recevoir une réponse.
Client UDP en Java
import [Link];
import [Link];
import [Link];

public class UDPClient {


public static void main(String[] args) throws Exception {
// Création du socket UDP pour le client
DatagramSocket clientSocket = new DatagramSocket();

// Adresse IP du serveur
InetAddress serverAddress = [Link]("localhost");

// Message à envoyer au serveur


String message = "Bonjour, serveur!";
byte[] sendData = [Link]();

TITRE DU RAPPORT 9
// Création du datagramme à envoyer au serveur
DatagramPacket sendPacket = new DatagramPacket(sendData, [Link],
serverAddress, 9876);
[Link](sendPacket); // Envoi du datagramme au serveur

// Buffer pour la réponse du serveur


byte[] receiveData = new byte[1024];

// Réception du datagramme de réponse du serveur


DatagramPacket receivePacket = new DatagramPacket(receiveData, [Link]);
[Link](receivePacket); // Attente de la réponse du serveur

// Affichage du message reçu


String receivedMessage = new String([Link](), 0, [Link]());
[Link]("Réponse du serveur: " + receivedMessage);

// Fermeture du socket du client


[Link]();
}
}
Serveur UDP :
1. Création du socket (DatagramSocket) :
o Le serveur crée un socket UDP sur le port 9876 avec new DatagramSocket(9876). Ce
port sera utilisé pour recevoir les datagrammes du client.
2. Attente de datagramme :
o Le serveur crée un DatagramPacket vide pour recevoir les données. Il utilise ensuite la
méthode receive() pour attendre la réception d'un datagramme. Cette opération est
bloquante, c'est-à-dire que le serveur reste en attente tant qu'aucun datagramme n'a
été reçu.
3. Traitement du message :
o Une fois le datagramme reçu, le serveur extrait les données sous forme de chaîne de
caractères (String receivedMessage).
4. Envoi de la réponse :
o Le serveur crée un nouveau datagramme contenant la réponse. Il récupère l'adresse IP
et le port du client (inclus dans le datagramme reçu), et envoie la réponse à l'aide de la
méthode send().
Client UDP :
1. Création du socket (DatagramSocket) :
o Le client crée également un socket UDP, mais ici sans spécifier de port particulier. Cela
signifie que le client utilisera un port dynamique attribué automatiquement par le
système d'exploitation.
2. Envoi d'un message :
o Le client prépare le message à envoyer sous forme de tableau d'octets (byte array) et
crée un datagramme (DatagramPacket) contenant ces données, l'adresse IP du serveur
et le port (9876). Ensuite, le datagramme est envoyé au serveur avec send().
3. Réception de la réponse :
TITRE DU RAPPORT 10
o Après l'envoi, le client attend une réponse du serveur en appelant la méthode receive()
sur un datagramme vide. Cette méthode est également bloquante, ce qui signifie que
le client attend jusqu'à ce qu'une réponse soit reçue.
4. Affichage de la réponse :
o Une fois le datagramme reçu, le client extrait le message du serveur et l'affiche.

1.6. Communication entre deux Applications à travers la


Stack TCP/IP
Dans toute communication réseau, les données passent par plusieurs couches avant d'atteindre leur
destination. Le modèle TCP/IP est utilisé pour expliquer le processus de communication entre deux
applications sur un réseau. Ce modèle est divisé en quatre couches principales :
 Couche Application : Gère les protocoles de haut niveau comme HTTP, FTP, ou SMTP.
Cette couche est responsable des interactions entre les applications. C’est ici que vous codez
vos services et vos applications.
 Couche Transport : Fournit des services de communication directe entre deux processus
d'application, utilisant des protocoles comme TCP (fiable) ou UDP (rapide). Cette couche gère
la transmission des données, leur division en segments, et leur reconstitution à l’arrivée.
 Couche Réseau : Cette couche est responsable du routage des paquets d'une machine à une
autre via des adresses IP.
 Couche Liaison : Assure la communication entre les différents matériels du réseau, comme
les routeurs ou les switches.
Exemple pratique :
 Client : Une application Java envoie une requête HTTP via la couche Application.

 Transport : La requête est encapsulée dans un segment TCP et envoyée.


 Réseau : Le segment est transmis sous forme de paquets IP, routés vers la machine distante.
 Liaison : Les paquets passent par les différents matériels réseau pour atteindre leur
destination.

TITRE DU RAPPORT 11
2. Multithreading en
Java
2.1. Introduction aux Threads
Un thread est l'unité de base d'exécution dans un programme. Dans une application multithread,
plusieurs tâches peuvent s'exécuter en parallèle, ce qui permet d'améliorer l'efficacité et la réactivité
de l'application. En Java, la gestion des threads est une fonctionnalité native fournie par la classe
Thread et l'interface Runnable.
Le multithreading est particulièrement utile en programmation réseau, car il permet à un serveur
de gérer plusieurs connexions simultanées sans bloquer l'exécution. Par exemple, un serveur
multithread peut accepter une connexion de client, tout en continuant à écouter d'autres connexions
et à traiter des requêtes en parallèle.
2.2. Concepts Clés du Multithreading
Avant de plonger dans la programmation des threads en Java, il est important de comprendre
quelques concepts fondamentaux :
 Thread Principal : Chaque application Java a un thread principal (le thread qui exécute la
méthode main). Tous les threads supplémentaires créés par le programme sont des sous-
threads du thread principal.
 Cycle de Vie d’un Thread : Un thread passe par plusieurs états pendant son cycle de vie :
o New : Le thread est créé mais pas encore démarré.
o Runnable : Le thread est prêt à être exécuté par le planificateur.
o Running : Le thread est actuellement en cours d'exécution.
o Blocked/Waiting : Le thread est temporairement en attente d'une ressource ou d'un
signal.
o Terminated : Le thread a terminé son exécution.
 Concurrence : Le multithreading permet l'exécution simultanée de plusieurs threads, mais
il est important de synchroniser l'accès aux ressources partagées pour éviter les problèmes de
concurrence, tels que les conditions de course et les blocages.

2.3. Créer et Démarrer un Thread en Java


Il existe plusieurs façons de créer et démarrer un thread en Java. Les deux méthodes les plus
courantes sont l'utilisation de la classe Thread et de l'interface Runnable.
2.3.1. Utiliser la classe Thread
L'approche la plus simple pour créer un thread est d'étendre la classe Thread et de redéfinir la
méthode run().
class MonThread extends Thread {
@Override
public void run() {
[Link]("Le thread " + [Link]().getName() + " est en cours
d'exécution");
}

TITRE DU RAPPORT 12
}

public class Main {


public static void main(String[] args) {
MonThread thread1 = new MonThread();
[Link](); // Lance le thread
}
}

 Ici, la méthode run() contient le code que vous souhaitez exécuter dans un thread séparé.
Pour démarrer le thread, on appelle la méthode start(), qui démarre l'exécution en parallèle
avec le thread principal.

2.3.2. Utiliser l’interface Runnable


L'autre approche courante consiste à implémenter l'interface Runnable. Cette méthode est souvent
privilégiée, car elle permet à une classe d'hériter d'une autre classe tout en utilisant les
fonctionnalités multithread.
Exemple :
class MonRunnable implements Runnable {
@Override
public void run() {
[Link]("Le thread " + [Link]().getName() + " est en cours
d'exécution");
}
}

public class Main {


public static void main(String[] args) {
Thread thread = new Thread(new MonRunnable());
[Link]();
}
}
Dans ce cas, l'objet Runnable est passé au constructeur de la classe Thread, qui prend ensuite en
charge la gestion du thread.
2.3.3. Utiliser ExecutorService
Une approche plus avancée pour gérer les threads en Java est d'utiliser l'API ExecutorService.
Cette API permet de gérer un pool de threads, d'exécuter des tâches de façon asynchrone, et de
simplifier la gestion des threads.
Exemple :
import [Link];
import [Link];

public class Main {


public static void main(String[] args) {
ExecutorService executor = [Link](3);

for (int i = 0; i < 5; i++) {


[Link](() -> [Link]("Le thread " + [Link]().getName()
+ " est en cours d'exécution"));
}

[Link](); // Terminer l'exécution des threads


}
TITRE DU RAPPORT 13
}

 Ici, ExecutorService gère automatiquement les threads. On définit un pool de threads, ce


qui permet d'exécuter plusieurs tâches en parallèle tout en contrôlant le nombre maximum de
threads.

2.4. Synchronisation et Ressources Partagées


Dans une application multithread, plusieurs threads peuvent tenter d'accéder aux mêmes
ressources, ce qui peut entraîner des erreurs si l'accès n'est pas correctement synchronisé.
Exemple de problème : Si deux threads modifient une même variable en même temps, le résultat
peut être inattendu, en raison d'un condition de course.
Pour éviter ce problème, Java propose des mécanismes de synchronisation qui permettent de
contrôler l'accès aux ressources partagées.
2.4.1. Utiliser synchronized
L'un des moyens les plus simples de synchroniser l'accès à une méthode est d'utiliser le mot-clé
synchronized, qui permet d'autoriser l'accès à un seul thread à la fois.
Exemple :
class Compteur {
private int compteur = 0;

public synchronized void incrementer() {


compteur++;
[Link]("Valeur du compteur : " + compteur);
}
}

public class Main {


public static void main(String[] args) {
Compteur compteur = new Compteur();

Thread thread1 = new Thread(compteur::incrementer);


Thread thread2 = new Thread(compteur::incrementer);

[Link]();
[Link]();
}
}
Dans cet exemple, la méthode incrementer() est synchronisée. Cela garantit qu'un seul thread à la
fois peut modifier la variable compteur, évitant ainsi les conditions de course.
2.4.2. Bloc synchronized
En plus de synchroniser des méthodes entières, il est possible de synchroniser uniquement des blocs
de code spécifiques à l'intérieur d'une méthode.
Exemple :
class Banque {
private int solde = 1000;

public void retirerArgent(int montant) {


synchronized (this) {
if (solde >= montant) {
solde -= montant;
[Link]("Retrait de " + montant + " effectué. Solde restant : " + solde);
} else {
TITRE DU RAPPORT 14
[Link]("Solde insuffisant !");
}
}
}
}
Dans cet exemple, seule la section critique du code (l’accès et la modification de la variable solde) est
synchronisée. Cela permet d’améliorer la performance en ne bloquant pas toute la méthode.
2.5. Problèmes Communes avec le Multithreading
 Deadlock (Blocage Mutuel) : Se produit lorsque deux threads ou plus sont bloqués en
attendant que les autres libèrent une ressource. Si tous les threads attendent indéfiniment, ils
seront bloqués.
 Starvation : Un thread est continuellement empêché d'accéder à une ressource car d'autres
threads monopolisent cette ressource.
 Livelock : Les threads ne sont pas bloqués mais changent continuellement d'état sans faire
de progrès.

TITRE DU RAPPORT 15
3. Communication
Bloquante vs Non
Bloquante
La gestion des entrées/sorties (I/O) est un aspect crucial en programmation réseau, car les
opérations de communication peuvent impliquer des échanges de données à travers le réseau. Ces
opérations peuvent être longues et affecter la performance d'une application si elles ne sont pas bien
gérées. Il existe deux principaux modèles de gestion des I/O en réseau : la communication
bloquante et la communication non bloquante. Ces deux concepts influencent directement la
manière dont un programme traite les connexions réseau et la performance globale du système.
3.1. Communication Bloquante
La communication bloquante est un modèle où le thread qui effectue une opération de lecture
ou d'écriture sur une ressource (comme un socket ou un fichier) attend que cette opération se
termine avant de pouvoir continuer l'exécution du programme. En d'autres termes, le thread est
bloqué jusqu'à ce que l'opération réseau soit terminée, qu'il s'agisse de recevoir une réponse ou
d'envoyer des données.
3.1.1. Exemple en Java
Dans une communication bloquante, lorsqu'un thread tente de lire des données à partir d'un socket,
il est mis en pause jusqu'à ce que des données soient disponibles. Si aucune donnée n'est disponible,
le thread reste bloqué, et aucun autre traitement ne peut avoir lieu jusqu'à ce que la lecture soit
effectuée.
Voici un exemple de socket bloquant en Java :
import [Link];
import [Link];
import [Link];
import [Link];

public class ServeurBloquant {


public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(1234);
[Link]("Serveur en attente de connexion...");

Socket clientSocket = [Link](); // Bloque jusqu'à la connexion d'un client


[Link]("Connexion acceptée.");

BufferedReader input = new BufferedReader(new


InputStreamReader([Link]()));
String message = [Link](); // Bloque jusqu'à la réception d'un message
[Link]("Message reçu : " + message);

[Link]();
[Link]();
}
}
Dans cet exemple, le serveur est bloqué à deux endroits :
TITRE DU RAPPORT 16
1. Lors de l'appel à accept() qui attend qu'un client se connecte.
2. Lors de l'appel à readLine() qui attend qu'une ligne de texte soit reçue du client.
Bien que la communication bloquante soit simple à mettre en œuvre, elle peut entraîner des
inefficacités. Par exemple, si une application serveur doit gérer de multiples connexions, chaque
thread pourrait rester bloqué en attente de données, ce qui consomme des ressources inutilement.
Cela rend la gestion de nombreuses connexions simultanées plus complexe et moins efficace.
3.1.2. Limitations de la Communication Bloquante
 Ressources limitées : Chaque connexion nécessite un thread séparé, et chaque thread
bloque des ressources du système (CPU, mémoire).
 Réactivité réduite : Si une connexion reste inactive ou lente, le thread reste bloqué, ce qui
peut ralentir l'ensemble du serveur.
 Scalabilité difficile : Il devient difficile de maintenir des milliers de connexions
simultanées avec des threads bloquants, car la gestion des threads consomme beaucoup de
mémoire.

3.2. Communication Non Bloquante


Dans la communication non bloquante, un thread n'est pas obligé d'attendre la fin d'une
opération réseau. Il peut continuer à exécuter d'autres tâches pendant que l'opération est effectuée
en arrière-plan. Une fois les données disponibles ou l'opération terminée, le thread est notifié ou
l'application récupère les résultats.
Ce modèle permet de gérer plusieurs connexions avec un seul thread, ce qui le rend bien plus adapté
pour les applications nécessitant une haute scalabilité, comme les serveurs à grande échelle ou les
systèmes temps réel.
3.2.1. Exemple en Java avec NIO
Java propose l'API NIO (New I/O), qui permet de gérer les opérations d'I/O de manière non
bloquante à l'aide de canaux (Channel) et de sélecteurs (Selector). Voici un exemple d'utilisation de
NIO pour implémenter un serveur non bloquant :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class ServeurNonBloquant {


public static void main(String[] args) throws IOException {
// Création du Selector
Selector selector = [Link]();

// Création du channel du serveur et configuration non-bloquante


ServerSocketChannel serverChannel = [Link]();
[Link](new InetSocketAddress(1234));
[Link](false);

// Enregistrement du serveur avec le Selector


[Link](selector, SelectionKey.OP_ACCEPT);

[Link]("Serveur non-bloquant démarré...");


TITRE DU RAPPORT 17
while (true) {
// Sélection des clés prêtes
[Link]();

// Itération sur les clés prêtes


Iterator<SelectionKey> keys = [Link]().iterator();
while ([Link]()) {
SelectionKey key = [Link]();
[Link]();

if ([Link]()) {
// Acceptation de la connexion client
SocketChannel clientChannel = [Link]();
[Link](false);
[Link](selector, SelectionKey.OP_READ);
[Link]("Client connecté.");
}

if ([Link]()) {
// Lecture des données du client
SocketChannel clientChannel = (SocketChannel) [Link]();
ByteBuffer buffer = [Link](256);
int bytesRead = [Link](buffer);
if (bytesRead == -1) {
[Link]();
} else {
String message = new String([Link]()).trim();
[Link]("Message reçu : " + message);
}
}
}
}
}
}

Dans cet exemple :


 Le serveur est configuré pour fonctionner en mode non bloquant grâce à
configureBlocking(false).
 Un sélecteur est utilisé pour gérer plusieurs connexions avec un seul thread.
 Le serveur ne bloque pas lors de l'attente des connexions ou des messages ; il vérifie les
événements prêts à être traités via le sélecteur.

3.2.2. Avantages de la Communication Non Bloquante


 Scalabilité accrue : Un seul thread peut gérer plusieurs connexions simultanément, ce qui
réduit la consommation de ressources.
 Réactivité améliorée : Le thread principal n'est jamais bloqué en attente d'une opération
réseau, ce qui permet au programme de rester réactif, même avec de nombreuses connexions
lentes ou inactives.
 Meilleure gestion des ressources : Moins de threads sont nécessaires, ce qui réduit
l'empreinte mémoire et la surcharge de gestion des threads.

TITRE DU RAPPORT 18
3.2.3. Inconvénients
 Complexité accrue : La gestion non bloquante introduit une plus grande complexité,
notamment dans la gestion des événements asynchrones et des états.
 Programmation plus difficile : Le code devient plus difficile à écrire et à déboguer, en
particulier pour des cas d'utilisation complexes.

3.3. Comparaison Bloquant vs Non Bloquant


Caractéristiques Bloquant Non Bloquant
Le thread continue à exécuter d'autres
Modèle Chaque thread attend la fin d'une
tâches pendant que l'opération est en
d'exécution opération avant de continuer.
cours.
Nécessite un thread par Un thread peut gérer plusieurs
Ressources
connexion. connexions.
Moins efficace avec de
Efficace pour de nombreuses connexions
Performance nombreuses connexions
simultanées.
simultanées.
Plus complexe, notamment pour la
Complexité Simple à implémenter.
gestion des événements.
Serveurs simples avec peu de Applications à grande échelle, systèmes
Utilisation
connexions. réactifs.

3.4. Bloquante vs Non-bloquante :


 Communication Bloquante : Utilisée dans les systèmes où la simplicité prime, et où les
performances ne sont pas un facteur critique. Par exemple, des petits serveurs ou des
systèmes embarqués avec peu de connexions simultanées.
 Communication Non Bloquante : Idéale pour les serveurs haute-performance, les
applications web massivement concurrentes (ex : serveurs HTTP, services de messagerie,
plateformes de streaming) ou les systèmes temps réel, où la réactivité et la gestion de milliers
de connexions sont essentielles.

4. Topologies Réseau
La topologie réseau représente l'organisation ou la disposition physique ou logique des éléments
d'un réseau. En programmation réseau avec Java, les différentes topologies peuvent être mises en
œuvre à l'aide des sockets et des threads. Cette section présente les principales topologies réseau et
des exemples pratiques pour chacune, en utilisant Java pour simuler leur fonctionnement.
4.1. Topologie en Étoile (Star Topology)
Dans une topologie en étoile, tous les nœuds (clients) sont connectés à un nœud central (serveur). Le
nœud central gère et contrôle toutes les communications du réseau. Les clients ne communiquent
pas directement entre eux, mais passent par le serveur.
4.1.1. Exemple en Java (Serveur en Étoile)
Dans cet exemple, un serveur central accepte plusieurs connexions de clients et gère leurs
communications.
Code du Serveur :
import [Link].*;
TITRE DU RAPPORT 19
import [Link].*;
import [Link].*;

public class ServeurEtoile {


private static final int PORT = 1234;
private static ExecutorService pool = [Link](10);

public static void main(String[] args) throws IOException {


ServerSocket serverSocket = new ServerSocket(PORT);
[Link]("Serveur démarré sur le port " + PORT);

while (true) {
Socket clientSocket = [Link]();
[Link]("Client connecté.");
[Link](new GestionClient(clientSocket));
}
}
}

class GestionClient implements Runnable {


private Socket clientSocket;

public GestionClient(Socket socket) {


[Link] = socket;
}

@Override
public void run() {
try {
BufferedReader in = new BufferedReader(new
InputStreamReader([Link]()));
PrintWriter out = new PrintWriter([Link](), true);
String message;

while ((message = [Link]()) != null) {


[Link]("Message reçu : " + message);
[Link]("Message traité : " + [Link]());
}
} catch (IOException e) {
[Link]("Erreur de communication avec le client.");
} finally {
try {
[Link]();
} catch (IOException e) {
[Link]();
}
}
}
}
Dans ce cas, le serveur gère plusieurs clients à l'aide d'un pool de threads. Chaque client envoie des
messages au serveur, qui les traite et renvoie une réponse.
Code du Client :
import [Link].*;
import [Link].*;

TITRE DU RAPPORT 20
public class ClientEtoile {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 1234);
BufferedReader in = new BufferedReader(new InputStreamReader([Link]()));
PrintWriter out = new PrintWriter([Link](), true);

[Link]("Bonjour Serveur");
[Link]("Réponse du serveur : " + [Link]());

[Link]();
}
}
Dans cet exemple, chaque client envoie un message au serveur qui est traité individuellement.
4.1.2. Avantages et Inconvénients
 Avantages :
o Facile à implémenter et à gérer.
o Le serveur central peut contrôler tout le trafic.
o Si un client tombe en panne, le reste du réseau continue de fonctionner.
 Inconvénients :
o Si le serveur central tombe en panne, tout le réseau est affecté.
o Peut être surchargé si trop de clients se connectent simultanément.

4.2. Topologie en Anneau (Ring Topology)


Dans une topologie en anneau, chaque nœud est connecté à deux autres nœuds, formant un cercle.
Les données circulent dans une direction à travers chaque nœud, jusqu'à atteindre la destination.
4.2.1. Exemple en Java (Topologie en Anneau)
Simuler une topologie en anneau avec des sockets peut être un peu plus complexe, car il faut gérer la
circulation des messages entre les différents nœuds.
Exemple simplifié :
Chaque client se connecte au suivant dans l'anneau, et le dernier client renvoie l'information au
premier, formant un cercle logique.
import [Link].*;
import [Link].*;

public class NoeudAnneau {


private static final String NEXT_NODE_HOST = "localhost";
private static final int NEXT_NODE_PORT = 1235;

public static void main(String[] args) throws IOException {


// Serveur pour recevoir des messages du nœud précédent
ServerSocket serverSocket = new ServerSocket(1234);
Socket socket = [Link]();
BufferedReader in = new BufferedReader(new InputStreamReader([Link]()));

String message = [Link]();


[Link]("Message reçu : " + message);

// Client pour envoyer le message au nœud suivant


Socket nextNodeSocket = new Socket(NEXT_NODE_HOST, NEXT_NODE_PORT);
TITRE DU RAPPORT 21
PrintWriter out = new PrintWriter([Link](), true);
[Link](message + " - Reçu par le nœud suivant.");

[Link]();
[Link]();
[Link]();
}
}
Chaque nœud reçoit un message du précédent, le modifie et le renvoie au suivant.
4.2.2. Avantages et Inconvénients
 Avantages :
o Aucune dépendance sur un nœud central.
o Répartition égale de la charge entre les nœuds.
 Inconvénients :
o Si un nœud tombe en panne, l'ensemble de l'anneau est affecté.
o Moins flexible, car il faut gérer manuellement la connexion entre chaque nœud.

4.3. Topologie en Bus (Bus Topology)


Dans la topologie en bus, tous les nœuds sont connectés à un même câble principal (le bus). Tous les
nœuds peuvent communiquer entre eux via ce câble unique. L'inconvénient est que seul un nœud à
la fois peut envoyer des données sur le bus.
4.3.1. Exemple en Java
Simuler une topologie en bus peut se faire en ayant un serveur qui diffuse les messages reçus à tous
les clients connectés.
import [Link].*;
import [Link].*;
import [Link].*;

public class ServeurBus {


private static final int PORT = 1234;
private static List<Socket> clients = new ArrayList<>();

public static void main(String[] args) throws IOException {


ServerSocket serverSocket = new ServerSocket(PORT);
[Link]("Serveur en bus démarré...");

while (true) {
Socket clientSocket = [Link]();
[Link](clientSocket);
new Thread(new GestionClientBus(clientSocket)).start();
}
}

static class GestionClientBus implements Runnable {


private Socket clientSocket;

public GestionClientBus(Socket socket) {


[Link] = socket;
}

TITRE DU RAPPORT 22
@Override
public void run() {
try {
BufferedReader in = new BufferedReader(new
InputStreamReader([Link]()));
String message;

while ((message = [Link]()) != null) {


[Link]("Message reçu : " + message);
envoyerAuxAutres(message);
}
} catch (IOException e) {
[Link]();
}
}

private void envoyerAuxAutres(String message) throws IOException {


for (Socket socket : clients) {
if (socket != clientSocket) {
PrintWriter out = new PrintWriter([Link](), true);
[Link]("Message du bus : " + message);
}
}
}
}
}
Dans ce code, tous les clients reçoivent les messages diffusés sur le bus (le serveur).
4.3.2. Avantages et Inconvénients
 Avantages :
o Facile à mettre en place pour de petits réseaux.
o Économique en termes de câblage (ou de connexions réseau).
 Inconvénients :
o Risque de collision de données.
o Si le câble central tombe en panne, tout le réseau est hors service.

4.4. Topologie Maillée (Mesh Topology)


Dans une topologie maillée, chaque nœud est directement connecté à tous les autres nœuds. Cela
garantit une redondance maximale, mais au prix d'une complexité accrue en termes de gestion des
connexions.
4.4.1. Exemple en Java
Pour une topologie maillée, chaque client maintient une connexion avec tous les autres clients du
réseau. Il envoie et reçoit des messages de tous les autres clients.
import [Link].*;
import [Link].*;
import [Link].*;

public class ClientMaillage {


private static final String[] PEER_HOSTS = {"localhost", "localhost"};

TITRE DU RAPPORT 23
private static final int[] PEER_PORTS = {1235, 1236};

public static void main(String[] args) throws IOException {


ExecutorService pool = [Link](PEER_HOSTS.length);

// Connexion avec chaque pair


for (int i = 0; i < PEER_HOSTS.length; i++) {
int index = i;
[Link](() -> {
try {
Socket socket = new Socket(PEER_HOSTS[index], PEER_PORTS[index]);
PrintWriter out = new PrintWriter([Link](), true);
BufferedReader in = new BufferedReader(new
InputStreamReader([Link]()));

[Link]("Message du client vers le pair " + index);


[Link]("Réponse du pair " + index + " : " + [Link]());

[Link]();
} catch (IOException e) {
[Link]();
}
});
}

[Link]();
}
}

TITRE DU RAPPORT 24
5. Introduction aux
Brokers et Systèmes de
Messagerie
Un broker de messages agit comme un intermédiaire entre les producteurs (qui envoient les
messages) et les consommateurs (qui reçoivent les messages). Il permet de découpler les
composants du système, les rendant plus flexibles, scalables et tolérants aux pannes.
5.1. Rôle du Broker de Messages
Le broker de messages gère plusieurs aspects clés du processus de messagerie :
1. Découplage des Composants : Le broker sépare les producteurs des consommateurs, de
sorte que ces derniers n'ont pas besoin d'être disponibles au même moment que les
producteurs. Cela favorise la souplesse et l'évolutivité dans les architectures distribuées.
2. Fiabilité : Le broker garantit que les messages sont livrés même si un consommateur est
temporairement indisponible. Il stocke les messages jusqu'à ce qu'ils soient consommés.
3. Scalabilité : En agissant comme un intermédiaire, le broker permet d'ajouter de nouveaux
consommateurs ou producteurs à la volée, sans interrompre la communication existante.
4. Tolérance aux pannes : Les systèmes de messagerie permettent d'ajouter une couche de
résilience. Par exemple, si un consommateur tombe en panne, les messages ne sont pas
perdus, car ils sont stockés temporairement par le broker.
5. Transmission Asynchrone : Les producteurs peuvent envoyer des messages à tout
moment sans attendre la réponse immédiate des consommateurs. Cela est particulièrement
utile dans des environnements où les composants ne peuvent pas toujours communiquer en
temps réel.

5.2. Mécanismes de Communication


Il existe deux principaux modèles de communication dans les systèmes de messagerie :
 File d'attente (Queue-based messaging) : Dans ce modèle, les messages sont placés
dans une file d'attente et consommés par un ou plusieurs consommateurs. Chaque message
est consommé une seule fois. Ce modèle est utile pour des tâches de traitement de données en
série ou des jobs de longue durée.
 Publication/Abonnement (Publish/Subscribe) : Ce modèle permet à un producteur
(publisher) d'envoyer un message à plusieurs consommateurs (subscribers). Les
consommateurs intéressés s'abonnent à un sujet spécifique, et chaque message publié sur ce
sujet est reçu par tous les abonnés.

5.3. Exemples d'Utilisation des Brokers


Les brokers et les systèmes de messagerie sont utilisés dans une grande variété de cas d'usage :
 Traitement de transactions bancaires : Les systèmes bancaires utilisent des brokers
pour gérer la validation, la transaction et la gestion d'erreurs de manière fiable entre plusieurs
services.

TITRE DU RAPPORT 25
 Traitement des commandes e-commerce : Un broker permet de gérer les différentes
étapes d'une commande, comme la vérification de la disponibilité des produits, la gestion du
paiement, et l'envoi d'alertes de confirmation au client.
 Systèmes de surveillance (Monitoring) : Dans les architectures distribuées, les
messages envoyés par des capteurs peuvent être collectés, filtrés, et distribués via un broker
de messagerie pour un traitement ou une analyse ultérieure.
 Applications IoT : Dans l'Internet des objets (IoT), des capteurs ou dispositifs envoient
continuellement des données vers un broker qui les distribue ensuite à différentes
applications de traitement ou de stockage.

5.4. Propriétés des Systèmes de Messagerie


Les brokers et systèmes de messagerie fournissent diverses propriétés importantes :
 Garanties de livraison : Selon les besoins de l'application, un broker peut offrir différentes
garanties de livraison :
o Au moins une fois : Un message peut être livré plusieurs fois, mais il ne sera jamais
perdu.
o Au plus une fois : Un message sera livré au maximum une fois, mais il peut être
perdu en cas de panne.
o Exactement une fois : Le message est livré une seule fois, ce qui est le scénario
idéal, mais il nécessite souvent des mécanismes de confirmation ou de déduplication
plus complexes.
 Persistance des messages : Certains systèmes de messagerie permettent de stocker les
messages sur disque pour s'assurer qu'ils ne sont pas perdus même en cas de panne du
broker. La persistance est essentielle pour garantir que les messages critiques ne sont pas
perdus.
 Priorisation des messages : Certains systèmes permettent de donner une priorité aux
messages, ce qui peut être utile pour garantir que les messages critiques soient traités avant
les autres.
 Filtres et Routage : Les brokers permettent souvent de filtrer les messages ou de les router
selon des règles définies. Par exemple, un message peut être dirigé vers un certain
consommateur en fonction de son contenu ou de ses attributs.

5.5. Exemple d'Implémentation d'un Broker Simple en


Java
Nous pouvons simuler un système de messagerie simple en utilisant des threads et des structures de
données comme des queues (files d'attente).
Voici un exemple simplifié de broker de messages en Java, où plusieurs producteurs envoient des
messages et plusieurs consommateurs les reçoivent à partir d'une file d'attente.
Exemple en Java :
import [Link];
import [Link];

public class SimpleMessageBroker {


private static BlockingQueue<String> queue = new LinkedBlockingQueue<>();

public static void main(String[] args) {

TITRE DU RAPPORT 26
// Démarrage des producteurs
new Thread(new Producer("Producteur 1")).start();
new Thread(new Producer("Producteur 2")).start();

// Démarrage des consommateurs


new Thread(new Consumer("Consommateur 1")).start();
new Thread(new Consumer("Consommateur 2")).start();
}

// Classe Producteur
static class Producer implements Runnable {
private String name;

public Producer(String name) {


[Link] = name;
}

@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
String message = name + " - Message " + i;
[Link](name + " envoie : " + message);
[Link](message);
[Link](1000); // Simulation d'un délai
}
} catch (InterruptedException e) {
[Link]();
}
}
}

// Classe Consommateur
static class Consumer implements Runnable {
private String name;

public Consumer(String name) {


[Link] = name;
}

@Override
public void run() {
try {
while (true) {
String message = [Link]();
[Link](name + " a reçu : " + message);
}
} catch (InterruptedException e) {
[Link]();
}
}
}
}
Dans cet exemple :
 Producteur : génère des messages et les place dans une file d'attente (queue).
TITRE DU RAPPORT 27
 Consommateur : récupère les messages de la file d'attente et les traite.
Cet exemple montre une architecture de base d'un système de messagerie, où les producteurs et
consommateurs ne sont pas directement liés, mais communiquent via une file d'attente partagée,
simulant le rôle d'un broker.
5.6. Conclusion
Les brokers et systèmes de messagerie sont des composants essentiels dans les architectures
distribuées modernes, assurant un découplage, une communication fiable, et une meilleure
scalabilité des systèmes. Leur utilisation est indispensable dans les environnements asynchrones, où
les composants doivent échanger des messages sans être directement connectés en temps réel. Ils
fournissent une flexibilité nécessaire pour gérer la communication à grande échelle et assurent la
résilience et la persistance des données à travers le réseau.
5.7. Notions de Topic, Queue et Exchange
Dans le cadre des systèmes de messagerie et des brokers, les concepts de Topic, Queue, et
Exchange sont essentiels pour comprendre comment les messages sont acheminés et distribués
entre les producteurs et les consommateurs. Chacun de ces concepts joue un rôle spécifique dans la
manière dont les messages sont organisés et livrés.
5.7.1. Queue
Une queue (file d'attente) est une structure de données dans laquelle les messages sont stockés
jusqu'à ce qu'ils soient consommés. Dans le modèle de messagerie basé sur les queues, un ou
plusieurs producteurs envoient des messages à une queue, et un ou plusieurs consommateurs les
récupèrent pour les traiter. Le principe fondamental des queues est généralement le FIFO (First In,
First Out), c'est-à-dire que les premiers messages envoyés sont les premiers à être consommés.
Exemple d'utilisation :
 Traitement de tâches en arrière-plan : Les messages peuvent représenter des tâches à
exécuter (comme le traitement d'une image ou d'une transaction), et les consommateurs les
exécutent un par un.
 Scalabilité : On peut ajouter plusieurs consommateurs pour répartir la charge de travail.
Une tâche sera attribuée à un seul consommateur.
Fonctionnement :
 Propriétés :

o Un message est consommé par un seul consommateur.


o La queue assure que chaque message sera traité au moins une fois (ou exactement une
fois selon la configuration).
 Illustration : Imaginons une application de traitement d'images :
o Le producteur envoie des messages contenant les détails de l'image à traiter dans une
queue.
o Les consommateurs récupèrent ces messages un par un pour appliquer les
transformations nécessaires (compression, redimensionnement, etc.).
// Exemple simplifié en Java :
[Link]("Image à traiter");
String message = [Link]();
[Link]("Traitement de " + message);
5.7.2. Topic
Un topic est un canal de communication dans le modèle publish/subscribe (pub/sub).
Contrairement aux queues où un message est consommé une seule fois, dans un topic, plusieurs

TITRE DU RAPPORT 28
consommateurs peuvent recevoir le même message. Le producteur publie des messages sur un topic,
et tous les consommateurs abonnés à ce topic recevront les messages.
Exemple d'utilisation :
 Notifications en temps réel : Imaginons une application de trading où les utilisateurs
souhaitent recevoir des mises à jour sur les actions en temps réel. Tous les utilisateurs
abonnés au topic recevront les informations sur les variations des cours boursiers.
Fonctionnement :
 Propriétés :

o Un message est envoyé à tous les abonnés du topic.


o Les consommateurs s’abonnent à un sujet particulier, et seuls ceux qui sont intéressés
par ce sujet recevront les messages.
 Illustration : Imaginons une plateforme de messagerie instantanée où les utilisateurs
s’abonnent à des canaux de discussion. Si un utilisateur envoie un message sur un topic
(correspondant à un canal de discussion), tous les utilisateurs abonnés à ce canal recevront le
message.
// Exemple simplifié :
[Link]("Message dans le canal général");
String message = [Link]("Utilisateur A");
[Link]("Utilisateur A a reçu : " + message);
5.7.3. Exchange
Un exchange est un concept utilisé principalement dans des systèmes comme RabbitMQ ou AMQP
(Advanced Message Queuing Protocol). Il sert d'intermédiaire entre les producteurs et les queues.
Les producteurs n'envoient pas directement des messages aux queues, mais plutôt à un exchange.
L'exchange a la responsabilité de router ces messages vers la bonne queue en fonction de règles
définies (appelées bindings).
Types d'Exchanges :
1. Direct Exchange : L’exchange envoie les messages à des queues spécifiques en fonction
d'une clé de routage exacte (routing key). Ce type est utile lorsque l'on souhaite un
acheminement précis des messages.
2. Fanout Exchange : L’exchange diffuse les messages à toutes les queues qui y sont liées,
sans se soucier de la clé de routage. Ce type est utile pour les notifications en diffusion
générale.
3. Topic Exchange : Ce type d'exchange permet d'envoyer des messages à des queues selon un
modèle de correspondance basé sur des mots-clés. Il est particulièrement utile dans des
scénarios complexes où il faut router des messages à plusieurs destinataires.
Exemple d'utilisation :
 Routage de messages : Imaginons une plateforme de logistique où différents services
(livraison, entrepôt, service client) doivent traiter différents types de messages (suivi des
colis, gestion des stocks, réclamations). Un exchange permettrait d'acheminer les messages
vers les services appropriés en fonction de leur type.
Fonctionnement :
 Propriétés :

o Les exchanges permettent un routage flexible des messages.


o Ils décident, selon des règles de correspondance, quelle queue doit recevoir le message
produit.

TITRE DU RAPPORT 29
 Illustration : Imaginons un système où les utilisateurs envoient des messages de différents
types (commandes, factures, notifications) vers un exchange. L'exchange les redirige ensuite
vers des queues appropriées en fonction du type de message.
// Exemple simplifié :
[Link]("Commande client", "queue_commande");
[Link]("Notification", "queue_notification");
String message = [Link](); // Le consommateur récupère les messages dans la queue associée.
[Link]("Traitement de " + message);
5.7.4. Comparaison entre Queue, Topic, et Exchange
Concept Modèle Propriétés Cas d'utilisation
Un message est consommé une Traitement de tâches,
Queue Point-à-point
seule fois, FIFO équilibrage de charge
Un message est diffusé à Notifications, diffusion de
Topic Publication/Abonnement
plusieurs abonnés mises à jour en temps réel
Route les messages selon des
Routage intelligent,
Exchange Routage de messages règles vers des queues
intégration de services
spécifiques

6. Réimplantation des
Protocoles de Couches
d'Application en
Sockets et Threads
Cette section aborde la réimplantation des principaux protocoles de communication utilisés dans les
applications modernes en s'appuyant sur les sockets et threads en Java. Cela permet de mieux
comprendre le fonctionnement sous-jacent des protocoles de la couche d'application en les
construisant à partir de zéro. Nous allons couvrir plusieurs protocoles comme REST, MQTT,
AMQP, CoAP, XMPP, Kafka, et STOMP, en expliquant leur structure et en proposant des
exemples d'implémentation.
6.1. Principes Fondamentaux des Protocoles de Couches
d'Application
Les protocoles de la couche d'application définissent les règles pour échanger des données entre des
applications via des réseaux. Ces protocoles se trouvent au sommet du modèle TCP/IP ou OSI et
facilitent la communication entre les processus au travers de différentes technologies.
En Java, la réimplantation de ces protocoles utilise les sockets pour établir des connexions, et les
threads pour gérer la concurrence et les requêtes simultanées.

TITRE DU RAPPORT 30
6.2. Réimplantation du Protocole REST avec Sockets et
Threads
REST (Representational State Transfer) est un protocole très utilisé pour les API web, basé
sur le protocole HTTP. Il repose sur une architecture stateless où les interactions entre client et
serveur se font via des requêtes HTTP.
Exemple d’implémentation REST :
L'idée est de créer un serveur socket qui écoutera sur un port et traitera les requêtes HTTP REST
envoyées par un client, répondant avec les données appropriées selon le verbe HTTP (GET, POST,
etc.).
// Serveur basique REST en Java
ServerSocket server = new ServerSocket(8080);
while (true) {
Socket client = [Link]();
new Thread(() -> handleRequest(client)).start();
}

public void handleRequest(Socket client) {


try (BufferedReader in = new BufferedReader(new InputStreamReader([Link]()));
PrintWriter out = new PrintWriter([Link](), true)) {

String requestLine = [Link](); // Lire la première ligne de la requête HTTP


if ([Link]("GET")) {
[Link]("HTTP/1.1 200 OK");
[Link]("Content-Type: application/json");
[Link]();
[Link]("{ \"message\": \"Hello from REST server\" }");
} else {
[Link]("HTTP/1.1 400 Bad Request");
}
} catch (IOException e) {
[Link]();
}
}
Dans cet exemple, chaque nouvelle requête est traitée par un thread distinct pour assurer la gestion
simultanée des connexions.
6.3. Réimplantation du Protocole MQTT avec Sockets et
Threads
MQTT (Message Queuing Telemetry Transport) est un protocole léger conçu pour les
communications dans les environnements à bande passante limitée, notamment pour les IoT
(Internet of Things).
Dans une réimplantation basique, le serveur MQTT acceptera les connexions des clients via des
sockets, et les messages seront publiés sur des topics. Les threads géreront la concurrence, assurant
que chaque client puisse publier et souscrire aux topics en parallèle.
Exemple d’implémentation MQTT simplifiée :
// Server socket qui accepte les connexions clients pour MQTT
ServerSocket server = new ServerSocket(1883); // Port par défaut MQTT
Map<String, List<Socket>> topicSubscribers = new HashMap<>();

while (true) {
Socket client = [Link]();
new Thread(() -> handleMQTTClient(client, topicSubscribers)).start();
}
TITRE DU RAPPORT 31
public void handleMQTTClient(Socket client, Map<String, List<Socket>> topicSubscribers) {
// Gérer la publication/souscription et la diffusion des messages aux clients abonnés
// Ici, chaque message publié est relayé aux abonnés du topic correspondant.
}
6.4. Réimplantation du Protocole AMQP
AMQP (Advanced Message Queuing Protocol) est un protocole pour la gestion de la
messagerie orientée file d'attente, souvent utilisé dans des contextes industriels avec RabbitMQ.
AMQP utilise des concepts comme les exchanges et queues pour router les messages.
Dans une réimplantation, un serveur socket agira comme un broker AMQP en permettant aux
clients de publier des messages dans des queues. Un thread distinct sera attribué à chaque client
pour gérer les connexions simultanées.
// Réimplantation AMQP simplifiée en Java
ServerSocket amqpServer = new ServerSocket(5672); // Port par défaut AMQP
Map<String, Queue<String>> queues = new HashMap<>();

while (true) {
Socket client = [Link]();
new Thread(() -> handleAMQPClient(client, queues)).start();
}

public void handleAMQPClient(Socket client, Map<String, Queue<String>> queues) {


// Gérer la publication des messages dans les queues, et l'extraction par les consommateurs
}

6.5. Réimplantation du Protocole CoAP


CoAP (Constrained Application Protocol) est un protocole léger utilisé pour les appareils
contraints dans les environnements IoT, similaire à HTTP mais conçu pour être plus économe en
ressources. CoAP est basé sur le protocole UDP, contrairement à HTTP qui repose sur TCP.
Exemple d’implémentation CoAP simplifiée :
// Serveur CoAP avec UDP
DatagramSocket coapServer = new DatagramSocket(5683); // Port par défaut CoAP

byte[] receiveBuffer = new byte[1024];


DatagramPacket packet = new DatagramPacket(receiveBuffer, [Link]);

while (true) {
[Link](packet); // Recevoir la requête CoAP
new Thread(() -> handleCoAPRequest(packet)).start();
}

public void handleCoAPRequest(DatagramPacket packet) {


// Gérer la requête CoAP et envoyer une réponse au client
}
6.6. Réimplantation du Protocole XMPP
XMPP (Extensible Messaging and Presence Protocol) est un protocole utilisé pour la
messagerie instantanée et la gestion de la présence. Il est largement utilisé dans des applications
comme Jabber et des services de messagerie.
Un serveur socket réimplémentant XMPP pourrait gérer des clients se connectant pour envoyer des
messages instantanés. Les messages seront encapsulés dans des structures XML, et chaque client
pourra envoyer des messages à d'autres utilisateurs via le serveur.
// Réimplantation XMPP simplifiée en Java

TITRE DU RAPPORT 32
ServerSocket xmppServer = new ServerSocket(5222); // Port par défaut XMPP

while (true) {
Socket client = [Link]();
new Thread(() -> handleXMPPClient(client)).start();
}

public void handleXMPPClient(Socket client) {


// Gérer la messagerie instantanée avec échanges XML
}
6.7. Réimplantation du Protocole STOMP
STOMP (Simple Text Oriented Messaging Protocol) est un protocole léger pour les messages
textuels, souvent utilisé avec les brokers de messages comme RabbitMQ ou ActiveMQ. Il est simple à
réimplémenter en utilisant des sockets pour la connexion des clients.
Dans une réimplantation STOMP, le serveur gérera les connexions des clients, acceptera les
messages publiés, et les acheminera aux abonnés selon le topic.
// Serveur STOMP simplifié
ServerSocket stompServer = new ServerSocket(61613); // Port par défaut STOMP

while (true) {
Socket client = [Link]();
new Thread(() -> handleSTOMPClient(client)).start();
}

public void handleSTOMPClient(Socket client) {


// Gérer la communication textuelle selon les standards STOMP
}

TITRE DU RAPPORT 33
7. Clone et Ré-
implémentation
d'Applications de Chat
Dans cette section du cours, nous allons étudier et ré-implémenter certaines des applications de
messagerie les plus populaires tout en mettant l'accent sur les techniques et protocoles sous-jacents
utilisés dans chaque cas. Nous aborderons également les appels VoIP et les spécificités du protocole
UDP pour les communications en temps réel.

a) WhatsApp : Messagerie instantanée avec chiffrement


E2EE (End-to-End Encryption)
Objectifs :
 Implémenter une application de messagerie similaire à WhatsApp avec un système de
chiffrement de bout en bout (E2EE).
 Gérer les sessions de messagerie entre clients avec une sécurité forte grâce au chiffrement des
messages, permettant à seulement l'expéditeur et le destinataire de lire le contenu des
messages.
Concepts clés :
 Protocole Signal : WhatsApp utilise le protocole de chiffrement développé par Signal, qui
implémente un modèle de chiffrement asymétrique basé sur les clés publiques et privées.
 Échange de clés : Utilisation de l'algorithme Diffie-Hellman pour générer une clé partagée
entre deux utilisateurs.
 Gestion des sessions : Implémentation de la gestion des sessions sécurisées pour
maintenir les communications continues entre les utilisateurs.
Étapes d'implémentation :
1. Génération des paires de clés pour chaque utilisateur lors de leur inscription.
2. Échange des clés publiques entre utilisateurs pour initier la session.
3. Chiffrement et déchiffrement des messages en utilisant la clé partagée pour sécuriser
chaque message.
4. Gestion des notifications et des confirmations de lecture pour indiquer si le message
a été livré et/ou lu.
5. Traitement des erreurs et gestion des cas où un utilisateur est déconnecté ou réinstalle
l'application (requiert la régénération de nouvelles clés).
Outils et technologies recommandés :
 Java Cryptography Extension (JCE) pour les opérations cryptographiques.
TITRE DU RAPPORT 34
 Java Sockets pour établir des connexions réseau entre les clients.

b) Telegram : Messagerie basée sur le cloud


Objectifs :
 Implémenter une application de messagerie qui stocke les messages dans le cloud, permettant
à l'utilisateur d'accéder à ses conversations depuis plusieurs appareils.
Concepts clés :
 Cloud-based Messaging : Contrairement à WhatsApp, Telegram stocke tous les messages
sur ses serveurs en utilisant un chiffrement client-serveur, ce qui permet une synchronisation
multi-appareils.
 Chiffrement client-serveur : Messages chiffrés entre le client et le serveur, mais déchiffrés
côté serveur (non E2EE par défaut).
 Sessions multi-appareils : Gestion de plusieurs sessions en même temps avec possibilité
de commencer une conversation sur un appareil et de la continuer sur un autre.
Étapes d'implémentation :
1. Déploiement d'un serveur centralisé qui stocke les messages des utilisateurs dans une
base de données sécurisée.
2. Synchronisation des messages entre plusieurs appareils en utilisant une architecture
REST ou WebSocket.
3. Gestion des fichiers média (photos, vidéos, documents) envoyés via la messagerie et
stockés dans le cloud.
4. Notifications push pour informer l'utilisateur de nouveaux messages.
Outils et technologies recommandés :
 REST API ou WebSockets pour la synchronisation en temps réel.
 Base de données NoSQL (comme MongoDB) pour stocker les messages.
 Serveur de fichiers pour gérer les pièces jointes.

c) Signal : Chiffrement avancé et gestion des


métadonnées
Objectifs :
 Ré-implémenter une application de messagerie similaire à Signal, qui met l'accent sur la
sécurité des métadonnées en plus du chiffrement des messages.
Concepts clés :
 Chiffrement avancé : Signal utilise un modèle de double ratchet algorithm qui change
fréquemment les clés de session pour chaque message afin de renforcer la sécurité.
 Protection des métadonnées : Signal minimise les métadonnées envoyées, en se basant
uniquement sur des informations minimales telles que l’heure d’envoi et les identifiants des
contacts.
Étapes d'implémentation :

TITRE DU RAPPORT 35
1. Double ratchet algorithm : Implémentation de ce mécanisme pour le renouvellement
constant des clés de session.
2. Sécurisation des métadonnées : Utilisation de perfect forward secrecy pour éviter
que les messages interceptés puissent être déchiffrés ultérieurement.
3. Notifications sécurisées : Mise en place de notifications push sans divulguer de contenu
sensible (juste une mention générique de message reçu).
4. Chiffrement des appels vocaux et vidéo pour protéger l'intégralité des communications.
Outils et technologies recommandés :
 Libsignal (bibliothèque open source de Signal) ou implémentation manuelle du double
ratchet.
 WebRTC pour les appels vocaux et vidéo sécurisés.

d) Appels VoIP : Utilisation d'UDP pour les appels


audio/vidéo en temps réel
Objectifs :
 Implémenter un système d’appels VoIP (Voice over IP) en utilisant le protocole UDP pour
des communications en temps réel.
Concepts clés :
 UDP (User Datagram Protocol) : Contrairement à TCP, ce protocole est sans connexion,
rapide, et plus adapté pour les appels audio/vidéo où la perte de quelques paquets n’est pas
critique.
 RTP (Real-time Transport Protocol) : Utilisé pour le transport des données multimédia.
 Jitter buffer : Mise en place d’un buffer pour lisser les variations de temps de transmission
des paquets UDP.
Étapes d'implémentation :
1. Implémentation d’un serveur UDP qui gère les connexions audio/vidéo entre deux
utilisateurs.
2. Utilisation du protocole RTP pour encapsuler les données audio/vidéo envoyées.
3. Gestion du flux en temps réel avec l'utilisation de buffers pour compenser les délais de
transmission.
4. Chiffrement des données UDP pour garantir la confidentialité des appels.
5. Gestion de la bande passante et ajustement dynamique de la qualité de la voix/vidéo
selon la qualité de la connexion réseau.
Outils et technologies recommandés :
 Java DatagramSocket pour la communication UDP.
 Java Media Framework (JMF) ou WebRTC pour le traitement des flux multimédia.
 SRTP (Secure RTP) pour chiffrer les flux multimédias.

TITRE DU RAPPORT 36
TITRE DU RAPPORT 37

Vous aimerez peut-être aussi