1. Introduction aux Sockets en Java
1. Qu’est-ce qu’un Socket :
Imagine que tu as un téléphone portable et que tu veux appeler ton ami pour lui parler. Le socket, c’est un peu comme le numéro de téléphone de ton ami. C’est un point de contact spécifique que tu utilises pour établir une connexion avec une autre machine sur un réseau, comme Internet. Le socket te permet d’envoyer et de recevoir des données, tout comme tu envoies et reçois des messages lorsque tu appelles ton ami.
2. Classes du Package java.net
:
Maintenant, imagine que tu as un nouveau jouet avec différentes pièces. Dans le monde de la programmation en Java, le package java.net
est comme une boîte à outils remplie de différentes classes que tu peux utiliser pour travailler avec des sockets. Par exemple, la classe Socket
est utilisée lorsque tu veux créer une connexion avec une autre machine, et la classe ServerSocket
est utilisée lorsque tu veux écouter et accepter les connexions entrantes des autres machines.
3. Différence entre les Sockets côté Client et côté Serveur :
Maintenant, imagine que tu veux parler avec ton ami, mais il n’est pas chez lui. Dans ce cas, tu serais le client, car c’est toi qui initie l’appel. Ton ami, qui est à la maison et attend ton appel, serait le serveur. C’est un peu la même chose avec les sockets. Le socket côté client est utilisé par celui qui initie la connexion, tandis que le socket côté serveur est utilisé par celui qui attend et accepte les connexions entrantes.
2. Communication Client-Serveur
1. Création d’une application simple de communication client-serveur :
Maintenant que nous comprenons ce qu’est un socket et comment il fonctionne, nous allons créer une petite application pour mettre en pratique ces concepts. Imagine que tu veux envoyer des messages à un ami qui se trouve dans une autre pièce. Dans cette partie du cours, nous allons apprendre à créer une application en Java qui te permettra d’envoyer des messages à un serveur, qui les recevra et y répondra.
2. Envoi et réception de données entre le client et le serveur :
Dans notre application client-serveur, tu seras le client et le serveur sera comme ton ami à qui tu envoies des messages. Nous allons apprendre comment envoyer des données depuis le client vers le serveur et comment le serveur peut recevoir ces données et y répondre. C’est un peu comme envoyer un message à ton ami et attendre sa réponse.
3. Gestion des flux de données entrantes et sortantes :
Pour que notre communication fonctionne bien, nous devons utiliser des flux de données. Imagine que tu envoies un message écrit sur un morceau de papier à ton ami. Ce morceau de papier est comme un flux de données. Dans notre application, nous allons apprendre à créer des flux de données pour envoyer et recevoir des messages entre le client et le serveur. Cela nous permettra de gérer efficacement la communication et d’échanger des informations de manière fluide.
Code pour le côté Serveur :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
// Création d'un serveur écoutant sur le port 8080
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("Server listening on port 8080...");
// Attente et acceptation de la connexion d'un client
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected: " + clientSocket.getInetAddress().getHostName());
// Création de flux de lecture et écriture pour communiquer avec le client
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// Lecture du message du client
String messageFromClient = in.readLine();
System.out.println("Message from client: " + messageFromClient);
// Réponse au client
out.println("Message received by server: " + messageFromClient);
// Fermeture des flux et des sockets
out.close();
in.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explications :
- Le serveur écoute les connexions entrantes sur le port 8080 à l’aide de la classe
ServerSocket
. - Lorsqu’un client se connecte, le serveur accepte la connexion et crée un socket pour communiquer avec ce client.
- Des flux de lecture et d’écriture sont créés pour envoyer et recevoir des données avec le client.
- Le serveur lit le message envoyé par le client, l’affiche, puis envoie une réponse au client.
- Enfin, les flux et les sockets sont fermés pour libérer les ressources.
Code pour le côté Client :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
// Connexion au serveur sur l'adresse "localhost" (même machine) et le port 8080
Socket socket = new Socket("localhost", 8080);
// Création de flux de lecture et écriture pour communiquer avec le serveur
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Envoi d'un message au serveur
out.println("Hello from client!");
// Lecture de la réponse du serveur
String responseFromServer = in.readLine();
System.out.println("Server response: " + responseFromServer);
// Fermeture des flux et du socket
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Explications :
- Le client se connecte au serveur sur l’adresse « localhost » (même machine) et le port 8080 à l’aide de la classe
Socket
. - Des flux de lecture et d’écriture sont créés pour envoyer et recevoir des données avec le serveur.
- Le client envoie un message au serveur en utilisant le flux d’écriture.
- Ensuite, il lit la réponse du serveur à l’aide du flux de lecture et l’affiche.
- Enfin, les flux et le socket sont fermés pour libérer les ressources.
Ces exemples de code illustrent comment créer une communication simple entre un client et un serveur en Java à l’aide de sockets. Les explications détaillées fournies avec chaque partie du code devraient aider à comprendre le fonctionnement de chaque composant de l’application.
3. Gestion de Connexions Multiples
Objectif :
Dans cette partie du cours, nous allons apprendre à gérer plusieurs connexions client simultanées sur le serveur. Plutôt que de traiter les connexions une par une de manière séquentielle, nous allons créer un serveur multi-threadé capable de gérer plusieurs connexions en même temps. Cela permettra à notre serveur d’être plus réactif et de pouvoir communiquer avec plusieurs clients simultanément.
Explication :
Lorsque nous développons une application serveur, il est important de pouvoir gérer efficacement les connexions de multiples clients. Imaginez un café où le serveur doit prendre des commandes de plusieurs clients en même temps. Plutôt que de servir les clients un par un, le serveur peut utiliser plusieurs serveurs assistants pour s’occuper de chaque client individuellement. De la même manière, notre serveur Java peut créer un thread distinct pour chaque connexion client, permettant ainsi de gérer les communications de manière concurrente.
Implémentation :
Voici comment nous pouvons implémenter la gestion de connexions multiples sur le serveur en Java :
- Créer une classe
ClientHandler
qui étend la classeThread
.
- Cette classe sera responsable de gérer la communication avec un client spécifique.
- Elle recevra le socket correspondant à la connexion client et gérera les flux de lecture et d’écriture pour échanger des données avec ce client.
- Dans le code du serveur principal, lorsqu’un nouveau client se connecte, créer une nouvelle instance de
ClientHandler
et démarrer un nouveau thread pour gérer cette connexion.
- Cela permettra au serveur de continuer à écouter de nouvelles connexions tout en gérant les connexions existantes de manière concurrente.
Exemple de Code :
Voici un exemple simplifié de la mise en œuvre de la gestion de connexions multiples sur le serveur en Java :
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("Server listening on port 8080...");
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected: " + clientSocket.getInetAddress().getHostName());
// Créer un thread pour gérer la connexion client
ClientHandler clientHandler = new ClientHandler(clientSocket);
clientHandler.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientHandler extends Thread {
private Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
public void run() {
try {
// Gérer la communication avec le client
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String message;
while ((message = in.readLine()) != null) {
System.out.println("Client message: " + message);
out.println("Server received: " + message);
}
// Fermer les flux et le socket
in.close();
out.close();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Dans cet exemple, chaque fois qu’un nouveau client se connecte au serveur, un nouveau thread ClientHandler
est créé pour gérer la communication avec ce client. Cela permet au serveur de gérer simultanément les connexions de plusieurs clients, améliorant ainsi les performances et la réactivité de l’application.
Communication UDP en Java
Qu’est-ce que l’UDP ?
UDP (User Datagram Protocol) est un protocole de communication sans connexion qui fonctionne sur le modèle de la communication par datagrammes. Contrairement au protocole TCP, UDP ne garantit pas la livraison des données ni l’ordre d’arrivée, mais il est plus rapide et plus léger. Il est souvent utilisé dans les applications où la perte occasionnelle de données n’est pas critique, comme la diffusion de vidéos en direct ou les jeux en ligne.
Communication Client-Serveur UDP :
Dans une communication client-serveur UDP, le client envoie des datagrammes (paquets de données) au serveur, qui les reçoit et peut éventuellement renvoyer une réponse. Les datagrammes sont envoyés sans établir de connexion préalable entre le client et le serveur, ce qui rend la communication plus rapide, mais moins fiable que TCP.
Exemple de Code :
Voici un exemple simplifié de communication client-serveur UDP en Java :
Code du côté Serveur :
import java.net.*;
public class UDPServer {
public static void main(String[] args) {
try {
// Création d'un socket UDP pour écouter sur le port 9876
DatagramSocket serverSocket = new DatagramSocket(9876);
byte[] receiveData = new byte[1024];
while (true) {
// Création d'un datagramme pour recevoir les données du client
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
// Réception du datagramme du client
serverSocket.receive(receivePacket);
// Extraction des données du datagramme
String clientMessage = new String(receivePacket.getData());
// Affichage du message reçu du client
System.out.println("Received from client: " + clientMessage);
// Réponse au client
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
String responseMessage = "Message received!";
byte[] sendData = responseMessage.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientAddress, clientPort);
serverSocket.send(sendPacket);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Code du côté Client :
import java.net.*;
public class UDPClient {
public static void main(String[] args) {
try {
// Création d'un socket UDP
DatagramSocket clientSocket = new DatagramSocket();
// Adresse IP et port du serveur
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 9876;
// Message à envoyer au serveur
String message = "Hello from client!";
byte[] sendData = message.getBytes();
// Création d'un datagramme pour envoyer les données au serveur
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
// Envoi du datagramme au serveur
clientSocket.send(sendPacket);
// Réception de la réponse du serveur
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
// Extraction des données de la réponse du serveur
String serverResponse = new String(receivePacket.getData());
// Affichage de la réponse du serveur
System.out.println("Server response: " + serverResponse);
// Fermeture du socket client
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Dans cet exemple, le serveur écoute sur le port 9876 pour les datagrammes UDP envoyés par le client. Lorsqu’un datagramme est reçu, le serveur extrait les données, les affiche et renvoie une réponse au client. Le client envoie un datagramme contenant un message au serveur, puis attend une réponse. La communication entre le client et le serveur se fait sans établir de connexion préalable, ce qui la rend plus rapide mais moins fiable que TCP.
Communication de Diffusion Multicast en Java
La communication de diffusion, également appelée diffusion multicast, est une méthode de communication dans laquelle un seul expéditeur envoie des données à plusieurs destinataires en même temps. Voici un exemple de base sur la mise en œuvre de la communication de diffusion multicast en Java :
Explication :
Dans la diffusion multicast, un groupe d’ordinateurs s’inscrit à une adresse de groupe multicast spécifique pour recevoir des messages envoyés à cette adresse. L’expéditeur envoie les données à cette adresse, et tous les membres du groupe multicast peuvent les recevoir.
Exemple de Code :
Code du côté Serveur (Expéditeur) :
import java.net.*;
public class MulticastServer {
public static void main(String[] args) {
try {
// Création d'un socket de diffusion multicast
MulticastSocket serverSocket = new MulticastSocket();
// Adresse IP de diffusion multicast (groupe multicast)
InetAddress group = InetAddress.getByName("230.0.0.1");
// Message à diffuser
String message = "Hello, everyone!";
// Création d'un datagramme contenant le message à diffuser
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), group, 4446);
// Envoi du datagramme à l'adresse de diffusion multicast
serverSocket.send(packet);
// Fermeture du socket de diffusion multicast
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Code du côté Client (Récepteur) :
import java.net.*;
public class MulticastClient {
public static void main(String[] args) {
try {
// Création d'un socket de diffusion multicast et liaison à l'adresse de groupe multicast
MulticastSocket clientSocket = new MulticastSocket(4446);
InetAddress group = InetAddress.getByName("230.0.0.1");
clientSocket.joinGroup(group);
// Réception des datagrammes multicast
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
clientSocket.receive(packet);
// Affichage du message reçu
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("Received message: " + message);
// Quitter le groupe de diffusion multicast
clientSocket.leaveGroup(group);
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Dans cet exemple, le serveur envoie un message à l’adresse de groupe multicast « 230.0.0.1 » sur le port 4446. Le client rejoint ce groupe multicast, reçoit le message envoyé par le serveur et l’affiche. La communication de diffusion multicast permet à plusieurs destinataires de recevoir des données envoyées par un seul expéditeur à l’adresse de groupe multicast spécifiée.