JAVA : Threads & Socket : 1 Serveur / n Clients

En Java, un thread est une unité d’exécution indépendante qui permet d’exécuter plusieurs tâches simultanément. Cela est particulièrement utile lorsque vous devez effectuer des opérations qui prennent beaucoup de temps, telles que la lecture et l’écriture de données dans un socket. Les threads peuvent être utilisés pour gérer les connexions de socket de manière concurrente et permettre à votre programme de traiter simultanément plusieurs connexions de manière efficace.

Voici un exemple de création de thread en Java:

Thread thread = new Thread(new Runnable() {
    public void run() {
        // Code exécuté par le thread
    }
});

thread.start(); // Démarre le thread

Dans cet exemple, nous créons un thread en passant une instance de la classe Runnable au constructeur de la classe Thread. La méthode run() de l’objet Runnable est exécutée lorsque le thread est démarré à l’aide de la méthode start().

Maintenant, voici comment utiliser des threads en conjonction avec des sockets:

ServerSocket serverSocket = new ServerSocket(12345);

while (true) {
    Socket socket = serverSocket.accept();
    Thread thread = new Thread(new Runnable() {
        public void run() {
            // Code pour traiter la connexion de socket
        }
    });
    thread.start();
}

Dans cet exemple, nous avons créé un serveur de socket qui écoute sur le port 12345. Lorsqu’une connexion de socket est acceptée, nous créons un nouveau thread pour traiter la connexion. Le code de traitement de la connexion est exécuté dans le contexte de ce nouveau thread, ce qui permet de gérer plusieurs connexions simultanément.

Notez que vous devez être prudent lorsque vous utilisez des threads pour gérer des connexions de socket, car une utilisation incorrecte des threads peut entraîner des problèmes de concurrence, tels que des conflits d’accès à des ressources partagées. Vous devez vous assurer que votre code est bien synchronisé et que les threads sont utilisés de manière appropriée pour éviter les problèmes de concurrence.

Voici un exemple simple de communication client-serveur en Java utilisant des sockets :

Serveur :

import java.io.; import java.net.;

public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(12345); // Crée un serveur de socket qui écoute sur le port 12345
System.out.println(« Serveur démarré. »);

    while (true) {
        Socket clientSocket = serverSocket.accept(); // Attend une connexion de client
        System.out.println("Connexion établie avec " + clientSocket.getInetAddress());

        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

        String inputLine;
        while ((inputLine = in.readLine()) != null) { // Attend une requête du client
            System.out.println("Requête reçue : " + inputLine);
            out.println("Serveur a reçu votre requête : " + inputLine); // Envoie une réponse au client
        }

        out.close();
        in.close();
        clientSocket.close();
    }
}

}

Dans cet exemple, le serveur crée un socket serveur qui écoute sur le port 12345. Lorsqu’une connexion de client est établie, le serveur crée un nouveau thread pour gérer la connexion et continue à écouter les connexions entrantes.

Client :

import java.io.*;
import java.net.*;

public class Client {
    public static void main(String[] args) throws IOException {
        String hostName = "localhost"; // Nom d'hôte du serveur
        int portNumber = 12345; // Numéro de port utilisé par le serveur

        try (
            Socket socket = new Socket(hostName, portNumber);
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))
        ) {
            String userInput;
            while ((userInput = stdIn.readLine()) != null) { // Attend une entrée de l'utilisateur
                out.println(userInput); // Envoie la requête au serveur
                System.out.println("Serveur répond : " + in.readLine()); // Attend la réponse du serveur
            }
        } catch (UnknownHostException e) {
            System.err.println("Hôte inconnu : " + hostName);
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Connexion échouée à " + hostName + " sur le port " + portNumber);
            System.exit(1);
        }
    }
}

Dans cet exemple, le client se connecte au serveur en utilisant le nom d’hôte localhost et le numéro de port 12345. Le client attend ensuite une entrée de l’utilisateur et envoie la requête au serveur via le socket. Le client attend ensuite la réponse du serveur et l’affiche à l’utilisateur. Le client continue ce processus jusqu’à ce que l’utilisateur entre exit.

Notez que dans cet exemple, le serveur ne gère qu’une seule connexion de client à la fois. Pour gérer plusieurs connexions de client, vous devrez utiliser des threads pour chaque connexion de client.

Et Pour connecter plusieurs clients, vous devez utiliser des threads pour chaque connexion de client.

Lorsqu’un client se connecte, vous devez créer un nouveau thread pour gérer cette connexion. Ce thread doit s’occuper de la communication avec le client et écouter les requêtes entrantes du client.

Voici un exemple de serveur multi-threadé qui peut gérer plusieurs connexions de clients simultanément :

import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(12345); // Crée un serveur de socket qui écoute sur le port 12345
        System.out.println("Serveur démarré.");

        while (true) {
            Socket clientSocket = serverSocket.accept(); // Attend une connexion de client
            System.out.println("Connexion établie avec " + clientSocket.getInetAddress());

            // Crée un nouveau thread pour gérer cette connexion
            Thread thread = new Thread(new ClientHandler(clientSocket));
            thread.start();
        }
    }
}

class ClientHandler implements Runnable {
    private Socket clientSocket;

    public ClientHandler(Socket socket) {
        this.clientSocket = socket;
    }

    public void run() {
        try {
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

            String inputLine;
            while ((inputLine = in.readLine()) != null) { // Attend une requête du client
                System.out.println("Requête reçue de " + clientSocket.getInetAddress() + " : " + inputLine);
                out.println("Serveur a reçu votre requête : " + inputLine); // Envoie une réponse au client
            }

            out.close();
            in.close();
            clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Dans cet exemple, le serveur crée un socket serveur qui écoute sur le port 12345. Lorsqu’une connexion de client est établie, le serveur crée un nouveau thread pour gérer la connexion et continue à écouter les connexions entrantes.

Le thread ClientHandler gère la communication avec le client. Il crée des objets PrintWriter et BufferedReader pour communiquer avec le client, attend les requêtes entrantes du client et envoie une réponse au client pour chaque requête.

Notez que ce code est toujours assez basique et qu’il y a plusieurs éléments de sécurité et de gestion d’erreur qui devraient être pris en compte pour un véritable système de communication client-serveur en production.