| <Index de l'aide> <Serveur> <Client> <Plugins> <Liste des Plugins> <Nos points forts> <Extensions envisagées> |
Fonctionnement de la partie ServeurLe centralise l'ensemble des fonctionnalités offertes aux clients.Il s'execute dans de multiples threads, et se base sur le serveur générique vu en TD: MonServeurUn serveur de tchat se crée de la manière suivante, en lui fournissant une classe de dialogue, un port d'écoute et un nombre maximum de connexions simultanées autorisées:
MonServeur serv = new MonServeur("serveur.MonDialogue", 6669, 250);
Il doit être prêt à tout moment à accepter un nouveau client qui se connecte. Il se résume donc en une simple boucle, qui attends un client, vérifie que celui-ci est bien autorisé à se connecter et instancie un nouveau dialogue pour le prendre en charge et ouvre une seconde socket pour qu'il puisse communiquer avec l'emetteur:
while(true) {
Dialogue dial = getUnClient(); dial.start(); }
protected synchronized Dialogue getUnClient() throws IOException, IllegalAccessException, InstantiationException {
//attente d'un client et acceptation de la connexion (dialogue) MonDialogue c = (MonDialogue) super.getUnClient(); //si le serveur est plein, attente de la libération d'une place while( isFull() ){ try{ wait(); } catch(InterruptedException e){System.out.println(e);} } //si le serveur est arrêté, attente de son redémarrage while(!actif){ try{ wait(); } catch(InterruptedException e){System.out.println(e);} } [...] //acceptation de la connexion du client (ecouteur) Emetteur.addClientTmp(getListenSocket().accept(), c); return c; } Lors de son démarrage, le serveur charge automatiquement la liste des utilisateurs enregistrés s'il la trouve. Lors de son arrêt, le serveur met à jour si besoin la liste des utilisateurs enregistrés dans le fichier, et et accorde un temps de grâce aux chatteurs présents, avant de les déconnecter. MonDialogueIl est propre à un client et se charge d'écouter les messages de celui-ci et d'y répondre.Il identifie tout d'abord le client, puis écoute le client:
while (true) {
//--------------------------------- // Attente d'un message du client //--------------------------------- line = ecouter(); [...traitement : voir ci-dessous...] //--------------------------------- // Envoi de la réponse //--------------------------------- out.writeObject(rep); } Selon qu'il s'agisse d'une commande ou d'un simple message texte, le dialogue réagit différemment:
EmetteurL' émetteur a pour rôle de retransmettre aux utilisateurs les messages qui sont ajoutés dans sa FifoPartagée par les MonDialogue.Bien que ceci ne soit pas demandé dans le sujet, nous avons défini un nombre de clients maximum pour chaque émetteur, afin de répartir la charge sur différents Threads et de permettre de meilleurs performances si le chat devenait très fréquenté. Tant que les clients ne sont pas identifiés, ils ne sont pas sensés recevoir les messages des autres usagers. C'est pourquoi, nous concervons leur socket dans une HashTable de classe temporaire (Les sockets sont indexées selon le dialogue qui gère le client) :
static void addClientTmp(Socket s, MonDialogue dial){
clientsTmp.put(dial,s); } Lorsque le client se connecte, il est alors assigné à un émetteur qui n'est pas au maximum de ses capacités. Sa socket est alors transférée de la HashTable temporaire vers la HashTable de l'émetteur choisi :
static void addConnecté(MonDialogue dial){
Emetteur e=null; //retire le client de la liste des clients non identifiés Socket s = (Socket)clientsTmp.get(dial); clientsTmp.remove(dial); //assignation du client à un Emetteur libre Enumeration en = lesEmetteurs.elements(); while( en.hasMoreElements() ){ e = (Emetteur)en.nextElement(); if( e.isFull() == false ) break; } //création d'un nouvel emetteur si besoin. if( e == null || e.isFull() ){ e = new Emetteur(); } //assignation du client à l'émetteur e.mesClients.put(dial, s); } Les emetteurs sont automatiquement détruits lorsqu'ils n'ont plus de clients... Note: La commande /chat map permet de visualiser à tout moment la répartition des clients sur les différents émetteurs. UserUn utilisateur est identifié par son login et son mot de passe.Pour que les plugins puissent stocker autant d'informations qu'ils le souhaitent sur chaque utilisateur, nous ne voulions pas créer une multitude d'attributs qui n'auraient peut-être pas été utilisés si le plugin Stat était désactivé. Nous avons opté pour une autre solution, bien plus évolutive: La classe User offre les methodes suivantes:
La liste des utilisateurs enregistrés est stockée dans la HashTable de classe registeredUsers, qui peut être elle-même sauvegardée et restituée dans un fichier USERFILE (par defaut Users.pwd, que l'on retrouvera dans C:\Program Files\IBM\VisualAge for Java\ide\project_resources\ SchedulerNotre Scheduler (programmateur) a été créé dans le souci de le rendre générique.Il est très simple d'utilisation et peut planifier les heures de fonctionnement de n'importe quel objet Plannifiable, c'est à dire qui implémente les methodes:
//création d'un serveur qui sera actif de 9h30 à 18h tous les jours
MonServeur serv = new MonServeur("serveur.MonDialogue", port, 5); new Scheduler( serv, "09:30:00", "18:00:00" ); |