0% ont trouvé ce document utile (0 vote)
3 vues6 pages

Cours l3 Java Serveur Multithread

Cet article décrit la création d'un serveur Java multithread capable de diffuser des messages à tous les clients connectés via des sockets. Il détaille les étapes de développement, y compris la structure des classes principales, la gestion des connexions clients, et les commandes administratives. Le serveur, nommé Blablaserveur, est conçu pour être multiplateforme et permet à plusieurs clients de communiquer simultanément.

Transféré par

juvenalxboom
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
3 vues6 pages

Cours l3 Java Serveur Multithread

Cet article décrit la création d'un serveur Java multithread capable de diffuser des messages à tous les clients connectés via des sockets. Il détaille les étapes de développement, y compris la structure des classes principales, la gestion des connexions clients, et les commandes administratives. Le serveur, nommé Blablaserveur, est conçu pour être multiplateforme et permet à plusieurs clients de communiquer simultanément.

Transféré par

juvenalxboom
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

ENI

Développement d'un serveur Java


multi-thread simple
article explique la réalisation pas à pas d'un
serveur multithread en java utilisant les sockets. Ce
serveur se chargera de broadcaster les messages
lui arrivant à tous les clients qui y sont connectés.

Introduction
1. Le fonctionnement
2. Le développement
3. La compilation
4. Premier démarrage

Introduction

Nous allons, dans cet article, tenter de comprendre


les mécanismes de base de la communication par
sockets via la construction d'un serveur java
multiclient. Ce serveur s'attachera à simplement On a ainsi l'impression que plusieurs actions sont
rediriger les flux arrivant d'un client connecté vers effectuées en parallèle.
tous les autres et ceci indépendemment du type du Ici, nous aurons trois types de thread :
client et du sytème l'hébergeant. En effet les - le thread principal : il va attendre les connexions
sockets sont une couche suffisamment bas niveau en écoutant le port ouvert pour le serveur. A
pour être inclus dans la plupart des systèmes et chaque connexion il va lancer un nouveau thread
pour être supportés par une majorité des langages de type client.
de programmation. - le thread client : il y en a donc un associé à
chaque client connecté, il est chargé d'attendre
Voici les fonctionnalités du serveur java que nous l'arrivée d'un message. A chaque fois, il enverra le
allons programmer, baptisé Blablaserveur : message reçu à tous les clients connectés.
- serveur en java, donc multiplateforme, - le thread des commandes : il attend que l'admin
- multithread, avec donc la possibilité d'accepter du serveur tape des commandes dans la console
plusieurs clients simultanément, pour faire une action (quitter le serveur par
- action du serveur : reçoit un message du client, exemple).
l'envoie alors à tous les autres connectés,
- possibilité d'interroger le serveur à travers des Et en pratique, on fait ça comment ?
commandes serveurs.
2. Le développement

Le langage choisi ici est le java, simplement parce


1. Le fonctionnement qu'on pourra lancer le serveur aussi facilement
sous Unix, que sous Windows ou que sous Mac (et
Voici un aperçu de l'architecture de l'application aussi parce que les sockets sont assez simples à
que nous allons développer : utiliser en java).

Nous allons créer 3 classes :


> BlablaServ, correspond au thread principal
Contient les méthodes suivantes :
- public static void main(String args[])
- static private void printWelcome(Integer port)
- synchronized public void sendAll(String
message,String sLast)
- synchronized public void delClient(int i)
1
ENI

- synchronized public int addClient(PrintWriter out) thread de gestion des commandes


- synchronized public int getNbClients()
ServerSocket ss = new
> BlablaThread, correspond au thread client ServerSocket([Link]()); // ouverture d'un
Contient les méthodes suivantes : socket serveur sur port
- BlablaThread(Socket s, BlablaServ blablaServ) printWelcome(port);
- public void run() while (true) // attente en boucle de connexion
(bloquant sur [Link])
> Commandes, correspond au thread des {
commandes new BlablaThread([Link](),blablaServ); //
Contient les méthodes suivantes : un client se connecte, un nouveau thread client est
- Commandes(BlablaServ blablaServ) lancé
- public void run() }
}
Créez un répertoire "blabla" et placez y trois catch (Exception e) { }
fichiers de type textes vides : [Link], }
[Link], [Link].
Dans les explications nous ne détaillerons pas trop //** Methode : affiche le message d'accueil **
le code, il semble suffisamment commenté pour static private void printWelcome(Integer port)
comprendre chaque ligne. {
[Link]("--------");
La classe BlablaServ [Link]("BlablaServeur : Par Minosis
- Minosis");
Mettez dans le fichier [Link], le code [Link]("Copyright : 2004 -
suivant : [Link]");
[Link] [Link]("Derniere version :
10/04/2004");
import [Link].*; [Link]("--------");
import [Link].*; [Link]("Demarre sur le port :
import [Link].*; "+[Link]());
[Link]("--------");
//** Classe principale du serveur, gère les infos [Link]("Quitter : tapez \"quit\"");
globales ** [Link]("Nombre de connectes :
public class BlablaServ tapez \"total\"");
{ [Link]("--------");
private Vector _tabClients = new Vector(); // }
contiendra tous les flux de sortie vers les clients
private int _nbClients=0; // nombre total de clients
connectés //** Methode : envoie le message à tous les
clients **
//** Methode : la première méthode exécutée, elle synchronized public void sendAll(String
attend les connections ** message,String sLast)
public static void main(String args[]) {
{ PrintWriter out; // declaration d'une variable
BlablaServ blablaServ = new BlablaServ(); // permettant l'envoi de texte vers le client
instance de la classe principale for (int i = 0; i < _tabClients.size(); i++) //
try parcours de la table des connectés
{ {
Integer port; out = (PrintWriter) _tabClients.elementAt(i); //
if([Link]<=0) port=new extraction de l'élément courant (type PrintWriter)
Integer("18000"); // si pas d'argument : port 18000 if (out != null) // sécurité, l'élément ne doit pas
par défaut être vide
else port = new Integer(args[0]); // sinon il {
s'agit du numéro de port passé en argument // ecriture du texte passé en paramètre
(et concaténation d'une string de fin de chaine si
new Commandes(blablaServ); // lance le besoin)

2
ENI

[Link](message+sLast); clients, ses méthodes de gestion du vecteur


[Link](); // envoi dans le flux de sortie _tabClients. On observe que ces méthodes sont
} "synchronised", cela signifie que chaque thread,
} voulant en exécuter une, devra attendre qu'un
} thread ait fini de l'utiliser. Ceci évite ainsi des accés
simultanés aux variables communes (_tabClients et
//** Methode : détruit le client no i ** _nbClients).
synchronized public void delClient(int i)
{ La classe BlablaThread
_nbClients--; // un client en moins ! snif
if (_tabClients.elementAt(i) != null) // l'élément Mettez dans le fichier [Link], le code
existe ... suivant :
{ [Link]
_tabClients.removeElementAt(i); // ... on le
supprime import [Link].*;
} import [Link].*;
}
//** Classe associée à chaque client **
//** Methode : ajoute un nouveau client dans la //** Il y aura autant d'instances de cette classe que
liste ** de clients connectés **
synchronized public int addClient(PrintWriter out) //implémentation de l'interface Runnable (une des
{ 2 méthodes pour créer un thread)
_nbClients++; // un client en plus ! ouaaaih class BlablaThread implements Runnable
_tabClients.addElement(out); // on ajoute le {
nouveau flux de sortie au tableau private Thread _t; // contiendra le thread du client
return _tabClients.size()-1; // on retourne le private Socket _s; // recevra le socket liant au
numéro du client ajouté (size-1) client
} private PrintWriter _out; // pour gestion du flux de
sortie
//** Methode : retourne le nombre de clients private BufferedReader _in; // pour gestion du
connectés ** flux d'entrée
synchronized public int getNbClients() private BlablaServ _blablaServ; // pour utilisation
{ des méthodes de la classe principale
return _nbClients; // retourne le nombre de private int _numClient=0; // contiendra le numéro
clients connectés de client géré par ce thread
}
} //** Constructeur : crée les éléments nécessaires
au dialogue avec le client **
La fonction statique main() est exécutée en premier BlablaThread(Socket s, BlablaServ blablaServ) //
au lancement de l'application. Les paramètres le param s est donnée dans BlablaServ par
passés au démarrage de l'application se trouvent [Link]()
stockés dans le tableau de strings args[]. Le seul {
argument que nous gérons est le numéro de port. _blablaServ=blablaServ; // passage de local en
Si l'argument est absent, alors le port sera 18000. global (pour gestion dans les autres méthodes)
Si le port est déjà pris par une autre application, ou _s=s; // passage de local en global
un problème survient lors de la création du try
ServerSoket, une exception est levée et un {
message d'erreur est affiché. // fabrication d'une variable permettant
Juste après l'instanciation du ServerSocket, un l'utilisation du flux de sortie avec des string
message d'accueil est lancé ainsi que le thread _out = new PrintWriter(_s.getOutputStream());
d'attente des commandes. // fabrication d'une variable permettant
Puis une boucle infinie attend les connexions des l'utilisation du flux d'entrée avec des string
clients. Dès qu'une requète arrive, [Link]() est _in = new BufferedReader(new
débloqué et une nouvelle instance de BlablaThread InputStreamReader(_s.getInputStream()));
est créée. L'objet BlablaServer est passé en // ajoute le flux de sortie dans la liste et
paramètre pour pouvoir utiliser, dans les threads récupération de son numero

3
ENI

_numClient = [Link](_out); }
} }
catch (IOException e){ } catch (Exception e){ }
finally // finally se produira le plus souvent lors
_t = new Thread(this); // instanciation du thread de la deconnexion du client
_t.start(); // demarrage du thread, la fonction {
run() est ici lancée try
} {
// on indique à la console la deconnexion
//** Methode : exécutée au lancement du thread du client
par [Link]() ** [Link]("Le client no
//** Elle attend les messages en provenance du "+_numClient+" s'est deconnecte");
serveur et les redirige ** _blablaServ.delClient(_numClient); // on
// cette méthode doit obligatoirement être supprime le client de la liste
implémentée à cause de l'interface Runnable _s.close(); // fermeture du socket si il ne l'a
public void run() pas déjà été (à cause de l'exception levée plus
{ haut)
String message = ""; // déclaration de la variable }
qui recevra les messages du client catch (IOException e){ }
// on indique dans la console la connection d'un }
nouveau client }
[Link]("Un nouveau client s'est }
connecte, no "+_numClient);
try Le socket créé par [Link]() est ici reçu en
{ paramètre, à partir de _s, on crée ainsi les buffers
// la lecture des données entrantes se fait gérant le flux d'entrée et le flux de sortie.
caractère par caractère ... Le thread est ensuite lancé (méthode run()). Une
// ... jusqu'à trouver un caractère de fin de attente en boucle des données a lieu. Dès qu'un
chaine caractère arrive en entrée, il est concaténé à la
char charCur[] = new char[1]; // déclaration chaine _message. Le message est envoyé dés
d'un tableau de char d'1 élement, _in.read() y qu'un caractère de fin de chaine est détecté. Si il
stockera le char lu s'agit de '\u0000', celui ci sera concaténé au
while(_in.read(charCur, 0, 1)!=-1) // attente en message lors de l'envoi (méthode sendAll()).. Au
boucle des messages provenant du client moins, BlablaServeur fonctionnera
(bloquant sur _in.read()) indépendamment du type de clients.
{ Enfin, aprés tout événement entraînant la fin de la
// on regarde si on arrive à la fin d'une boucle, le bloc finally{} sera exécuté pour mettre fin
chaine ... proprement à l'existence du thread client.
if (charCur[0] != '\u0000' && charCur[0] != '\n'
&& charCur[0] != '\r') La classe Commandes
message += charCur[0]; // ... si non, on
concatène le caractère dans le message Mettez dans le fichier [Link], le code
else if(![Link]("")) // suivant :
juste une vérification de principe [Link]
{
if(charCur[0]=='\u0000') // le dernier import [Link].*;
caractère était '\u0000' (char de terminaison nulle)
// on envoi le message en disant qu'il //** Classe qui gère les commandes tapées dans
faudra concaténer '\u0000' lors de l'envoi au client la console **
_blablaServ.sendAll(message,""+charCur[0 // implémentation de l'interface Runnable (une des
]); 2 méthodes pour créer un thread)
else _blablaServ.sendAll(message,""); // class Commandes implements Runnable
sinon on envoi le message à tous {
message = ""; // on vide la chaine de BlablaServ _blablaServ; // pour utilisation des
message pour qu'elle soit réutilisée méthodes de la classe principale
} BufferedReader _in; // pour gestion du flux

4
ENI

d'entrée (celui de la console) connectes : \"total\"");


String _strCommande=""; // contiendra la [Link]("--------");
commande tapée }
Thread _t; // contiendra le thread [Link](); // on affiche tout ce qui est
en attente dans le flux
//** Constructeur : initialise les variables }
nécessaires ** }
Commandes(BlablaServ blablaServ) catch (IOException e) {}
{ }
_blablaServ=blablaServ; // passage de local en }
global
// le flux d'entrée de la console sera géré plus
pratiquement dans un BufferedReader Cette classe n'est pas indispensable dans le
_in = new BufferedReader(new fonctionnement du serveur mais apporte la
InputStreamReader([Link])); possibilité d'une administration propre même si
_t = new Thread(this); // instanciation du thread minimaliste.
_t.start(); // demarrage du thread, la fonction La classe est instanciée dès le chargement du
run() est ici lancée serveur (méthode main() de BlablaServ). Elle crée
} dès le départ un buffer d'entrée à partir du flux
provenant du système (texte tapé dans la console
en général, dans une fenêtre Dos sous Windows
//** Methode : attend les commandes dans la ou le terminal sous Unix/Linux). L'activité principale
console et exécute l'action demandée ** (dans run()) est d'attendre les instructions et de les
public void run() // cette méthode doit exécuter. La méthode readLine() est ici plus
obligatoirement être implémentée à cause de adaptée et plus simple que le read() utilisé dans le
l'interface Runnable thread client. Bien sûr la méthode readLine() n'était
{ pas intéressante dans le thread client puisque le
try zéro terminal devait être géré.
{ Deux commandes sont implémentées : le "quit" qui
// si aucune commande n'est tapée, on ne fait appelle un simple [Link](0) et le "total" qui
rien (bloquant sur _in.readLine()) informe sur la quantité de clients connectés.
while ((_strCommande=_in.readLine())!=null)
{
if
(_strCommande.equalsIgnoreCase("quit")) //
commande "quit" detectée ... 3. La compilation
[Link](0); // ... on ferme alors le
serveur Le code est écrit, il reste maintenant à compiler nos
else .java et exécuter notre serveur.
if(_strCommande.equalsIgnoreCase("total")) // Ouvrez une console (Dos, xTerm, ... selon votre
commande "total" detectée ... OS). Placez vous dans le repertoire "blabla" et
{ faites "javac *.java".
// ... on affiche le nombre de clients
actuellement connectés
[Link]("Nombre de connectes :
4. Premier démarrage
"+_blablaServ.getNbClients());
[Link]("--------");
}
else Pour lancer le serveur sous n'importe quel
{ système, tapez en ligne de commande dans le
// si la commande n'est ni "total", ni "quit", on répertoire blabla : "java -jar [Link] 18000".
informe l'utilisateur et on lui donne une aide Logiquement à ce stade un message apparaît en
[Link]("Cette commande n'est indiquant que le serveur est démarré sur le port
pas supportee"); 18000 :
[Link]("Quitter : \"quit\"");
[Link]("Nombre de

5
ENI

On voit qu'en tapant le mot "total" (puis Entrer), le


nombre de connectés apparaît. Logiquement, pour
l'instant le nombre est 0.
Pour quitter proprement le serveur, veillez à taper
"quit".

Vous aimerez peut-être aussi