Programmation Réseaux Illustration : Les Sockets en Java Anne-Marie Déry À travailler seuls Concepts généraux Mise en œuvre Java
Communication par diffusion : Multicast Client1 Serveur Gr Client2 Clientn
Ouvrir un socket = demander à se Connecter Les clients demandent seulement à joindre un groupe
Exemple de multicast Un serveur de citation qui envoie une citation toutes les minutes à tous les clients qui écoutent (multicast)
Scénario d’un serveur 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
Scénario d’un client 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
Classe MulticastServer 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)
Multicast: MulticastSocket Type de socket utilisé côté client pour écouter des paquets que le serveur « broadcast » à plusieurs clients. . Une extension du QuoteServer : broadcast à intervalle régulier à tous ses clients
Cœur du serveur 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();}
Différences principales 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".
Un nouveau Client 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();
Synthèse Client Serveur TCP aSocket aServerSocket connecté write read I/O Stream I/O Stream TCP aSocket aServerSocket connecté write read read write UDP aDatagramSocket aDatagramSocket non connecté send receive receive send Multicast aMulticastSocket aDatagramSocket/ aMulticastSocket aDatagramPacket
Définir un nouveau type de socket Pourquoi ? Préparer les données avant de les envoyer Préparer les données reçues Exemple Java RMI Sockets spécialisées (marshalling et unmarshalling) Images : Compression et Décompression Comment ? En spécialisant les classes de base
Comment Définir un nouveau type de Sockets La classe CompressionSocket et ses classes relatives 4 étapes 1. Etendre java.io.FilterOutputStream pour créer un output stream pour ce type de Socket. Surcharge de méthodes si nécessaire. 2. Etendre java.io.FilterInputStream 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.
Un nouveau Package : java.nio The central abstractions of the NIO APIs are: Buffers, which are containers for data; Charsets and their associated decoders and encoders, which translate between bytes and Unicode characters; Channels of various types, which represent connections to entities capable of performing I/O operations; and Selectors and selection keys, which together with selectable channels define a multiplexed, non blocking I/O facility.
Le package Channel Multiplexed, non-blocking I/ODescription SelectableChannel A channel that can be multiplexed DatagramChannel A channel for a java.net.DatagramSocket Pipe.sinkChannel The write end of a pipe Pipe.sourceChannel The read end of a pipe ServerSocketChannel A channel for a java.net.ServerSocket SocketChannel A channel for a java.net.Socket Selector A multiplexor of selectable channels SelectionKey A token representing the registration of a channel with a selector Pipe Two channels that form a unidirectional pipe
Les nouvelles sockets This package defines selectable-channel classes corresponding to the DatagramSocket, ServerSocket, and Socket classes defined in the java.net package. Minor changes to these classes have been made in order to support sockets that are associated with channels. This package also defines a simple class that implements unidirectional pipes. In all cases, a new selectable channel is created by invoking the static open method of the corresponding class. If a channel needs an associated socket then a socket will be created as a side effect of this operation.
Définir un nouveau type de socket Pourquoi ? Préparer les données avant de les envoyer Préparer les données reçues Exemple Java RMI Sockets spécialisées (marshalling et unmarshalling) Images : Compression et Décompression Comment ? En spécialisant les classes de base
Comment Définir un nouveau type de Sockets La classe CompressionSocket et ses classes relatives 4 étapes 1. Etendre java.io.FilterOutputStream pour créer un output stream pour ce type de Socket. Surcharge de méthodes si nécessaire. 2. Etendre java.io.FilterInputStream 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.
Un nouveau Package : java.nio The central abstractions of the NIO APIs are: Buffers, which are containers for data; Charsets and their associated decoders and encoders, which translate between bytes and Unicode characters; Channels of various types, which represent connections to entities capable of performing I/O operations; and Selectors and selection keys, which together with selectable channels define a multiplexed, non blocking I/O facility.
Le package Channel Multiplexed, non-blocking I/ODescription SelectableChannel A channel that can be multiplexed DatagramChannel A channel for a java.net.DatagramSocket Pipe.sinkChannel The write end of a pipe Pipe.sourceChannel The read end of a pipe ServerSocketChannel A channel for a java.net.ServerSocket SocketChannel A channel for a java.net.Socket Selector A multiplexor of selectable channels SelectionKey A token representing the registration of a channel with a selector Pipe Two channels that form a unidirectional pipe
Les nouvelles sockets This package defines selectable-channel classes corresponding to the DatagramSocket, ServerSocket, and Socket classes defined in the java.net package. Minor changes to these classes have been made in order to support sockets that are associated with channels. This package also defines a simple class that implements unidirectional pipes. In all cases, a new selectable channel is created by invoking the static open method of the corresponding class. If a channel needs an associated socket then a socket will be created as a side effect of this operation.
Quelques Informations utiles sur la sérialisation Java
Sérialisation-Desérialisation Enregistrer ou récupérer des objets dans un flux Persistance Transfert sur le réseau
Sérialisation Via la méthode writeObject() Classe implémentant l’interface OutputObject Exemple : la classe OutputObjectStream Sérialisation d’un objet -> sérialisation de tous les objets contenus par cet objets Un objet est sauvé qu’une fois : cache pour les listes circulaires
Desérialisation Via la méthode readObject() Classe implémentant l’interface InputObject Exemple : la classe InputObjectStream
Exception NotSerializableException Si la classe de l’objet sauvé N’étend ni l’interface Java Serializable Ni l’interface Java Externalizable
Interface Serializable Ne contient pas de méthode -> enregistrement et récupération de toutes les variables d’instances (pas de static) + informations sur sa classe (nom, version), type et nom des variables 2 classes compatibles peuvent être utilisées Objet récupéré = une copie de l’objet enregistré
Gestion de la sérialisation desérialisation Implémenter les méthodes private void writeObject(OutputObjectStream s) throws IOException private void readObject(OutputInputStream s) throws IOException defaultReadObject() et defaultWriteObject() méthodes par défaut Ajout d’informations à l’enregistrement, choix de sérialisation Seulement pour les champs propres de la classe (héritage géré automatiquement)
Gestion complète de la sérialisation desérialisation : utiliser Externalizable Graphe d’héritage complet Implémenter les méthodes public void writeExternal(ObjectOutput o) throws IOException public void readExternal(ObjectInput o) throws IOException ATTENTION PBM de SECURITE
Un peu plus de réflexivité Les ClassLoader ????
Classe 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)
ClassLoader ? 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).
Exemple de chargement de classe 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.
Utilité et utilisation RMI
Chargement dynamique des classes 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 de clients RMI Possibilité de créer des gestionnaires de sécurité personnalisés pour des applications spécifiques RMI fournit des gestionnaires de sécurité suffisants pour un usage classique
Chargement dynamique 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 de l’ objet distant ne changent pas Le code du serveur ne change pas le client et la façon de le démarrer sont modifiés Et lancer un serveur Web pour nos classes
Hello World : chargement dynamique 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) Client (fichiers nécessaires au démarrage du client) HelloWorldClient.class
Hello World : Démarrage du serveur Web 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.class-server.com/classes/HelloWorldImpl_Stub.class };
Hello World : Politiques de sécurité 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"; "*:80", "connect"; };
Hello World : gestionnaire de sécurité RMI 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(); }
Hello World : Démarrage coté serveur 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
Hello World : Démarrage coté client 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
Les méthodes 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.
Exemple de programme Class cls = Class.forName("method1"); 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());}
Exemple d’exécution 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 param #0 class java.lang.String return type = void
Conclusion 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 et maintenant les EJB : Entreprise Java Beans et JINI …..