Algo. Distribuée - Thread, Socket et RMI - coli.uni

publicité
École Nationale Supérieure
des Sciences
Appliquées et
de Technologie
Algo. Distribuée
Thread, Socket et RMI
1 / 47
Avant-propos
Découpage du module :
Une partie théorique
Une partie techno. : Thread, socket + RMI
Organisation
6h = CM/TP présentation des notions
10h = projet
2 / 47
Première partie
Threads
3 / 47
Généralités
Utilité des Threads : voir cours de prog. syst. en IMR1
Threads en Java
Les méthodes/variables de classes sont connues de
chacun des Threads (partage d’information)
L’implémentation d’un Thread doit :
hériter de la classe Thread
ou implémenter l’interface Runnable
définir le contenu de la méthode public void run()
4 / 47
Lancement d’un Thread
ATTENTION !
Un Thread ne sera PAS exécuté lors de l’appel de la
méthode run()
Un Thread sera exécuté uniquement après l’appel de la
méthode start() (implémentée dans la classe Thread)
5 / 47
Exemple de Threads
class ThreadDormeur extends Thread {
public ThreadDormeur() {
super();
}
public void run(){
System.out.println("Dans ThreadDormeur");
}
}
public class HelloThread {
public static void main(String arg[]) {
ThreadDormeur dormeur = new ThreadDormeur();
dormeur. start() ;
System.out.println("Dans HelloThread");
}
}
6 / 47
Exécution
$ javac HelloThread.java ThreadDormeur.java
$ java -cp . HelloThread
Dans HelloThread
Dans ThreadDormeur
$
7 / 47
Implémentation de Runnable
Une classe de Thread peut également se contenter
d’implémenter l’interface Runnable
Il faut alors utiliser le constructeur de la classe Thread
class RunnableThread implements Runnable {
RunnableThread() {
...
}
public void run() {
...
}
}
RunnableThread p = new RunnableThread();
Thread t = new Thread(p) ;
t.start() ;
8 / 47
Contrôle de l’exécution
Les outils de contrôle de l’exécution des Threads sont
sleep(int n_ms) : le Thread est bloqué pendant n_ms
millisecondes
isAlive() : indique par un booléen si le Thread est vivant
ou non (démarré par start et run non fini)
getPriority() et setPriority(int prio) :
indication sur la priorité du Thread
join() : permet d’attendre la fin de l’exécution du Thread
wait(), notify() et notifyAll() : permet de mettre
en attente un Thread ou de réveiller un Thread en attente
9 / 47
Exemple : un métronome
Les méthodes permettant les rendez-vous doivent se faire
sur un objet partagé par les Threads
class Alarme {
public void reveille() {
notify();
}
public void dors(){
try{
wait();
}
catch(Exception e){}
}
}
10 / 47
class ThreadDormeur extends Thread {
private Alarme alarme;
private int sec;
public ThreadDormeur(Alarme alarme, int N) {
this.alarme = alarme;
this.sec = N;
}
public void run(){
System.out.println("Debut du compte");
while(sec!=0) {
try {
sleep(1000);
}
catch(Exception e){}
alarme.reveille();
sec = sec-1;
}
System.out.println("Compte termine");
}
}
11 / 47
class ThreadAfficheur extends Thread {
private Alarme alarme;
private Thread threadAAttendre;
public ThreadAfficheur(Alarme alarme, Thread threadAAttendre) {
this.alarme = alarme;
this.threadAAttendre = threadAAttendre;
}
public void run(){
while(threadAAttendre.isAlive()) {
alarme.dors();
System.out.println("1 seconde ecoulee...");
}
}
}
12 / 47
public class Metronome {
public static void main(String arg[]) {
Alarme alarme=new Alarme();
ThreadDormeur dormeur = new ThreadDormeur(alarme, 5);
ThreadAfficheur afficheur=new ThreadAfficheur(alarme, dormeur)
dormeur.start();
afficheur.start();
try {
dormeur.join();
afficheur.join();
} catch (InterruptedException e) {
System.out.println("Probleme d’attente : ");
e.printStackTrace();
}
System.out.println("Execution terminee...");
}
}
13 / 47
Exécution de l’exemple : problème !
$ javac Metronome.java Alarme.java \
ThreadDormeur.java ThreadAfficheur.java
$ java -cp . Metronome
.....
que ce passe-t-il ?
14 / 47
Raison et correction : prise en
compte de l’exécution concurrente
class Alarme {
public synchronized void reveille() {
notify();
}
public synchronized void dors(){
try{
wait();
}
catch(Exception e){}
}
}
15 / 47
Accès concurrents
Pour limiter l’accès à une section critique à un seul thread :
utilisation du mot clé synchronized
valable pour un bloc d’instruction
valable pour une méthode
16 / 47
Deuxième partie
Socket en JAVA
17 / 47
Généralités
La communication réseau avec des socket passe par :
Un serveur qui ouvre un port et se met en attente de
message (flux d’octets)
Un client qui démarre une connexion sur le port ouvert par
le serveur
Quand la communication est terminée, le flux doit être
fermé
18 / 47
Un exemple de serveur
import java.io.*;
import java.net.*;
public class Serveur {
static final int port = 1095;
public static void main(String[] args)
throws IOException, ClassNotFoundException
{
ServerSocket s = new ServerSocket(port);
Socket socket = s.accept();
ObjectInputStream in =
new ObjectInputStream( socket .getInputStream());
MyObject oserver = (MyObject) in.readObject();
System.out.println("recu = " + oserver.toString());
socket.close();
}
}
19 / 47
Un exemple de client
import java.io.*;
import java.net.*;
public class Client {
static final int port = 1095;
static final String host = "127.0.0.1";
public static void main(String[] args)
throws UnknownHostException, IOException
{
Socket socket = new Socket(host, port);
ObjectOutputStream out =
new ObjectOutputStream(socket.getOutputStream());
MyObject o = new MyObject();
out.writeObject(o);
socket.close();
}
}
20 / 47
Petite remarque....
public class MyObject implements Serializable
{
public String toString()
{
return "yahooo";
}
}
21 / 47
Lecture et écriture
Dans le cadre de l’utilisation de chaı̂nes de caractères
utilisation de
InputStreamReader/OutputStreamWriter
Utilisation de BufferedReader/Writer
c http://recursor.blogspot.com/
Lecture/écriture
utilisation de la méthode readline/write
NE PAS OUBLIER LE flush APRES LE write
22 / 47
Application
Transformer l’exemple présenté dans la partie “Thread”
afin que le processus dormeur et le processus afficheur
puissent petre sur des machines distinctes
23 / 47
Troisième partie
RMI
24 / 47
Utilité des objets distants
Défauts des sockets :
Conception du protocole lourde (exemple ”RFC sur les
Ftp” : http://www.faqs.org/rfcs/rfc959.html)
Source de confusion sur le nombre et le type des
paramètres à envoyer
Faibles performances dues aux codages/décodages
nécessaire
Comparaison socket/rmi :
http://java.sun.com/developer/
technicalArticles/ALT/sockets/
⇒ RMI : permet d’utiliser directement
des objets
25 / 47
Fonctionnement de RMI
c http://www.sce.carleton.ca/courses/94580/
Java-RMI.html
26 / 47
Conception d’un projet RMI
c http://www.sce.carleton.ca/courses/94580/
Java-RMI.html
27 / 47
Exemple d’interface
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloInterface extends Remote
{
public String say() throws RemoteException ;
}
28 / 47
Implémentation de l’interface
import java.rmi.RemoteException;
public class HelloObject implements HelloInterface
{
private String message;
public HelloObject(String msg) throws RemoteException
{
message = msg;
}
public String say() throws RemoteException
{
return message;
}
}
29 / 47
Écriture du serveur
Le code du serveur doit :
1
2
définir une politique de sécurité
enregistrer l’objet distant
récupération dynamique du stub (type de l’interface)
enregistrement du stub sur le rmiregistry
30 / 47
Déclaration d’une politique de
sécurité
if (System.getSecurityManager() == null)
{
System.setSecurityManager(new SecurityManager());
}
31 / 47
Récupération du stub
HelloObject helloObject =
new HelloObject("Action executee par le serveur");
HelloInterface stub = (HelloInterface)
UnicastRemoteObject.exportObject(helloObject, 0 );
32 / 47
Enregistrement de l’objet créé
String name = " objet distant ";
Registry registry = LocateRegistry.getRegistry() ;
registry.rebind(name, stub);
33 / 47
Écriture de la classe serveur
import ...
public class HelloServer {
public static void main(String[] args) {
// definition de la politique de securite
try
{
// recuperation du stub de l’objet HelloObject
// enregistrement de l’objet sur le rmiregistry
System.out.println("Serveur OK");
}
catch (Exception e)
{
System.out.println("Serveur KO : ");
e.printStackTrace();
}
}
}
34 / 47
Classe Naming
L’objet peut être obtenu de 2 façons :
en recherchant le rmiregistry (si le client et le serveur
s’exécutent sur la même machine)
en utilisant la classe Naming et une uri du type :
rmi://host:port/object_name
Dans les 2 cas, la méthode de recherche est lookup
Attention ! Pour exporter un objet en utilisant la classe
Naming , il est obligatoire que l’objet distant hérite de la
classe UnicastRemoteObject
35 / 47
Retour sur l’implémentation
import java.rmi.RemoteException;
public class HelloObject extends UnicastRemoteObject
implements HelloInterface
{
private String message;
public HelloServer(String msg) throws RemoteException
{
message = msg;
}
public String say() throws RemoteException
{
return message;
}
}
36 / 47
Différents appels à lookup
String name = "objet_distant";
Registry registry = LocateRegistry.getRegistry(args[0]);
HelloInterface reference = (HelloInterface) registry.lookup(name);
String name = "objet_distant";
String host = "127.0.0.1";
String port = args[0];
HelloInterface reference =
(HelloInterface) Naming.lookup("rmi://"+host+":"+port+"/"+name);
37 / 47
Différents appels à rebind
String name = "objet_distant";
Registry registry = LocateRegistry.getRegistry(args[0]);
registry.rebind(name, stub);
String name = "objet_distant";
String host = "127.0.0.1";
String port = args[0];
Naming.rebind("rmi://"+host+":"+port+"/"+name, helloObject );
38 / 47
Écriture de la classe client
import ...
public class HelloClient {
public static void main(String[] args) {
// definition de la politique de securite
try
{
// recuperation de l’objet distant
String msg = helloObject.say();
System.out.println(msg);
}
catch (Exception e)
{
System.out.println("Erreur Client : ");
e.printStackTrace();
}
}
}
39 / 47
Valeurs de sécurités
Le code du serveur, comme du client, met en place une
politique de sécurité :
définie par un fichier : security.policy
cas le plus simple et le plus permissif :
grant {
permission java.security.AllPermission;
};
Utilisation de l’option
-Djava.security.policy=<path>/security.policy
40 / 47
Compilation et exécution
$ javac HelloInterface.java HelloObject.java \
HelloServer.java
$ java -cp .\
-Djava.security.policy=./security.policy \
-Djava.rmi.server.codebase=file:./ \
HelloServer 1099
Serveur KO
java.rmi.ConnectException: Connection refused to ho
java.net.ConnectException: Connection refused
....
41 / 47
Exécution du registre et du serveur
LES INTERFACES DOIVENT ÊTRES DANS LE
CLASSPATHAVANT L EXECUTION DU REGISTRE !
$ export CLASSPATH=$CLASSPATH:.
$ rmiregistry &
$ nmap 127.0.0.1 -p 1099
....
PORT
STATE SERVICE
1099/tcp open unknown
....
$ java -cp . \
-Djava.security.policy=./security.policy \
-Djava.rmi.server.codebase=file:./ \
HelloServer 1099
Serveur OK
42 / 47
Compilation et exécution du client
$ javac HelloInterface.java HelloClient.java
$ java -cp . \
-Djava.security.policy=./security.policy \
-Djava.rmi.server.codebase=file:./ \
HelloClient
Action exécutée par le serveur
....
43 / 47
Génération des noms d’objets
En règle générale, les objets distants ne sont pas
directement connus du client :
le serveur est connu du client,
le client envoie une première requête au serveur
le serveur répond au client en désignant l’objet distant à
utiliser
44 / 47
Passage et type de paramètres
Type de base = passage par valeur
Dérivé de Remote = passage par référence
autre cas :
IMPLÉMENTATION DE Serializable
passage sous forme standard
création (implicite...) d’un nouvel objet chez le client
45 / 47
Une exception
Pour les types complexes et spécifiques à une machine/un
état mémoire :
Descripteur de fichier
Thread
...
Il n’est pas possible d’utiliser l’interface Serializable
46 / 47
En complément
Il est possible de ne pas supposer l’exécution préalable de
rmiregistry :
la classe LocateRegistry fourni la méthode
createRegistry(int port)
Pour être compatible avec du code antérieur à java 1.5, il
est nécessaire de générer explicitement les stub :
utilisation de rmic
il prend en paramètre la classe compilée de l’objet distant
47 / 47
Téléchargement