Socket JAVA


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 :

  1. Créer une classe ClientHandler qui étend la classe Thread.
  • 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.
  1. 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.