I6 - Programmation Réseau
Programmation Client/Serveur
Java & RMI
Dernière minute...
Une petite remarque onernant la politique de séurité en Java pour le lanement des appliations
serveuretliente.
Vousremarquerezquel'unedespremièreslignesexéutéesdanslaméthodemaindesesappliationsest
setSeurityManager(...).Cette instrutionimposeà laJVM d'installer ungestionnaire de séurité pour
limiterle hamp d'ation de vosappliations. Du oup, pour qu'elles puissent s'exéuter "normalement", il
estnéessaired'indiqueràegestionnairedeséuritéquevosappliationsserveuretlienteontlapermission
d'établirdesonnexionsréseauxpourdialoguer.C'estquandmêmel'objetifdeeTP...;-)D'oùlanéessité
du hier java.poliy (page 7) et de l'option -Djava.seurity.poliy=java.poliy de la ommande
java.
Sivousne souhaitezpasêtre onfrontés auxproblèmes depermissionsinhérentsà lamiseen plaed'un
gestionnairedeséuritéenJava,ilvoussutsimplement demettreenommentaire(ou retirer)l'instrution
setSeurityManager(...)danslaméthodemaindevosappliationsserveuretliente.Peut-êtreaurez-vous
l'oasionde revenirun jour suresproblèmes de permissionsen lieneASUR...
I6 - Programmation Réseau
Programmation Client/Serveur
Java & RMI
Résumé
L'objetif de e premier TP est d'assurer la transition entre la programmation d'appliations mo-
noposte (un programme s'exéutant sur une mahine) et la programmation d'appliations distribuées
(plusieurs programmess'exéutantsur des mahines diérentes et interonnetésviale réseau). Ce TP
vous permettra de vous familiariser ave le modèle lient/serveur puis de le mettre en pratique sans
voussouierdesproblèmes inhérentsàlaouheréseau(programmationdessoketsnotamment).Nous
utiliseronspourelalesRMI(RemoteMethod Invoation)dulangageJava 1
.
1 Compléments de Cours
En POO et en Java vous avez déjà vu de quelle manière un objet (lient) pouvait invoquer une
méthode sur unautreobjet (serveur). Ils'agitde laversionla plussimpledumodèle lient/serveur
oùlesdeuxobjetss'exéutentsurlamême mahine.Leméanismed'invoationdeméthodesàdistane
de Java(les Java RMI) permet à un programme s'exéutant sur un ordinateur (lient) d'eetuer des
appelsde méthodessur unobjet setrouvantsur unordinateur distant (serveur).LesRMI orentainsi
aux programmeursJavala possibilitéde distribuer lestâhes exéutées par leursprogrammes dans un
environnement réseau. En oneption orientée objet, l'objetif est que haque tâhe soit exéutée par
l'objetleplusappropriépourettâhe.LesRMIvontenoreplusloindansettedémarheenpermettant
l'exéutiond'unetâhesurl'ordinateurleplusappropriéàette tâhe.
L'arhiteture RMI dénit un ensemble d'interfaes spéiques destinées à réer et manipuler des
objets distants. Un lient peut ainsi invoquer des méthodes sur un objet distant en utilisant la même
syntaxe quepour l'invoation de méthodes surun objet loal.L'API des RMI fournit les lasseset les
méthodesquivontgérertoutequionernelaouheommuniationsous-jaenteetlesréférenessurles
paramètreslorsdesinvoationsdeméthodesàdistane.LesRMIs'oupentégalementdelasérialisation
desobjetspassésenargumentsdesméthodessurlesobjetsdistants.
1.1 L'arhiteture RMI
Les paquettages java.rmi et java.rmi.serverontiennent les interfaes et les lasses qui implé-
mententtoutes esfontionnalités de l'arhitetureRMI. Ils ontiennentles briquesde base néessaires
pourdévelopperlesobjetsdutéserveuret lesobjets stubs dutélient.Un stub 2
est une représen-
tation loale del'objet distant.Le lient eetue sesinvoations deméthodes sur e stub (objet loal),
lequel s'oupe automatiquement de ommuniquer ave la partie serveur. Cei se fait de manièretota-
lement transparente pourlelient.Du téserveur,'est leskeleton 3
qui reçoitles appelseetués par
1
LesRMI(Remote Method Invoation)sontsimilairesauxRPC(Remote Proedure Call)introduitsparSUNen1985. Les
RPCnéessitaienttoutefoisdesérialisermanuellementlesparamètresetlesvaleursderetourdesméthodes.SUNavaitd'ailleurs
développéunebibliothèqueàeteet:XDR(eXternalDataRepresentation). Unedesprinipales diérenesentrelesRMIet
lesRPCestquelesRMIsebasentsurleméanismedesérialisationoertparlelangageJava.LesRMIpermettentégalement
desérialiserlesexeptionspouvantêtrelevéesparuneméthodedistantepourlestransmettreauxprogrammeslients.
2
La tradutionfrançaisede"stub"est"talon",e quiestmoinsparlant.Duoup,vousrenontrerezlaplupartdutemple
motstub,mêmeenfrançais.
ore, que l'invoation soit loale oudistante est totalementtransparentpourle serveur. Leprinipe de
fontionnementpeutserésumerainsi(f.gure1):
•
L'objet lient ne ommuniquepas diretement ave l'objet serveur, mais ave un proxy loal : lestub.Celui-iestenhargede:
lasérialisationdesobjetspassésenparamètres(marshaling)
laonstrutiondelarequêteréseau
l'attentedurésultatset/oudesexeptions
•
Desonté,l'objetserveurest reliéauréseauviaunskeletondontlerleest de:désérialiserlesparamètres(unmarshaling)
d'invoquerlaméthodesurl'objetserveur
desérialiserlerésultatset/oulesexeptionset delesrenvoyeraulientvialestub
Client RMI
Stub
Serveur RMI
Skeleton 1
2
3 4
5
6 7
RZO RZO
Fig. 1 Arhiteture RMI
Commeelaest illustréparlagure1,lorsquequ'unobjetlientinvoqueune méthodesurunobjet
serveurdistant,etteinvoationdeméthodedistantesedérouleenplusieursétapes:
1. l'objetlientinvoque(normalement)laméthodesurlestub
2. lestubsérialiselesobjetspassésenparamètresdel'appel(marshaling)puisenvoielarequêtesurle
réseau
3. le skeleton reçoit la requête, désérialise les paramètres (unmarshaling), puis invoque la "vraie"
méthodesurl'objetserveur
4. l'objetserveurexéute(normalement)laméthode
5. l'objetserveurretournelerésultatet/oulesexeptionsàl'objetayantinvoquélaméthode,'est-à-
direleskeleton(etnondiretementàl'objetlient)
6. leskeletonsérialiselerésultatet/oulesexeptions,puisenvoieletoutvialeréseauaustubquiavait
eetué larequête
7. lestubreçoitlerésultatdel'invoation,désérialiselesdonnées,puislestransmetàl'objet,omme
si l'invoationavait étéréaliséeloalement
Lestub n'est donqu'un proxypermettant àl'objet lient d'aédervialeréseau, et e demanière
totalementtransparente,auxservies (méthodes) proposés parl'objetserveur. Pouronstruire estub,
seule l'interfae duserveur est néessaire.C'est dans ette interfaeque le serveurdélare tousles ser-
vies qu'il propose. Il n'est pas néessaire de onnaître son implémentation. Quand on programme en
lient/serveurilestdonindispensabledebiendistinguerl'interfaeetl'implémentationduserveur.
1.2 Un exemple RMI pas à pas
Cettesetionvavousguiderpasàpasdanslaoneption,laompilationetl'exéutiond'unexemple
d'appliationutilisantlesRMI. Pourréeruneappliationqui soitaessibleàdeslientsdistants,vous
1. Dénirlesinterfaesdeslassesdistantes.
2. Erire etompilerl'implémentationdeeslassesdistantes.
3. Créerleslassesstub etskeletonenutilisantlaommandermi.
4. Erire etompilerl'appliationserveur.
5. Démarrer leRMIregistryetl'appliationserveur.
6. Erire etompilerleprogrammelientaédantauxobjetsdistants.
7. Testerlelient.
Nous allons détailler notre exemple en suivant les étapes indiquées i-dessus. L'exemple utilisé est
relativementsimple.L'appliationserveuresthargéedegérerplusieursomptesbanairesetdepermettre
à des programmes lients d'eetuerdiérentes opérations de base sur es omptes. Cet exemplea été
simpliéaumaximumandeseonentreruniquementsurlesRMI.
1.2.1 Interfaes des lasses distantes
Lapremièrelassequenousérivonsestellereprésentantunomptebanaire.Lesprogrammeslients
ne manipulerontpas diretement les objetsde ette lasse. Ils devront le fairevia la banque. Orette
dernièren'eetueraque desinvoationsdeméthodesloales.La lasse"omptebanaire"neseradon
jamais utilisée àdistane. Nous pouvons donérire l'interfae Compteet lalasse CompteImplomme
d'habitude.
Compte.java
import java.lang.*;
interfae Compte {
String nom();
double solde();
void deposer(double montant);
void retirer(double montant);
}
CompteImpl.java
import java.lang.*;
publi lass CompteImpl implements Compte {
// Attributs
private String _nom = null;
private double _solde = 0.0;
// Construteurs
publi CompteImpl()
{
_nom = "unknown";
_solde = 0.0;
}
{
this();
_nom = n;
}
// Méthodes de l'interfae
publi String nom()
{
return _nom;
}
publi double solde()
{
return _solde;
}
publi void deposer(double montant)
{
_solde += montant;
}
publi void retirer(double montant)
{
_solde -= montant;
}
}
Quandunprogrammelientvoudraréaliseruneopérationsurunomptebanaire,ildevras'adresser
à la banqueen indiquant leompte qu'ilveut manipuler.C'est donsur ette lasse "banque"que les
lientinvoqueront,àdistane,desméthodes.Lorsdeladénitiondel'interfaeBanquenousdevonsdon
indiquer qu'ils'agitd'une interfaedontles méthodespourrontêtre invoquées àdistane. Ilsut pour
ela qu'elle étende l'interfae java.rmi.Remote et que toutes ses méthodes signalent qu'elles peuvent
éventuellement lever une exeption java.rmi.RemoteExeption lors d'une invoation à distane. Ces
deux onditionssontimposées parl'utilitaire rmiqui sehargera de réerleslasses stub dutédes
lients.Cesstubsimplémenterontetteinterfae.
Banque.java
import java.lang.*;
interfae Banque extends java.rmi.Remote {
boolean existe(String nom) throws java.rmi.RemoteExeption;
void ouvrir(String nom) throws java.rmi.RemoteExeption;
double solde(String ompte) throws java.rmi.RemoteExeption;
void deposer(String ompte, double montant) throws java.rmi.RemoteExeption;
void retirer(String ompte, double montant) throws java.rmi.RemoteExeption;
void transferer(String ompte1, String ompte2, double montant)
throws java.rmi.RemoteExeption;
}
Nous pouvons maintenant érire l'implémentation de nos objets serveur. La lasse BanqueImplim-
plémentetoutnaturellementl'interfaeBanquedéniepréédemment.Commevouspouvezleremarquer,
nous indiquonsque la lasse BanqueImpl étendla lasse UniastRemoteObjet,e qui signie que les
objets intaniés à partir de la lasse BanqueImplne pourront traiter qu'une seule requête àla fois. Si
plusieurslientstententd'aéderenmêmetempsàundeesobjetsdistants,ilsserontmisdansunele
d'attente et nereevrontlaréféreneàetobjetdistantqu'unefoisette référenerelâhéeparlelient
préédent.
BanqueImpl.java
import java.lang.*;
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
// La lasse UniastRemoteObjet indique que l'objet
// distant sera "uniast", i.e. qu'il ne pourra répondre
// qu'à une seule requête à la fois.
publi lass BanqueImpl extends UniastRemoteObjet implements Banque {
// Attributs
private Hashtable _omptes = null;
// Construteurs
publi BanqueImpl() throws java.rmi.RemoteExeption
{
_omptes = new Hashtable();
}
// Méthodes de l'interfae
publi boolean existe(String nom) throws java.rmi.RemoteExeption
{
return ( (Compte)_omptes.get(nom) != null );
}
publi void ouvrir(String nom) throws java.rmi.RemoteExeption
{
_omptes.put(nom,new CompteImpl(nom));
}
publi double solde(String ompte) throws java.rmi.RemoteExeption
{
return ( (Compte)_omptes.get(ompte) ).solde();
}
publi void deposer(String ompte, double montant) throws java.rmi.RemoteExeption
{
( (Compte)_omptes.get(ompte) ).deposer(montant);
}
{
( (Compte)_omptes.get(ompte) ).retirer(montant);
}
publi void transferer(String ompte1, String ompte2, double montant)
throws java.rmi.RemoteExeption
{
( (Compte)_omptes.get(ompte1) ).retirer(montant);
( (Compte)_omptes.get(ompte2) ).deposer(montant);
}
}
LalasseBanqueImplestresponsable delaréationetdustokaged'objetsCompte.Elleutilise pour
elaune tabledehashagedontlalé estlenomduompte.
1.2.3 Création des lasses stub et skeleton
Unefoisquel'imlémentationdeslassesdistantessontompilées,l'étapesuivanteestlagénérationdes
lasses stub et skeleton permettant d'aéderàdistane àes distane. Les lassesstub serontutilisées
parleodedulientpourommuniqueraveleodeduskeletonduserveuronerné.
La ommande rmi permet de réer automatiquement le ode des stubs et skeletons à partir de
l'interfaeetdel'implémentationdeslassesdesserveurs.Parexemple,pourgénérerlestubetleskeleton
denotrelassedistanteBanque,ilfautexéuter:
rmi BanqueImpl
Cetteommandermiréedanslerépertoiredeuxnouveauxhiersdelasses,BanqueImpl_Skel.lass
etBanqueImpl_Stub.lass,orrespondantrespetivementauskeletonetaustubdelalasseBanqueImpl.
Les stubset lesskeletonsétantréés,l'étapesuivante onsisteàérirel'appliationserveur mettantes
lassesàdisposition deslientsdésirantyinvoquerdesméthodesàdistane.
1.2.4 Création et ompilation de l'appliation serveur
Toutest maintenantenplae pourledéveloppementde l'appliationtéserveur.Nous allonspour
elaréerunelasseServeurdontleseultravailserad'instanierunobjetàpartirdelalasseBanqueImpl
puisdelepublierdansleRMIregistryanqu'ilsoit aessibleparleslients.
Pardéfaut, lesappliations s'exéutentsansseuritymanager.L'appelsetSeurityManagerimpose
l'utilisationd'unRMIseuritymanager.Nousn'ironspasplusloinsurettenotion.Sahezsimplementque
eméanismepermet deséuriserlesonnexionslient/serveurenvériantnotammentquelesinterfaes
utiliséesparlelientsontbien ellespubliées parleserveur.Ceméanismepermetégalementl'envoide
esinterfaesàl'appliationlientesinéessaire.
Leserveurpublieensuitel'objetinstaniéenl'enregistrantdansleRMIregistrysousunertainnom.
Nousdisposonspoureladedeuxméthodes:bindetrebind.Cesdeuxméthodesfontionnentdemanière
identique,saufquandlenomestdéjàliéàunautreobjet.Danseas,laméthodebindlèveuneexeption
AlreadyBoundExeptionalorsque laméthode rebinddéréférenel'anien objet et fore le lien versle
nouvelobjet.Danslesdeuxas,lenomutiliséestunehaînedearatèresdelaformed'uneURL,'est-
à-dire protool ://host :port/bindingName.Dans leas présent, protoolorrespondà rmi, host
est le nom de la mahine sur laquelles'exéute le serveur RMI, port est le numéro de port sur lequel
le serveur est à l'éoute, et bindingName orrespond au nom exat qui sera utilisé par leslients pour
obtenirl'aèsauserveur.SiseullebindingNameestfourni,lesvaleurspardéfautsontutiliséespourles
autresparamètres,àsavoir:rmipourprotool,loalhostpourhostet 1099pourport.
import java.rmi.*;
publi lass Serveur {
publi stati void main(String args[℄)
{
// Création et installation d'un "seurity manager".
System.setSeurityManager(new RMISeurityManager());
try {
// Création d'une instane de notre lasse Banque
Banque bank = new BanqueImpl();
System.out.println("Banque réée");
// "Bind" de ette instane dans le RMI registry
Naming.rebind("banqueGTR",bank);
System.out.println("Banque enregistrée dans le RMI registry");
System.out.println("Le serveur est prêt...");
}
ath (Exeption e)
{
System.out.println("Il y a eu une erreur !!!");
e.printStakTrae();
System.out.println(e.getMessage());
}
}
}
1.2.5 Lanement du RMI registry et de l'appliation serveur
LeRMI registryest unprogramme qui permet defairela orrespondaneentre unnom symbolique
et uneréféreneversunobjetdistant(unobjetserveur).Commenousl'avonsvu préédemment,l'enre-
gistrementd'unnouvelobjet sefaitvialesméthodesbindet rebind.Lesappliations lientes pourront
alorsseonneterauRMIregistryetyfaireunlookupqui,àpartird'unnomsymbolique,instanieraun
stubet unskeletonpourleserveurindiqué,lesmettraenontat,puisretourneraaulientuneréférene
verslestub.
Les deux lignes de ommandes suivantes permettent d'intaller le RMI registrypuis de démarrer le
serveurbanaire.Pourdesraisonsde simpliation,nousnous abstiendronsdetéléhargerlesinterfaes
viaHTTP.Duoup,ilest néessaired'exéuterleRMIregistrydanslerépertoireoùsetrouventtoutes
leslassesquevousavezérites,demanièreàequ'ilpuisseyavoiraès.
rmiregistry
java -Djava.seurity.poliy=java.poliy Serveur
Commementionnépréédemment,nousnenousouponspaspourlemomentdesproblèmesdeséu-
ritéentreleserveuretlelient.C'estpouretteraisonquelapolitiquedeséuritéuiliséeestextrêmenent
simpleetpermissivepuisqu'elleautorisetouteslesonnexionsauxportssituésentre1024et65535,ainsi
qu'auport80(protooleHTTP).Cettepolitiqueestenregistréedanslehierjava.poliy.
grant {
permission java.net.SoketPermission "*:1024-65535", "onnet,aept";
permission java.net.SoketPermission "*:80", "onnet";
};
devriez obtenirei:
Banque réée
Banque enregistrée dans le RMI registry
Le serveur est prêt...
Anoter quevouspouvezdémarrer lermiregistrysur unportdiérent, equi est utilesiplusieurs
rmiregistrydoivent tourner simultanément sur lamême mahine. Ilsut pourela d'indiquer enpa-
ramètre le numéro du port (exemple : rmiregistry 2048). Ensuite, il vous faudra bien évidemment
signaler aux programmes serveurs et lientsqu'ils doivent utiliser e numéro de port pour ontater le
rmiregistryetnonleportpardéfaut(1099).
1.2.6 Création et ompilation du programme lient
Lorsque l'appliation liente démarre, elle doit trouver un objet Banque sur le serveur distant. Ce
programme suppose que le nom de la mahine sur laquelle s'exéute l'appliation serveur est passé
en premier argument sur la ligne de ommandes. Ce nom est utilisé pour réer une URL de la forme
rmi ://<hostname>/banqueGTR.CetteURLest passéeàlaméthode lookupinvoquéestatiquementsur
la lasse Naming.Comme indiquépréédemment, ette méthode renvoie au programme lient une réfé-
reneverslestub quisehargeradeommuniquer ave l'objetserveur.A noterqueletypederetourde
laméthodelookupestRemotequi estl'interfaemèredetouteslesinterfaesstubs.
Le programme lient utilise le seond argumentde sa ligne de ommandes omme nom de ompte
banaire.Ilontatealorsleserveurbanairepoureetuerplusieursopérationssureompte.
Client.java
import java.rmi.*;
publi lass Client {
publi stati void main(String args[℄)
{
Banque bank = null;
// Analyse des paramètres de la ligne de ommande
if (args.length < 2)
{
System.err.println("Usage:");
System.err.println("java Client <serveur> <nom ompte>");
System.exit(1);
}
// Création et installation d'un "seurity manager".
System.setSeurityManager(new RMISeurityManager());
// Obtention d'une référene de la banque (le serveur)
try {
String url = new String("//"+args[0℄+"/banqueGTR");
System.out.println("Client: lookup serveur, url = "+url);
bank = (Banque)Naming.lookup(url);
}
ath (Exeption e)
{
System.out.println("Erreur dans l'obtention du serveur"+e);
}
try {
// Si le ompte n'existe pas, on l'ouvre
if (!bank.existe(args[1℄))
{
bank.ouvrir(args[1℄);
System.out.println("Compte \""+args[1℄+"\" réé");
}
System.out.println("Le solde du ompte \""+args[1℄+"\" est de "+
bank.solde(args[1℄)+" FF");
bank.deposer(args[1℄,5000);
System.out.println("Ajout de 5000 FF sur le ompte \""+args[1℄+"\"");
System.out.println("Le solde du ompte \""+args[1℄+"\" est de "+
bank.solde(args[1℄)+" FF");
bank.retirer(args[1℄,400);
System.out.println("Retrait de 400 FF depuis le ompte \""+args[1℄+"\"");
System.out.println("Le solde du ompte \""+args[1℄+"\" est de "+
bank.solde(args[1℄)+" FF");
}
ath (Exeption e)
{
System.out.println("Erreur de transation pour "+args[1℄);
}
System.exit(0);
}
}
1.2.7 Test du programme lient
Ladernièreétapedenotreexempleonsisteàtesterlebonfontionnementdenotreprogrammelient
(etdonégalementdenotreappliationserveur).Ceprogrammepeutêtreexéutédepuisn'importequelle
mahineayantaèsauserveuretauxlassesnéessaires(nousutilisonspourlemomentunRMIseurity
managerminimal).Voiiunexempled'exéutiondeeprogrammeaveleserveurdistanttournantsurla
mahineurizen(lapremièreligneorrespondàlaommandeexéutée):
munierhryseis:remote > java -Djava.seurity.poliy=java.poliy Client urizen munier
Client: lookup serveur, url = //urizen/banqueGTR
Compte "munier" réé
Le solde du ompte "munier" est de 0.0 FF
Ajout de 5000 FF sur le ompte "munier"
Le solde du ompte "munier" est de 5000.0 FF
Retrait de 400 FF depuis le ompte "munier"
Le solde du ompte "munier" est de 4600.0 FF
Unefoisqueleprogrammelientaterminésonexéution,lesobjetsdistantsexistenttoujourssurle
serveur. L'exéution i-dessusa réé le ompte banaire pour l'utilisateur munier.Si nous exéutonsà
nouveaueprogrammeavelesmêmesparamètres,leprogrammevautiliserleompteexistant.Latrae
i-dessous montrebien quelesopérationsontété exéutéessur leomptedans l'étatoùil étaitaprèsla
premièreexéutionduprogrammelient.
Client: lookup serveur, url = //urizen/banqueGTR
Le solde du ompte "munier" est de 4600.0 FF
Ajout de 5000 FF sur le ompte "munier"
Le solde du ompte "munier" est de 9600.0 FF
Retrait de 400 FF depuis le ompte "munier"
Le solde du ompte "munier" est de 9200.0 FF
1.3 Sérialisation des paramètres
Dans l'exempleque nousvenonsde développer,lesparamètrestransmis lorsdesinvoationsde mé-
thodesàdistanesontdetypesrelativementsimples:double,String,...Imaginezmaintenantquevous
voulieztransmettredesparamètresplusompliquéstelsquedesobjetsinstanesdelassesquevousavez
dénies. Prenonsparexemplesleslassesreprésentantdesexpressionsnumériques(f.gure2).
Expression
double evaluer() String notationInfixee() String notationPostfixee()
Constante
double valeur
Fonction
Plus
Moins
Multiplier
Diviser
PlusU
MoinsU OperateurBinaire
Expression gauche Expression droite String opName()
OperateurUnaire
Expression expr
String opName() ?
Fig. 2 Graphe de lasses desexpressionsnumériques
Le problème est que pour pouvoir passer de tels objets omplexes en paramètres (nous parlons ii
d'arbres binaires dont les n÷uds peuventêtre de lasses diérentes), il est néessaire d'être apable de
lessérialiserdemanièreàlesdéouperenpaquetsauniveaudelaouheréseau,puisdereonstruirees
paramètressur lamahinedistante! Essayez delefairemanuellementpourvoir...et vousomprendrez
queen'estpassisimple;-)
Heureusement,Javaproposeenstandardunméanismedesérialisationautomatiquedeslasses.Ilvous
sutsimplementd'indiquerquel'interfaemèredevosparamètresétendl'interfaejava.io.Serializable
etletourestjoué.Parexemple:interfae Expression extends java.io.Serializable {...} Vous
pouvez bienévidemmentsurhargerlesméthodeswriteObjetetreadObjetutiliséeslorsdelasériali-
sation, maisellesdisponiblesenstandardsonttrèssatisfaisantes.
Ilestmaintenanttempspourvousdemettretoutesesnotionsenpratique.Voiidondeuxexeries
(distributeur de tikets,serveur DNS) qui, d'un point de vue algorithmique sont relativement simples,
maisquivouspermettrontjustementden'êtreonfrontésqu'àdesproblèmesd'invoationdeméthodessur
desobjetsdistants(parrapportauxinvoationsdeméthodes surdesobjetsloauxqui n'ontquasiment
plusauunseretpourvous).
2.1 Distributeur de tikets
Pour e premier exerie, il serait diile de faire plus simple. Il s'agit de réaliser un serveur ne
disposant qued'une seuleméthode : int prendreTiket().C'est le typede "serveur" que l'ontrouve
dansertainssupermarhésquandvousdevezfairelaqueuepourêtreservi(e):ildonneuntiketdiérent
àhaquefoisqu'onlesolliite.Ceserveurserainitialisélorsdesamiseenservie,puisdistribueraletiket
n
o 1
aupremierlient,letiket no 2
ausuivant,etainsidesuite.Outrel'interfaeDistributeuretlalasseDistributeurImpl,vousdevrezégalementérireunpro-
gramme permettant de réer un nouveau serveur et de l'enregistrer dans le RMI registry ainsi qu'un
programmelientquiseonneteraàeserveurpourprendreuntiket.Ilvousfaudrabienévidemment
vérierquevousobtenezunnumérodetiketdiérentàhaqueappel.
2.2 Serveur DNS
Dans et exerie, onse propose pourela de réaliserl'interfaepuis l'implémentation d'un serveur
DNS (notéesrespetivementDNSetDNSImpl). Ceserveurpermettradefairelaorrespondaneentreun
nom demahine (uneString) etune adresseIP (pourlemoment, uneStringégalement).Ilproposeà
eteetdeuxservies:
Laméthodevoid enregistrer(String, String)permetaulientd'ajouterunenouvelleassoia-
tionnomde mahine/adresseIP dansleDNS.Lepremierparamètreest l'adresseIP,leseond est
lenom delamahine.
La méthode String reherher(String) reherhe l'adresse IP d'une mahine à partir de son
nom.
2.2.1 Objets loaux
Cettepremièrepartieestfaultative.Elleonsisteàérirel'interfaeDNS,lalasseDNSImplainsiqu'un
programme detest enonsidérantque touslesobjetss'exéutent surlamême mahine. Cettepremière
étapeorrespondàlaprogrammationorientéeobjetslassique.
2.2.2 Objets distants
Dans ette seonde partie vousallez testervos(nouvelles) ompétenes en programmation orientée
objetsdistribués.Ils'agitdemodierlesinterfaesetleslasseséritesdanslapremièreétapedemanière
à e que le serveur DNS puisse être utilisé par des programmes lients s'exéutant sur des mahines
diérentes.End'autrestermes,ilvousestdemandédemettreen÷uvreetdetesterlesméanismesRMI
pourlesméthodesenregistreret reherherduserveurDNS.
2.2.3 Objets distants & sérialisation
PouronlureetexeriesurlesRMI,nousvoulonshangerlareprésentationdesadressesIP.Aulieu
de stokersousformedehaînesdearatèresnous allonsdénirune interfaeAdresseIPet unelasse
AdresseIPImplpermettant dereprésenter une adresseIP sous laformed'un tableau dequatre entiers,
ave bien évidemmentlesméthodes adéquates.Lessignaturesdes méthodesduDNS doiventégalement
êtremodiées:
void enregistrer(AdresseIP, String)
AdresseIP reherher(String)
Maintenant quevousavez bien ompris leméanisme des RMI, nousallons passerà unexerie où
les invoations de méthodes à distane entre le "lient" et le "serveur" s'enhaînent les unes après les
autres. Vous allez pour ela développerun serveur hat auquel pourront se onneter plusieurs lients
pourdisuter.Le prinipedefontionnementest lesuivant:une fois leserveurdémarré,unlient peut
prendre partà la disussion en s'enregistrant sur le serveur, ei an de signaler saprésene. Ensuite,
lorsqu'un lientveutémettreunmessage,il l'envoieauserveur.A réeptiondee message,leserveurse
hargedeleretransmettreàtousleslientsquisesontenregistrés,quis'oupentalorsdel'ahersurla
onsoledel'utilisateur.Anquevouspuissiezutiliservotreprogrammelientaveleprogrammeserveur
d'un autre binme, le mieux est que vous vous basieztous sur lesmêmes interfaes(et e au aratère
prêt,yomprislesmajusules/minusules!).
ChatRoom.java
publi interfae ChatRoom extends java.rmi.Remote {
void enregistrerClient(ChatClient lient) throws java.rmi.RemoteExeption;
void envoyerMessage(Message msg) throws java.rmi.RemoteExeption;
}
ChatClient.java
import java.lang.*;
publi interfae ChatClient extends java.rmi.Remote {
String nom() throws java.rmi.RemoteExeption;
void affiherMessage(Message msg) throws java.rmi.RemoteExeption;
}
Message.java
import java.lang.*;
publi interfae Message extends java.io.Serializable {
String origine();
String toString();
}
Parlonsunpeuplusendétailsdelamanièredonttouteivafontionner.Eneet,ommevousallez
leonstaterrapidement,iln'existeplusdansetexeriede"lient"oude"serveur".Lesobjetsquenous
allonsutiliservontêtretouràtourlientouserveur;-)
1. Ondémarre,ommed'habitude,leRMIregistry.
2. On lane l'appliationserveurqui instanie unobjetChatRoom 4
puis l'enregistre,toujoursomme
d'habitude,dansleRMIregistry.
3. Onpeutalorsdémarreruneappliationlientesuruneautremahine. Celle-iseonneteauRMI
registrypourréupéreruneréféreneversl'objetChatRoom.Jusquelà,iln'yariendenouveaupar
rapportauxexeriespréédents.
4. C'estmaintenantqueçahangeunpeu.L'appliationlientepeutalorsinstanierunobjetChatClient.
Elle eetueensuiteune RMIpourinvoquerlaméthodeenregistrerClientsurl'objetChatRoom
enluipassantommeargumentlaréféreneversl'objetChatClientqu'ellevientderéer.
5. L'objetChatRoomstokeetteréféreneversl'objetChatClientdansundesesattributs(unVetor
parexemple).
6. L'appliationlienteentrealorsdansunboulequilitunehaînedearatèresaulavier,onstruit
unobjetMessage enpréisantsonnomomme origine,puisenvoie emessageàl'objetChatRoom
viauneRMIsurlaméthodeenvoyerMessage.
4
Ouplusexatementunobjetissud'unelasseimplémentantl'interfaeChatRoom.
objets ChatClient enregistrés et, pour haun d'entre eux, eetue à son tourune RMI sur leur
méthodeaffiherMessageenutilisanttoutsimplementlaréféreneChatClientstokée.Illuiest
inutiled'interrogerleRMIregistrypourfaireunlookuppuisquel'objetChatRoompossèdedéjàune
référeneversl'objetChatClientqu'ilveutatteindre(f.méthodeenregistrerClient).
8. Chaque objet ChatClientexéutera donsa méthode affiherMessagequi aura pour eet d'af-
herl'origineet leontenudumessagesurl'éran delamahinesur laquelle tournel'appliation
lienteorrespondante.
Dans e sénario, vous pouvez eetivement onstater qu'à l'étape 6, 'est l'objet ChatClient qui
eetueuneRMIversl'objetChatRoompourinvoquersaméthodeenvoyerMessage.L'objetChatClient
joue le rle de lient et l'objet ChatRoom joue le rle de serveur. Par ontre, à l'étape 7, 'est l'objet
ChatRoom qui eetue une RMI vers l'objet ChatClient pour invoquersa méthode affiherMessage.
Danse as,l'objetChatRoomjouelerledelientetl'objetChatClientjouelerledeserveur!
Uneonséquenedeeisetqu'ilvousseranéessairedegénérerlesstubsetlesskeletonsnonseulement
pourleslassesimplémentant l'interfaeChatRoom,mais égalementpourelles implémentantl'interfae
ChatClient,demanièreàequel'objetsChatRoompuisseinvoquerlaméthodeaffiherMessagesurdes
objetsChatClientdistants.