Sockets ?

publicité
À travailler seuls
Concepts généraux
Mise en œuvre Java
Année 2011-2012
Client1
Serveur
Client2
Gr
Clientn
Les clients demandent seulement à joindre un groupe
Un serveur de citation qui envoie une citation toutes les minutes
à tous les clients qui écoutent (multicast)
Créer le socket d’entrée
Créer un paquet de sortie
Préparer et Envoyer une donnée
Fermer le socket d’entrée
Créer le socket d’entrée
Création d’un paquet d’entrée
Attente de données en entrée
Réception et traitement des données en
entrée
Fermer le socket d ’entrée
Des constructeurs : par défaut, port à utiliser
Des accesseurs en lecture : adresse du groupe (getInterface…)
Des méthodes : pour envoyer un paquet datagramme, pour
joindre ou quitter un groupe (send, joinGroup, leaveGroup)
Type de socket utilisé côté client pour écouter des paquets que
le serveur « broadcast » à plusieurs clients. .
Une extension du QuoteServer :
broadcaste à intervalle régulier à tous ses clients
while (moreQuotes) {
try { byte[] buf new byte[256];
// don't wait for request...just send a quote
String dString = null;
if (in == null) dString = new Date().toString();
else dString = getNextQuote();
buf = dString.getBytes();
InetAddress group = InetAddress.getByName("230.0.0.1");
DatagramPacket packet;
packet = new DatagramPacket(buf, buf.length, group, 4446);
socket.send(packet);
try {sleep((long)Math.random() * FIVE_SECONDS);
} catch (InterruptedException e) { }
} catch (IOException e) { e.printStackTrace();
moreQuotes = false;}
} socket.close();}
Le DatagramPacket est construit à partir de
de « l’adresse de plusieurs clients »
L ’adresse et le no de port sont câblés
no de port 4446 (tout client doit avoir un MulticastSocket lié à ce no).
L’adresse InetAddress "230.0.0.1" correspond à un identificateur de
groupe et non à une adresse Internet de la machine d’un client
Le DatagramPacket est destiné à tous les clients qui écoutent le port 4446
et qui sont membres du groupe "230.0.0.1".
Pour écouter le port 4446, le programme du client doit créer son
MulticastSocket avec ce no.
Pour être membre du groupe "230.0.0.1" le client adresse la méthode
joinGroup du MulticastSocket avec l’adresse d’identification du groupe.
Le serveur utilise un DatagramSocket pour faire du broadcast à partir
de données du client sur un MulticastSocket. Il aurait pu utiliser aussi
un MulticastSocket. Le socket utilisé par le serveur pour envoyer le
DatagramPacket n’est pas important. Ce qui est important pour le
broadcast est d’adresser l’information contenue dans le DatagramPacket,
et le socket utilisé par le client pour l’écouter.
MulticastSocket socket = new MulticastSocket(4446);
InetAddress group = InetAddress.getByName("230.0.0.1");
socket.joinGroup(group);
DatagramPacket packet;
for (int i = 0; i < 5; i++) {
byte[] buf = new byte[256];
packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
String received = new String(packet.getData());
System.out.println("Quote of the Moment: " + received);
}
socket.leaveGroup(group);
socket.close();
Client
Serveur
I/O Stream
I/O Stream
TCP
connecté
aSocket
write
read
aServerSocket
read
write
aDatagramPacket
UDP
aDatagramSocket
non connecté
send
receive
Multicast
aMulticastSocket
receive
aDatagramSocket
receive
send
aDatagramPacket
send
aDatagramSocket/
aMulticastSocket
Pourquoi ?
Préparer les données avant de les envoyer
Reconstruire les données reçues
Exemple
Java RMI
Sockets spécialisées (marshalling et unmarshalling)
Exemple Images : Compression et Décompression
Comment ?
En spécialisant les classes de base
La classe CompressionSocket et ses classes relatives
4 étapes
Communication TCP
Définir des E/S Spécifiques
1. Etendre java.io.FilterOutputStream
pour créer un output stream pour ce type de Socket.
Surcharge de méthodes si nécessaire.
Le write doit compresser l’image avant d’écrire
2. Etendre java.io.FilterInputStream
Le read doit décompresser après avoir lu
La classe CompressionSocket et ses classes relatives
4 étapes
3. Etendre java.net.Socket
Implémenter les constructeurs appropriés et surcharger
getInputStream, getOutputStream et close.
4. Etendre java.net.ServerSocket
Implémenter le constructeur et surcharger accept
pour créer un socket du bon type.
Cette API définit :

Buffers : qui explicitent la notion de buffers –
containers de données
› Améliorent les problèmes de bufferisation liés aux E/S

Charsets : qui associent des « décodeurs » et des
« encodeurs » qui gèrent correctement les
conversions chaines – octets
› Éliminent les problème d’accent (caractères Unicode /
UTF),

Channels : qui représentent des connexions
entre entités avec de meilleures performances
pour les opérations de lecture et d’écriture

Selectors et selection keys : associées aux
selectable channels définissent des E/S
multiplexées non bloquantes
› évitent les threads

SelectableChannel : canal qui peut être multiplexé
DatagramChannel Un canal dédié aux communications
UDP prises en charge par des sockets de type
java.net.DatagramSocket

ServerSocketChannel : Un canal dédié aux connexions TCP
prises en charge par des sockets de type java.net.ServerSocket

SocketChannel : Un canal dédié aux communications TCP
prises en charge par des sockets de type java.net.Socket

Une large bibliothèque pour traiter les sockets et différents
types de communication entre Clients et Serveurs dans
Java
Une extension naturelle par abstraction à l’appel de
méthodes à distance - Java RMI
et une normalisation Corba avec l’intégration d’un ORB
….
Java reflection is useful because it supports dynamic retrieval
of information about classes and data structures by name, and allows
for their manipulation within an executing Java program.
This feature is extremely powerful and has no equivalent in other
conventional languages such as C, C++, Fortran, or Pascal.
Glen McCluskey
has focused on programming languages since 1988.
He consults in the areas of Java and C++ performance,
testing, and technical documentation.
La réflexivité en Java permet
à un programme Java de s’examiner en cours d’exécution
de manipuler ses propriétés internes.
Par exemple, une classe Java peut obtenir le nom de tous ses
membres.
Utilisation connue de la réflexivité :
l’édition sous Eclipse
L’outil utilise la réflexivité pour obtenir liste des méthodes
publiques qui peuvent être envoyées à une instance d’une classe
Obtenir des informations sur les classes
Simuler l’opérateur instanceof
Découvrir la liste et le descriptif des méthodes
Obtenir des informations sur les constructeurs
Avoir des informations sur les variables
Invoquer des méthodes, des constructeurs, affecter des variables
Créer des objets et des tableaux dynamiquement lors de l ’exécution du
programme sans connaître à la compilation
le nom et les arguments d’une méthode / constructeur
le nom de la variable
le type des objets, des tableaux….
java.lang.reflect.*
des classes:
Object
Modifier
AccessibleObject
ReflectPermission
Constructor
InvocationTargetException
Field
Array
Method
une interface :
Member
Comment travailler avec les classes du package reflect ?
Obtenir un objet java.lang.Class
Récupérer des informations sur la classe
Utiliser l’API de reflect
Illustration à partir de l’exemple
public class DumpMethods {
public static void main(String args[])
{ try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i]);
}
catch (Throwable e) {System.err.println(e);}
}
}
java.util.Stack
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)
Instances de Class : classes et interfaces d’une application Java.
Tous les tableaux sont aussi instances de Class (type des éléments,
dimension).
Les types primitifs (boolean, byte, char, short, int, long, float, double) et
le mot clé void sont aussi des objets Class.
Cette classe n’a pas de constructeur public.
Les instances sont créées automatiquement par la VM lorsque les classes
sont chargées et par la méthode defineClass des class loader.
représentant de la classe que l’on veut manipuler :
Pour les types de base
Class c = int.class;
ou
Class c = Integer.TYPE;
TYPE est une variable prédéfinie du wrapper (Integer, par exemple)
du type fondamental.
Pour les autres classes
Class c = Class.forName("java.lang.String");
Appeler une méthode sur l’objet classe récupéré :
getDeclaredMethods : obtenir la liste de toutes les méthodes
déclarées dans la classe
getConstructors : obtenir la liste des constructeurs dans
la classe
getDeclaredFields : obtenir la liste des variables déclarées
dans la classe (variables non héritées)
getFields : obtenir la liste des variables publiques
accessibles
....
pour manipuler l’information
Par exemple :
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0]));
Instances de la classe passée en paramètres ?
Class cls = Class.forName(args[0]);
boolean b1 = cls.isInstance(args[1]);
System.out.println(b1);
AccessibleObject : Classe de base de Field, Method et Constructor.
Permet de supprimer ou de valider la vérification faite par défaut
concernant les contrôles d’accès.
Ces vérifications (sur private, public, package..) sont effectuées
lorsqu’on affecte ou lit des champs, lorsqu’on invoque une méthode
et lorsqu’on crée une instance.
Member : interface qui réifie les informations communes à un membre
(champ ou méthode) ou à un constructeur.
Method fournit les informations et les accès à une méthode
(de classe, d’instance ou abstraite) d’une classe ou d’une interface.
Field fournit les informations et accès dynamiques aux champs (static
ou d’instances) d’une classe ou d’une interface.
Constructor fournit des informations et des accès à un
constructeur d’une classe.
1. récupérer l ’objet Class que l’on souhaite observer,
2. récupérer la liste des objets Method par getDeclaredMethods :
méthodes définies dans cette classe (public, protected, package, et
private)
getMethods permet d’obtenir aussi les informations concernant les
méthodes héritées
3. A partir des objets méthodes, il est facile de récupérer :
les types de paramètres, les types d’exception, et le type de
l’argument retourné sous la forme d’un type fondamental ou
d’un objet classe.
Class cls = Class.forName(args[0]);
Method methlist[] = cls.getDeclaredMethods();
for (int i = 0; i < methlist.length; i++) {
Method m = methlist[i];
System.out.println("name = " + m.getName());
System.out.println("decl class = " + m.getDeclaringClass());
Class pvec[] = m.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #" + j + " " + pvec[j]);
Class evec[] = m.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("return type = " + m.getReturnType());}
public class method1 {
private int f1(Object p, int x) throws NullPointerException
{……..}
public static void main(String args[]) {….}
name = f1
decl class = class method1
param #0 class java.lang.Object
param #1 int
exc #0 class java.lang.NullPointerException
return type = int
name = main
decl class = class method1
param #0 class java.lang.String
return type = void
Equivalent du apply de Scheme
pour invoquer une méthode m dont le nom est spécifié à l’exécution
(dans le cadre de l’environnement de développement des
JavaBeans par exemple)
1.
Trouver une méthode dans une classe getMethod à partir
des types de ses paramètres et de son nom.
2.
La stocker dans un objet Method
3.
Construire la liste des paramètres d’appel
4.
Faire l’appel
Si on manipule un type fondamental à l’appel ou au retour
l’encapsuler dans la classe correspondante (int , Integer)
Class cls = Class.forName(args[0]);
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod("add",partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue())
public class method2 {
public int add(int a, int b)
{
return a + b;
}
public static void main(String args[])
{……
……
}
?
Similaire à la découverte de méthodes et de constructeurs.
l’utilisation de la classe Modifier.
Modifier représente les modifiers des variables (private int ...).
Ils sont eux mêmes représentés par un integer,
Modifier.toString renvoie la chaine correspondant à l’ordre « officiel »
("static" avant "final").
On peut obtenir les informations sur les variables définies dans
les super classes par getFields.
Les constructeurs sont similaires aux méthodes
mais ne renvoient pas de résultat
Invoquer un constructeur implique de créer un nouvel objet
(allouer de la mémoire et construire l’objet)
1.
2.
Trouver un constructeur qui accepte les types spécifiés
L’invoquer
Création purement dynamique avec recherche (lookup) du constructeur
et invocation à l’exécution et non à la compilation.
Création et manipulation des tableaux.
Array = un type spécial de classe
Le type du tableau qui est créé est dynamique et n’a pas besoin
d’être connu à la compilation
java.lang.reflect.*
des classes:
Object
Modifier
AccessibleObject
ReflectPermission
Constructor
InvocationTargetException
Field
Array
Method
une interface :
Member
Le protocole est écrit dans une interface Java
Des stubs sont générés et contiennent le code sockets
Ils sont écrits grâce à la réflexivité Java
Que font les stubs ?
1. Trouver la liste des méthodes de l’interface
2. Du côté client
1. Sérialiser et envoyer sur le réseau
2. Attendre le résultat sérialisé et le restructurer
3. Dou côté du serveur
1. Déserialiser ce qui est reçu et invoquer la méthode avec les
paramètres
2. Récupérer le résultat, le sérialiser et l’envoyer au client
Les ClassLoader ????
ClassLoader est une classe abstraite.
Un class loader est un objet responsable du chargement des classes
Un nom de classe donné, il peut localiser ou générer les données qui
constituent une définition de la classe.
Chaque objet Class a une référence à un ClassLoader qui le définit.
Applications implémentent des sous classes de ClassLoader afin
d’étendre la façon de dynamiquement charger des classes par la VM.
(utilisation de manager de sécurité, par exemple)
En UNIX la VM charge les classes à partir des chemins définis dans
CLASSPATH.
Certaines classes peuvent être obtenues à partir d’autres sources,
telles que le réseau ou construites par une application. La méthode
defineClass convertit un tableau d’octets en une instance de Class.
Instances pouvant être créées grâce à newInstance
Les méthodes et constructeurs créés par un class loader peuvent
référencer d’autres classes (loadClass du class loader de cette classe).

Pour ne plus déployer les classes du serveur
chez le client
 Utilisation des chargeurs de classes qui téléchargent
des classes depuis une URL
 Utilisation d ’un serveur Web qui fournit les classes

Ce que ça change
 Bien entendu, les classes et interfaces du serveur ne
changent pas
 Le code du serveur ne change pas
 le client et la façon de le démarrer sont modifiés
 Il faut lancer un serveur Web

Problème de sécurité
 Le programme client télécharge du code sur
le réseau
 Ce code pourrait contenir des virus ou
effectuer des opérations non attendues !!!
 Utilisation d ’un gestionnaire de sécurité pour
les applications clientes
 Possibilité de créer des gestionnaires de
sécurité personnalisés pour des applications
spécifiques

Problème de sécurité
 Utilisation d ’un gestionnaire de sécurité pour
les applications clientes RMI
 RMI fournit des gestionnaires de sécurité
suffisants pour un usage classique

Séparation des classes
› Serveur (fichiers nécessaires a l'exécution du serveur)




HelloWorldServer.class
HelloWorldImpl.class
HelloWorld.class
HelloWorldImpl_Stub.class
› Download (fichiers de classes à charger dans le
programme client)
 HelloWorldImpl_Stub.class
› Client (fichiers nécessaires au démarrage du client)
 HelloWorld.class
 HelloWorldClient.class

chargement statique en socket ?

chargement dynamique en socket ?

Le client doit connaître l'emplacement des classes
afin de pouvoir les télécharger
 On peut le lui préciser lors du lancement
> java -Djava.security.policy=client.policy
-Dcodebase=http://www.class-server.com:80/

HelloWorldClient
Les classes à télécharger doivent être placées
dans le répertoire des documents Web du serveur
Web, accessibles via une URL
› le chargeur de classes ira chercher les classes à un
emplacement de type
›
http://www.class-server.com/classes/ClasseATelechharger.class

Mettre les classes Download dans le répertoire des
documents Web du serveur Web, accessibles via
une URL
› le chargeur de classes ira chercher les classes à un
emplacement de type http://www.classserver.com/classes/HelloWorldImpl_Stub.class
› Le programme Java client doit pouvoir se connecter aux
ports de la base de registres RMI et des implémentations
des objets de serveur, ainsi qu'au port du serveur Web
› Fichier client.policy
grant {
permission java.net.SocketPermission
"*:1024-65535", "connect,resolve";
permission java.net.SocketPermission
"*:80", "connect";
};

Le client intègre un gestionnaire de sécurité RMI
pour les stubs téléchargés dynamiquement
import java.rmi.*;
import java.rmi.server.*;
public class HelloWorldClient {
public static void main(String[] args) {
try {
// Installe un gestionnaire de sécurité RMI
System.setSecurityManager(new RMISecurityManager());
System.out.println("Recherche de l'objet serveur...");
HelloWorld hello =
(HelloWorld)Naming.lookup("rmi://server/HelloWorld");
System.out.println("Invocation de la méthode sayHello...");
String result = hello.sayHello();
System.out.println("Affichage du résultat :");
System.out.println(result);
} catch(Exception e) {
e.printStackTrace();
}
}
}
› 1) Lancer la base de registres RMI (elle doit pouvoir
accéder aux classes Download - CLASSPATH)
> rmiregistry
› 2) Lancer le serveur Web servant les fichiers de classes
Download
› 3) Lancer le serveur (les classes Server doivent être
accessibles)
> java HelloWorldServer
Création de l'objet serveur...
Référencement dans le RMIRegistry...
Attente d'invocations - CTRL-C pour stopper
› Le client doit pouvoir se connecter à des machines
distantes pour la base de registres RMI, les objets de
serveur ainsi que le serveur Web
 On doit lui fournir un fichier client.policy
› Le client doit bien connaître l'emplacement des classes
afin de pouvoir les télécharger
 On va le lui préciser lors du lancement
> java -Djava.security.policy=client.policy
-Djava.rmi.server.codebase=http://www.class-server.com:80/
HelloWorldClient
Un class loader qui permet de charger des fichiers de classes via
le réseau
ClassLoader loader=new NetworkClassLoader(host,port);
Object main= loader.loadClass("Main", true).newInstance();
….
NetworkClassLoader doit définir findClass et loadClassData pour
charger et defineClass pour créer une instance de Class.
Téléchargement