Fondamentaux de la Programmation Réseau TCP avec Java

La communication réseau en Java s'appuie sur l'API Socket pour gérer les échanges de données via le protocole TCP. Cette approche garantit la fiabiilté des transmissions entre un client et un serveur. Nous examinerons ici plusieurs niveaux d'implémentation, depuis la connexion basique jusqu'à la gestion concurrente et le transfert de fichiers.

  1. Connexion Simple avec Flux Binaires

Cette première étape démontre l'établissement d'un canal de communication utilisant des flux d'octets bruts. Le client se connecte à une adresse IP locale spécifique pour échanger des messages.

Implémentation du Client

package org.example.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClientBasic {
    public static void main(String[] args) throws IOException {
        // Établissement de la connexion sur le port 9090
        Socket connection = new Socket("127.0.0.1", 9090);
        
        // Obtention des flux de communication
        InputStream inputStream = connection.getInputStream();
        OutputStream outputStream = connection.getOutputStream();
        
        // Lecture de la réponse initiale du serveur
        byte[] buffer = new byte[2048];
        int bytesRead = inputStream.read(buffer);
        System.out.println("Réserve serveur: " + new String(buffer, 0, bytesRead));
        
        // Envoi d'une requête au serveur
        outputStream.write("Requête client initiale".getBytes());
        
        connection.close();
    }
}

Implémentation du Serveur

package org.example.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerBasic {
    public static void main(String[] args) throws IOException {
        // Démarrage de l'écoute sur le port 9090
        ServerSocket serverSocket = new ServerSocket(9090);
        
        // Attente d'une connexion entrante
        Socket clientSocket = serverSocket.accept();
        
        InputStream inputStream = clientSocket.getInputStream();
        OutputStream outputStream = clientSocket.getOutputStream();
        
        // Envoi d'un message de bienvenue
        outputStream.write("Bienvenue sur le serveur TCP".getBytes());
        
        // Réception des données du client
        byte[] buffer = new byte[2048];
        int length = inputStream.read(buffer);
        System.out.println("Données reçues: " + new String(buffer, 0, length));
        
        serverSocket.close();
    }
}
  1. Optimisation avec Flux de Caractères

Pour faciliter la gestion des lignes de texte, lu'tilisation de BufferedReader et PrintStream est recommandée. Cela permet de lire et d'écrire des chaînes complètes plutôt que des octets individuels.

Client Optimisé

package org.example.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

public class TcpClientBuffered {
    public static void main(String[] args) throws IOException {
        Socket connection = new Socket("127.0.0.1", 9090);
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        PrintStream writer = new PrintStream(connection.getOutputStream());
        
        // Affichage de la première ligne reçue
        System.out.println(reader.readLine());
        
        // Envoi d'une ligne de texte
        writer.println("Demande d'inscription");
        System.out.println(reader.readLine());
        
        connection.close();
    }
}

Serveur Optimisé

package org.example.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerBuffered {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9090);
        Socket clientSocket = serverSocket.accept();
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        PrintStream writer = new PrintStream(clientSocket.getOutputStream());
        
        writer.println("Accès autorisé au service");
        System.out.println("Message client: " + reader.readLine());
        writer.println("Traitement terminé");
        
        serverSocket.close();
    }
}
  1. Gestion Concurrente des Clients

Un serveur capable de gérer plusieurs clients simultanément doit utiliser le multithreading. Chaque connexion acceptée est déléguée à un thread indépendant.

package org.example.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerConcurrent {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9090);
        
        while (true) {
            final Socket clientSocket = serverSocket.accept();
            new Thread(new ClientHandler(clientSocket)).start();
        }
    }
    
    static class ClientHandler implements Runnable {
        private final Socket socket;
        
        public ClientHandler(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                PrintStream writer = new PrintStream(socket.getOutputStream());
                
                writer.println("Session démarrée");
                System.out.println("Reçu: " + reader.readLine());
                writer.println("Session fermée");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. Exercice Pratique : Inversion de Chaîne

Dans ce scénario, le client envoie une chaîne de caractères et le serveur la renvoie inversée. Cela valide la capacité de traitement des données avant retour.

Client d'Inversion

package org.example.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientStringReverse {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9091);
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintStream writer = new PrintStream(socket.getOutputStream());
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Serveur: " + reader.readLine());
        
        String input = scanner.nextLine();
        writer.println(input);
        
        String reversed = reader.readLine();
        System.out.println("Résultat: " + reversed);
        
        socket.close();
    }
}

Serveur d'Inversion

package org.example.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerStringReverse {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9091);
        Socket clientSocket = serverSocket.accept();
        
        new Thread(() -> {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintStream writer = new PrintStream(clientSocket.getOutputStream());
                
                writer.println("Prêt à recevoir du texte");
                String original = reader.readLine();
                StringBuilder sb = new StringBuilder(original);
                writer.println(sb.reverse().toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
  1. Transfert de Fichiers via Réseau

Cette dernière section couvre l'envoi d'un fichier depuis le client vers le serveur. Le serveur vérifie l'existence du fichier avant d'accepter les octets.

Client d'Envoi

package org.example.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class FileUploadClient {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Chemin du fichier: ");
        String path = scanner.nextLine();
        File file = new File(path);
        
        if (!file.exists() || file.isDirectory()) {
            System.out.println("Fichier invalide");
            return;
        }
        
        Socket socket = new Socket("127.0.0.1", 9090);
        PrintStream writer = new PrintStream(socket.getOutputStream());
        Scanner responseReader = new Scanner(socket.getInputStream());
        
        // Envoi du nom du fichier
        writer.println(file.getName());
        
        // Vérification côté serveur
        if ("EXISTS".equals(responseReader.nextLine())) {
            System.out.println("Le fichier existe déjà sur le serveur");
            socket.close();
            return;
        }
        
        // Envoi du contenu
        FileInputStream fis = new FileInputStream(file);
        byte[] buffer = new byte[8192];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            writer.write(buffer, 0, len);
        }
        
        fis.close();
        socket.close();
    }
}

Serveur de Réception

package org.example.io;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class FileUploadServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9090);
        File storageDir = new File("uploads");
        if (!storageDir.exists()) storageDir.mkdir();
        
        while (true) {
            final Socket clientSocket = serverSocket.accept();
            new Thread(() -> {
                try {
                    InputStream input = clientSocket.getInputStream();
                    Scanner scanner = new Scanner(input);
                    PrintStream writer = new PrintStream(clientSocket.getOutputStream());
                    
                    String fileName = scanner.nextLine();
                    File targetFile = new File(storageDir, fileName);
                    
                    if (targetFile.exists()) {
                        writer.println("EXISTS");
                        clientSocket.close();
                        return;
                    }
                    
                    writer.println("OK");
                    
                    FileOutputStream fos = new FileOutputStream(targetFile);
                    byte[] buffer = new byte[8192];
                    int len;
                    // Correction de la logique de lecture
                    while ((len = input.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                    }
                    
                    fos.close();
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Étiquettes: Java tcp-socket Network-Programming multithreading file-io

Publié le 21 septembre à 19h16