TP1 du client-serveur à l'invocation des méthodes au P2P hybride

Troisième TP

By Mariem ZAOUALI

TP1 du client-serveur à l’invocation des méthodes au P2P hybride

Objectifs du TP :

Au terme de ce TP, vous serez capable de : • Créer des applications client/serveur utilisant les sockets avec TCP et UDP • Gérer le problème de concurrence de plusieurs clients se connectant à un seul serveur (le multi-threading) • Répartir l’application et appeler un objet distant

Manipulation 1

Le but de cette manipulation de démo de créer :

  • Une application client/serveur utilisant les sockets en mode connecté (protocole de transport utilisé TCP)
  • Une application client/serveur utilisant les sockets en mode non connecté (protocole de transport utilisé UDP)

Client_TCP

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**
 * Client TCP générique pouvant se connecter à un serveur donné.
 * Compatible avec plusieurs serveurs (Server1, Server2, Server3...).
 */
public class Client {

    private final String serverName = "localhost"; // Adresse du serveur
    private final int serverPort = 8081;           // Port du serveur

    public Client() {
        System.out.println("=== Client TCP ===");

        try (
            // Création automatique de la socket et des flux
            Socket socket = new Socket(serverName, serverPort);
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            Scanner scanner = new Scanner(System.in)
        ) {
            System.out.println("Client démarré sur le port " + socket.getLocalPort());
            System.out.println("Connecté à " + socket.getRemoteSocketAddress());
            System.out.println("Tapez 'exit' pour quitter.\n");

            while (true) {
                System.out.print("Message à envoyer : ");
                String messageToServer = scanner.nextLine().trim();

                // Quitter proprement
                if (messageToServer.equalsIgnoreCase("exit")) {
                    dos.writeUTF("exit");
                    break;
                }

                // Envoi du message
                dos.writeUTF(messageToServer);
                dos.flush();

                // Lecture de la réponse du serveur
                String messageFromServer = dis.readUTF();
                if (messageFromServer.equalsIgnoreCase("exit")) {
                    System.out.println("Serveur a fermé la connexion.");
                    break;
                }

                System.out.println("[" + socket.getRemoteSocketAddress() + "] → " + messageFromServer);
            }

            System.out.println("Déconnexion du client...");

        } catch (IOException e) {
            System.err.println("Erreur de communication : " + e.getMessage());
        }

        System.out.println("Client arrêté.");
    }

    public static void main(String[] args) {
        new Client();
    }
}

Serveur_TCP

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * Serveur TCP simple — dialogue avec un client unique.
 * Compatible avec le client TCP utilisant DataInputStream/DataOutputStream.
 */
public class Server {

    private final int port = 8081;  // Port d’écoute

    public Server() {
        System.out.println("=== Serveur TCP ===");

        try (ServerSocket serverSocket = new ServerSocket(port)) {

            System.out.println("Serveur démarré sur le port " + serverSocket.getLocalPort());
            System.out.println("En attente d’un client...\n");

            // Attente d'une connexion
            try (
                Socket socket = serverSocket.accept();
                DataInputStream dis = new DataInputStream(
                        new BufferedInputStream(socket.getInputStream()));
                DataOutputStream dos = new DataOutputStream(
                        new BufferedOutputStream(socket.getOutputStream()));
                Scanner scanner = new Scanner(System.in)
            ) {
                System.out.println("Client " + socket.getRemoteSocketAddress() + " connecté.");

                while (true) {
                    // Lecture du message du client
                    String messageFromClient = dis.readUTF().trim();
                    if (messageFromClient.equalsIgnoreCase("exit")) {
                        System.out.println("Client a demandé la fermeture.");
                        break;
                    }

                    System.out.println("[Client " + socket.getRemoteSocketAddress() + "] → " + messageFromClient);

                    // Préparer la réponse
                    System.out.print("Message à envoyer : ");
                    String messageToClient = scanner.nextLine().trim();

                    // Si le serveur tape 'exit', il ferme la session
                    if (messageToClient.equalsIgnoreCase("exit")) {
                        dos.writeUTF("exit");
                        dos.flush();
                        break;
                    }

                    // Envoi de la réponse
                    dos.writeUTF(messageToClient);
                    dos.flush();
                }

                System.out.println("Client " + socket.getRemoteSocketAddress() + " déconnecté.");
            }

        } catch (IOException e) {
            System.err.println("Erreur du serveur : " + e.getMessage());
        }

        System.out.println("Serveur arrêté.");
    }

    public static void main(String[] args) {
        new Server();
    }
}

ClientUDP

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

/**
 * Client UDP qui envoie un message sérialisé à un serveur donné.
 */
public class UDPClient {

    public static void main(String[] args) {
        final int SERVER_PORT = 8081;
        final String SERVER_HOST = "localhost";
        final String MESSAGE = "Bonjour du cours de UDP envoyé du client !";

        System.out.println("=== UDP Client ===");

        // try-with-resources : fermeture automatique du socket
        try (DatagramSocket socket = new DatagramSocket()) {

            InetAddress serverAddress = InetAddress.getByName(SERVER_HOST);
            System.out.println("Serveur ciblé : " + serverAddress.getHostAddress() + ":" + SERVER_PORT);

            // Sérialisation du message dans un flux de bytes
            byte[] sendBuffer = serializeObject(MESSAGE);

            // Création du datagramme à envoyer
            DatagramPacket packet = new DatagramPacket(sendBuffer, sendBuffer.length, serverAddress, SERVER_PORT);

            // Envoi du datagramme
            socket.send(packet);
            System.out.println("Message envoyé au serveur : \"" + MESSAGE + "\"");

        } catch (UnknownHostException e) {
            System.err.println("Adresse du serveur inconnue : " + SERVER_HOST);
        } catch (IOException e) {
            System.err.println("Erreur d’E/S : " + e.getMessage());
        }

        System.out.println("Client UDP terminé.");
    }

    /**
     * Sérialise un objet Java en tableau d’octets.
     */
    private static byte[] serializeObject(Object obj) {
        try (
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(byteStream))
        ) {
            oos.writeObject(obj);
            oos.flush();
            return byteStream.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Erreur de sérialisation de l’objet : " + e.getMessage(), e);
        }
    }
}

Serveur UDP

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

/**
 * Serveur UDP qui reçoit un message sérialisé depuis un client.
 */
public class UDPServer {

    private static final int SERVER_PORT = 8081;
    private static final int BUFFER_SIZE = 5000;
    private static final int TIMEOUT_MS = 30000; // 30 secondes

    public static void main(String[] args) {
        System.out.println("=== UDP Server ===");
        new UDPServer().runServer();
    }

    public void runServer() {
        try (DatagramSocket socket = new DatagramSocket(SERVER_PORT)) {
            System.out.println("Serveur en écoute sur le port " + SERVER_PORT + "...");
            socket.setSoTimeout(TIMEOUT_MS);

            byte[] buffer = new byte[BUFFER_SIZE];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

            // Attente de la réception
            try {
                socket.receive(packet);
                System.out.println("Paquet reçu depuis : " +
                        packet.getAddress().getHostAddress() + ":" + packet.getPort());

                // Traitement du message
                String message = deserializeObject(packet.getData());
                System.out.println("Message reçu du client : " + message);

            } catch (SocketTimeoutException e) {
                System.err.println(" Timeout : aucun paquet reçu dans le délai imparti (" + TIMEOUT_MS + " ms).");
            }

        } catch (SocketException e) {
            System.err.println("Erreur lors de la création du socket : " + e.getMessage());
        } catch (IOException e) {
            System.err.println("Erreur d’E/S lors de la réception : " + e.getMessage());
        }

        System.out.println("Serveur arrêté.");
    }

    /**
     * Désérialise un objet à partir d’un tableau d’octets.
     */
    private String deserializeObject(byte[] data) {
        try (
            ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(byteStream))
        ) {
            return (String) ois.readObject();
        } catch (IOException e) {
            throw new RuntimeException("Erreur de désérialisation : " + e.getMessage(), e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Classe non trouvée pour l’objet reçu.", e);
        }
    }
}

Manipulation 2

Le but de cette manipulation démo est de créer : • Une application client/serveur multi-threadée utilisant les sockets en mode connecté (protocole de transport utilisé TCP)

Client

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;

public class ClientDemo1 {

    private static final String SERVER_HOST = "localhost";
    private static final int SERVER_PORT = 7771;

    public static void main(String[] args) {

        try (
            Socket socket = new Socket(SERVER_HOST, SERVER_PORT);
            DataOutputStream dos = new DataOutputStream(
                    new BufferedOutputStream(socket.getOutputStream()));
            DataInputStream dis = new DataInputStream(
                    new BufferedInputStream(socket.getInputStream()))
        ) {
            // Messages à envoyer
            sendMessage(dos, "HELLO! now is " + new Date());
            sendMessage(dos, "Trial");
            sendMessage(dos, "QUIT");

            // Lecture de la réponse du serveur
            readServerResponses(dis);

        } catch (UnknownHostException e) {
            System.err.println("Unknown host: " + SERVER_HOST);
        } catch (IOException e) {
            System.err.println("I/O error while connecting to " + SERVER_HOST + ":" + SERVER_PORT);
            e.printStackTrace();
        }
    }

    /** Envoie un message au serveur et flush le flux. */
    private static void sendMessage(DataOutputStream dos, String message) throws IOException {
        dos.writeUTF(message);
        dos.flush();
    }

    /** Lit les réponses du serveur jusqu’à un message contenant "OK". */
    private static void readServerResponses(DataInputStream dis) throws IOException {
        String response;
        while ((response = dis.readUTF()) != null) {
            System.out.println("Server: " + response);
            if (response.contains("OK")) {
                break;
            }
        }
    }
}

Serveur - Thread

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class ServiceThread1 extends Thread {

    private final int clientNumber;
    private final Socket socketOfServer;

    public ServiceThread1(Socket socketOfServer, int clientNumber) {
        this.clientNumber = clientNumber;
        this.socketOfServer = socketOfServer;

        // Log propre
        ServerProgram1.log("Nouvelle connexion avec le client #" + clientNumber +
                           " depuis " + socketOfServer.getInetAddress() + ":" + socketOfServer.getPort());
    }

    @Override
    public void run() {
        try (
            DataInputStream dis = new DataInputStream(new BufferedInputStream(socketOfServer.getInputStream()));
            DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socketOfServer.getOutputStream()))
        ) {
            // Message de bienvenue
            dos.writeUTF("Bonjour client #" + clientNumber + " ! Bienvenue sur le serveur.");
            dos.flush();

            // Boucle de lecture du client
            String clientMessage;
            while (true) {
                clientMessage = dis.readUTF();  // bloquant
                ServerProgram1.log("Client #" + clientNumber + " a dit : " + clientMessage);

                if ("QUIT".equalsIgnoreCase(clientMessage)) {
                    dos.writeUTF("OK, bye!");
                    dos.flush();
                    break; // sortir de la boucle et fermer le socket
                } else {
                    dos.writeUTF("Server received: " + clientMessage);
                    dos.flush();
                }
            }

        } catch (IOException e) {
            ServerProgram1.log("Erreur avec le client #" + clientNumber + " : " + e.getMessage());
        } finally {
            try {
                socketOfServer.close();
                ServerProgram1.log("Connexion fermée avec le client #" + clientNumber);
            } catch (IOException e) {
                ServerProgram1.log("Erreur lors de la fermeture de la connexion du client #" + clientNumber);
            }
        }
    }

}

Serveur

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

public class ServerProgram1 {

    private static final int PORT = 7771;

    public static void main(String[] args) {
        System.out.println("Serveur en attente de connexions sur le port " + PORT + "...");
        int clientNumber = 1;

        // Utilisation du try-with-resources pour garantir la fermeture du ServerSocket
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            while (true) {
                // Attente d'une connexion client
                Socket clientSocket = serverSocket.accept();

                // Log clair
                log("Connexion acceptée depuis " + clientSocket.getInetAddress() + ":" + clientSocket.getPort());

                // Lancement du thread client
                new ServiceThread1(clientSocket, clientNumber++).start();
            }

        } catch (IOException e) {
            log("Erreur lors de la création ou de l'écoute du serveur : " + e.getMessage());
            e.printStackTrace();
        }

        // Ce message n’est jamais atteint en mode serveur continu,
        // mais utile si la boucle est interrompue.
        log("Serveur arrêté.");
    }

    /** Méthode centralisée de log pour uniformiser les sorties. */
    public static void log(String message) {
        System.out.println("[Server] " + message);
    }
}

Manipulation 3

Le but de cette manipulation est de montrer comment implémenter la technologie Remote Method Invocation (RMI). Invoquer des méthodes sur un objet qui se trouve dans une autre JVM, éventuellement sur une machine distante. Pour ce faire, nous développerons une application côté client et une application côté serveur. L’arborescence de ces deux applications est comme suit:

Module Client (Guided_JavaRMI_client)

MessageInfo.java

  • Rôle : Classe sérialisable qui représente la structure des messages échangés entre client et serveur
  • ** Important** : Doit être exactement la même des deux côtés (client et serveur) **
package tn.enit;
import java.io.Serializable;

public class MessageInfo implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public int totalMessages;
	public int messageNum;

	public MessageInfo(int total, int msgNum) {
		totalMessages = total;
		messageNum = msgNum;
	}
}

RMServerIntf.java

  • Rôle : Interface qui définit les méthodes distantes que le serveur expose
  • Contient : Les signatures des méthodes que le client peut appeler à distance
package tn.enit;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RMIServerIntf extends Remote {
	public String getMessage(MessageInfo m) throws RemoteException;
}

RMIClient.java

  • Rôle : Classe principale du client qui implémente la logique client, montre comment se connecte au registre RMI, obtenir la référence de l’objet distant serveur et envoyer les messages au serveur.
package tn.enit;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient { 
    public static void main(String args[]) throws Exception {
    	//TODO créer un registry pour localiser le serveur localhost qui propose le service
        Registry reg = LocateRegistry.getRegistry("localhost");
        //TODO récupérer l'objet du registry
    	RMIServerIntf obj = (RMIServerIntf) reg.lookup("RMIServer");
        MessageInfo v= new MessageInfo(1,10);
        //TODO Lancer le traitement distant
        System.out.println(obj.getMessage(v));
        
    }
}

Module Serveur (Guided_JavaRMLserver)

MessageInfo.java

  • Rôle : Même classe que côté client (doit être identique), nécessaire pour la désérialisation correcte des objets reçus ```java package tn.enit; import java.io.Serializable;

public class MessageInfo implements Serializable {

public static final long serialVersionUID = 52L;

public int totalMessages;
public int messageNum;


public MessageInfo(int total, int msgNum) {
	totalMessages = total;
	messageNum = msgNum;
}
public String toString(){
	return new String("C'est un traitement chez le serveur que vous croyez qu'il se fait en local chez le client "+totalMessages+";"+messageNum+"\n");
} } ```

RMServerIntf.java

  • Rôle : Même interface que côté client (doit être identique), pour garantir la cohérence entre les signatures méthodes appelées et implémentées ```java package tn.enit;

import java.rmi.Remote; import java.rmi.RemoteException;

public interface RMIServerIntf extends Remote { public String getMessage(MessageInfo m) throws RemoteException; }

`RMServer.java`
- Rôle : Classe principale du serveur qui implémente les fonctionnalités. Elle permet de créer et enregistrer l'objet distant dans le registre RMI puis implémenter les méthodes de l'interface RMServerIntf

```java
package tn.enit;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
 
public class RMIServer 
    implements RMIServerIntf {
    public static final String MESSAGE = "Hello world";
 
    public RMIServer() throws RemoteException {
    	
    }
    //TODO Implémenter la procédure distante
    public String getMessage(MessageInfo m) {
    	return "GLSI"+m.toString();
    	
    }
 
    public static void main(String args[]) throws Exception {

        System.out.println("RMI server started");
        
        //Instantiate RmiServer
        RMIServer obj = new RMIServer();
 
        try { //special exception handler for registry creation
        	
        	//TODO exporter l'objet à manipuler à distance (if port is zero, an anonymous port is chosen)
            RMIServerIntf stub = (RMIServerIntf) UnicastRemoteObject.exportObject(obj,0);
            Registry reg;
            try {
            	//TODO création du registry sur le port 1099
            	reg= LocateRegistry.createRegistry(1099);
            	
                System.out.println("java RMI registry created.");

            } catch(Exception e) {
            	System.out.println("Using existing registry");
            	reg = LocateRegistry.getRegistry();
            }
          //TODO nommer l'instance de l'objet distant avec sa nommination
        	reg.rebind("RMIServer",stub);
        	//Naming.bind("RMIServer", obj);

        } catch (RemoteException e) {
        	e.printStackTrace();
        }
    }
}

Manipulation 4

Nous allons implémenter maintenant une architecture P2P hybride comme est indiqué dans la figure suivante : image Il s’agit d’une architecture distribuée comprenant différents composants à savoir pairs, RMI Registry, annuaire P2P. Pour qu’un pair puisse télécharger un fichier sur d’autres pairs du réseau :

  1. Le pair obtient une référence distante RMI sur l’annuaire P2P via le RMI Registry, l’annuaire P2P détient la liste des pairs ainsi que les portions de fichiers (chunks) et il répond aux recherches.
  2. Le pair se connecte sur l’annuaire et obtient une connexion Cx.
  3. Le pair utilise cette connexion pour obtenir la liste des pairs déjà enregistrés sur l’annuaire qui possèdent le fichier voulu.
  4. Le pair s’adresse ensuite directement au(x) pair(s) qui possède(nt) ce fichier dans son intégralité (ou des morceaux recherchés du fichier).

Afin de réaliser cette architecture nous développeons une application java ayant l’arborescence suivante:

projet_rmi_p2p/
│
├── launcher/
│   └── Launcher.java                      # Lance automatiquement le réseau (annuaire + pairs)
│
├── remote/
│   ├── RemoteRegistry.java                # Interface distante du serveur d’annuaire
│   ├── RemoteRegistryConnection.java      # Interface distante de connexion à l’annuaire
│   └── RemotePeer.java                    # Interface distante d’un pair (serveur de fichiers)
│
├── registry/
│   ├── implementation/
│   │   ├── RegistryImpl.java              # Métier de l’annuaire (gestion des pairs & chunks)
│   │   ├── RemoteRegistryImpl.java        # Interface distante exposée via RMI (logon)
│   │   └── RemoteRegistryConnectionImpl.java  # Connexion spécifique à un pair
│   └── utils/
│       └── RegistryHelper.java            # Démarrage/gestion du RMI Registry Java
│
├── peer/
│   ├── implementation/
│   │   ├── RemotePeerImpl.java            # Serveur RMI du pair (fournit des fichiers/chunks)
│   │   └── PeerMain.java                  # Programme principal pour lancer un pair
│   ├── ui/
│   │   └── PeerUI.java                    # Interface console/graphique pour l’utilisateur
│   └── utils/
│       ├── FileUtils.java                 # Outils : découpe, fusion, checksum
│       └── FileDownloader.java            # Téléchargement parallèle via RMI
│
├── model/
│   ├── FileChunkInfo.java                 # Métadonnée : identifiant de chunk, checksum, pairs
│   └── PeerInfo.java                      # Métadonnée : nom, adresse RMI, statut
│
└── docs/
    └── architecture.md                    # Description fonctionnelle (doc ci-dessous)

Avec:

  • Packages et classes
    • launcher/ - Launcher.java : Point d’entrée du projet. on lance automatiquement : - le RMI Registry sur le port 1099, - l’annuaire P2P, - plusieurs pairs avec leurs dossiers respectifs. - Configure les dossiers partagés (shared1, shared2, etc.).

    • remote/ - RemoteRegistry.java : Interface distante principale de l’annuaire P2P exposée via RMI. ➜ Méthode clé : java RemoteRegistryConnection logon(String peerName, String peerAddress) throws RemoteException; - RemoteRegistryConnection.java : Interface distante représentant une connexion logique entre un pair et l’annuaire. ➜ Méthodes typiques : java void publishChunk(FileChunkInfo chunkInfo); Map<String, List<FileChunkInfo>> searchFile(String fileName); void heartbeat(String peerName);

      • RemotePeer.java : Interface distante que chaque pair implémente pour servir ses fichiers ou chunks. ➜ Méthode clé :
          byte[] getFileChunk(String fileName, int chunkIndex, int chunkSize);
        
  • registry/implementation/
    • RegistryImpl.java: Partie métier de l’annuaire P2P. On stocke la liste des pairs (Map<String, PeerInfo>) et des chunks disponibles (Map<String, List>) et on vérifie l’intégrité via checksum. - `RemoteRegistryImpl.java` : Expose le service RemoteRegistry au RMI Registry (port 1099). On gère les connexions des pairs :
        public RemoteRegistryConnection logon(String peerName, String peerAddress) {
        return new RemoteRegistryConnectionImpl(peerName, this);
        }
      
      - RemoteRegistryConnectionImpl.java : Classe qui gère la session d’un pair connecté (Cx dans la Figure). Elle permet au pair :
      - 	de publier ses chunks,
      - 	de chercher des fichiers,
        	- 	de se déconnecter proprement.
      
  • registry/utils/
    • RegistryHelper.java : Classe utilitaire pour initialiser ou détecter un RMI Registry Java. ➜ Port par défaut : 1099. ➜ Permet de ne pas lancer rmiregistry manuellement.
package registry.utils;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 * Utility class to handle the creation and lookup of the RMI Registry.
 * 
 * It ensures that the RMI registry is running on the specified port
 * (by default 1099) and avoids the need to launch it manually
 * using the "rmiregistry" command.
 * 
 * This class can also be reused both by the P2P Registry server
 * and by peers that need to connect to it.
 */
public class RegistryHelper {

    // Default RMI port
    public static final int DEFAULT_RMI_PORT = 1099;

    /**
     * Tries to locate an existing RMI Registry.
     * If none is found, it creates a new one.
     *
     * @param port the port number (use 1099 by default)
     * @return the RMI Registry instance
     */
    public static Registry createOrGetRegistry(int port) {
        try {
            Registry registry = LocateRegistry.getRegistry(port);
            // Test if registry is alive (may throw an exception if not found)
            registry.list();
            System.out.println("[RegistryHelper] - Found existing RMI registry on port " + port);
            return registry;
        } catch (RemoteException e) {
            try {
                System.out.println("[RegistryHelper] - No existing registry found. Creating new one on port " + port);
                Registry newRegistry = LocateRegistry.createRegistry(port);
                System.out.println("[RegistryHelper] - New RMI registry created on port " + port);
                return newRegistry;
            } catch (RemoteException ex) {
                System.err.println("[RegistryHelper] - Failed to create RMI registry on port " + port);
                ex.printStackTrace();
                return null;
            }
        }
    }

    /**
     * Returns the default RMI Registry (port 1099).
     *
     * @return RMI Registry instance or null if failed
     */
    public static Registry getDefaultRegistry() {
        return createOrGetRegistry(DEFAULT_RMI_PORT);
    }

    /**
     * Gets a remote registry running on another host.
     *
     * @param host remote hostname or IP
     * @param port port number (usually 1099)
     * @return remote Registry reference
     */
    public static Registry getRemoteRegistry(String host, int port) {
        try {
            Registry registry = LocateRegistry.getRegistry(host, port);
            registry.list(); // test connection
            System.out.println("[RegistryHelper]- Connected to remote RMI registry at " + host + ":" + port);
            return registry;
        } catch (RemoteException e) {
            System.err.println("[RegistryHelper] - Unable to connect to remote RMI registry at " + host + ":" + port);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Simple test launcher for the RegistryHelper.
     * You can run this class standalone to verify the RMI setup.
     */
    public static void main(String[] args) {
        Registry registry = RegistryHelper.createOrGetRegistry(DEFAULT_RMI_PORT);
        if (registry != null) {
            System.out.println("[RegistryHelper] - Registry is active and ready to use.");
        } else {
            System.err.println("[RegistryHelper] - Registry initialization failed.");
        }
    }
}
  • peer/implementation/
    • RemotePeerImpl.java : Elle implémente l’interface RemotePeer. Elle sert les chunks demandés via RMI. Elle peut aussi recevoir des fichiers.
    • PeerMain.java: le point d’entrée pour un pair. On y initialise le service RMI local du pair. On se connecte à l’annuaire via RemoteRegistry.logon et on publie ses chunks sur le réseau.
  • peer/ui/ -PeerUI.java: Interface utilisateur (console ou graphique).
    • Permet :
      • de configurer le dossier partagé,
      • de chercher un fichier,
      • de suivre un téléchargement.
  • peer/utils/
    • FileUtils.java
      • Fonctions utilitaires :
        • Découpage des fichiers (splitFile)
        • Fusion des fichiers (mergeChunks)
        • Vérification d’intégrité (computeChecksum).
    • FileDownloader.java : Gère le téléchargement parallèle des chunks depuis plusieurs pairs et reconstitue le fichier localement après téléchargement.
  • model/
    • PeerInfo.java : Structure décrivant un pair enregistré.
        String name;
        String address;
        long lastHeartbeat;
      
    • FileChunkInfo.java : Structure décrivant un fragment d’un fichier :
        String fileName;
        int chunkIndex;
        String checksum;
        String peerOwner;
        long size;
      
  • docs/architecture.md Documentation fonctionnelle du projet :
    • But du système
    • Diagramme d’architecture (avec RMI Registry et Annuaire P2P)
    • Explication du cycle de vie (connexion, publication, recherche, transfert)