|
Les accès au réseau |
Accès via une URL
L'architecture client-serveur
Accès via les sockets
Accès via les datagrams
Ce chapitre décrit le package java.net de Java 1.0 qui rassemble les classes permettant de gérer les accès réseau via une URL ou par les sockets.
Les accès au réseau peuvent se faire à plusieurs niveaux en Java.
Le niveau le plus simple permet d'accéder à un fichier sur le réseau grâce à son URL (Uniformed Resource Locator) : Contrairement au chemin d'accès classique à un fichier dont la notation varie d'un système à l'autre (c:\fichier.txt, /usr/unCompte/fichier.txt), une URL permet de désigner un fichier de manière uniforme quelque que soit le système qui héberge ce fichier.
Même si vous ne la connaissiez pas sous ce nom, l'adresse d'un site Internet est une URL : http://www.javasoft.com/index.html, http://www.eteks.com/coursjava/net10.html#AccesURL sont des exemples d'URL.
De quoi est composée une URL ?
- Une chaîne représentant le protocole à utiliser pour accéder au fichier (http, ftp, file, mailto,...), suivi du symbole :.
- Le nom de l'hôte qui fournit le service recherché (www.yahoo.fr). Cet hôte correspond à une machine.
- Eventuellement un numéro de port sur la machine de l'hôte précédé du symbole :.
- Le chemin d'accès au fichier recherché sur l'hôte. Les répertoires éventuels de ce chemin d'accès sont séparés par le symbole /.
- Pour les fichiers HTML, le nom du fichier peut être suivi d'une référence à une ancre à l'intérieur du fichier précédé du symbole #.
Une URL peut représenter un fichier mais aussi de manière plus générale une ressource. Par exemple, une ressource peut être un programme renvoyant une image ou le résultat d'un accès à une base de données. La plupart des programmes auxquels on accède sur un site Internet via une URL utilise le modèle CGI (Common Gateway Interface). Ce standard définit comment appeler un programme et lui transmettre ses paramètres.
Par exemple, http://www.hit-parade.com/hp.asp?site=a15740 est un programme CGI appelé avec la valeur a15740 pour le paramètre site. Ce programme renvoie l'image du site hit-parade.com.Java permet d'accéder au fichier ou à la ressource représentés par une URL sous forme de flux de données, grâce aux deux classes URL et URLConnection.
La classe java.net.URL
Cette classe final permet de manipuler une URL sous forme d'un objet constant. Les constructeurs de cette classe pouvant éventuellement déclencher une exception de classe MalformedURLException, la création d'une nouvelle instance de classe URL doit être programmée dans un bloc try ... catch. L'objet créé ne garantit pas que le fichier existe mais seulement que l'URL mémorisée par l'objet est correctement formée et que le protocole spécifié est géré par la Machine Virtuelle Java.
Les objets de cette classe sont utilisés pour accéder sous forme de flux de données au fichier correspondant, mais aussi par certaines méthodes de la classe Applet pour obtenir l'URL d'un document HTML, lire une image ou un fichier son.Constructeurs
public URL (String protocol, String host, int port, String file) throws MalformedURLException public URL (String protocol, String host, String file) throws MalformedURLExceptionCes constructeurs permettent de créer un objet de classe URL à partir du protocole protocol, l'hôte host, le port port et le chemin d'accès file d'une URL.
public URL (String spec) throws MalformedURLExceptionConstruit une instance de classe URL à partir de la chaîne de caractères spec représentant une URL.
public URL (URL context, String spec) throws MalformedURLExceptionConstruit une instance de classe URL à partir d'une URL existante context et de la chaîne de caractères spec. Si spec décrit entièrement une URL (avec protocole, hôte, fichier,...) , l'URL context est ignorée et l'objet créé correspond à l'URL spec. Sinon, context est utilisé comme base complétée par les informations fournies dans spec, pour construire le nouvel objet de classe URL (spec peut par exemple désigné un fichier dans un sous répertoire différent).
Méthodes
public String getProtocol () public String getHost () public int getPort () public String getFile () public String getRef ()Ces méthodes permettent d'interroger le protocole, l'hôte, le port, le chemin d'accès au fichier et la référence mémorisée dans une instance de la classe URL. Si le port n'a pas été précisé en paramètre du constructeur, la valeur -1 est renvoyée.
public boolean sameFile (URL other)Renvoie true si other désigne la même URL que l'objet sur lequel est invoquée cette méthode. Les références mémorisées par les objets ne sont pas prises en compte pour la comparaison.
public String toExternalForm ()Renvoie une chaîne de caractères correspondant à l'URL mémorisée par un objet de classe URL.
public final InputStream openStream () throws IOExceptionOuvre en lecture la connexion avec une URL, et renvoie une instance de la classe InputStream pour accéder aux données sous forme de flux de données.
public final Object getContent () throws IOExceptionRenvoie un objet correspondant au contenu du fichier représenté par un objet de classe URL. Par exemple, si le fichier est une image, cette méthode renverra une instance d'une classe dérivée de la classe Image.
public URLConnection openConnection () throws IOExceptionOuvre une connexion avec une URL, et renvoie une instance de la classe URLConnection qui permet d'obtenir toute sorte de renseignements (dates, en-tête,...) sur le fichier correspondant à l'URL.
public static synchronized void setURLStreamHandlerFactory (URLStreamHandlerFactory fac) throws SecurityExceptionSi le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création de flux de données pour chacun des protocoles reconnus. Cette méthode static ne peut être appelée qu'une seule fois. Par défaut, la Machine Virtuelle Java reconnaît un certain nombre de protocoles (voir l'exemple qui suit).
public int hashCode () public boolean equals (Object obj) public String toString ()Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un objet de classe URL à un objet ou renvoyer une chaîne de caractères correspondant à l'URL.
L'application suivante permet de tester si la Machine Virtuelle Java accepte ou non les 4 protocoles les plus communément utilisés : http, ftp, file et mailto. Recopiez-la dans un fichier TestProtocole.java, que vous compilez avec l'instruction javac TestProtocole.java pour ensuite l'exécuter avec java ou Java Runner, grâce à l'instruction java TestProtocole :
import java.net.*; public class TestProtocole { public static void main (String [] args) { // Tableau avec 4 URL à tester String urlTest [] = {"http://www.eteks.com/index.html", "ftp://www.eteks.com/index.html", "file://C//java/bin/java.exe", "mailto:puybaret@imaginet.fr"}; for (int i = 0; i < urlTest.length; i++) try { // Création d'une URL URL url = new URL (urlTest [i]); System.out.println ("URL : " + url + " correctement form\u00e9e\n" + " et protocole " + url.getProtocol () + " reconnu."); } catch (MalformedURLException e) { // URL incorrect ou protocole inconnu System.out.println ("URL : " + urlTest [i] + " incorrect\n" + " ou protocole non reconnu\n" + "(Exception " + e + ")."); } } }Autre exemple
Applets HelloFromNet et BoutonsNavigation.
La classe java.net.URLConnection
Cette classe abstract gère la connexion avec une URL. Une instance d'une classe dérivant de cette classe peut être obtenue grâce à la méthode openConnection () de la classe URL.
Les méthodes de cette classe permettent de créer un flux de données pour lire et/ou d'écrire dans le fichier désigné par une URL, mais aussi d'obtenir toute sorte d'information sur ce fichier (sa date de dernière modification, sa taille, son type,...).Champs
protected URL url protected boolean doInput protected boolean doOutput protected boolean allowUserInteraction protected boolean useCaches protected long ifModifiedSince protected boolean connectedCes champs protected sont utilisés par les classes dérivées de la classe URLConnection.
Constructeur
protected URLConnection (URL url)Méthodes
public URL getURL ()Renvoie l'URL avec laquelle la connexion est faite.
public InputStream getInputStream () throws IOException public OutputStream getOutputStream () throws IOExceptionCes méthodes renvoient un flux de données en lecture ou en écriture à partir d'une connexion à une URL. Une exception de classe UnknownServiceException (dérivant de la classe IOException) est déclenchée si le protocole interdit l'accès en lecture ou en écriture à cette URL.
public int getContentLength ()Renvoie la taille du fichier avec lequel la connexion est faite ou 0 si cette taille n'est pas connue.
public String getContentType () public String getContentEncoding ()Renvoie le type du contenu et son codage ou null si ces renseignements ne sont pas connus
public Object getContent () throws IOExceptionRenvoie un objet correspondant au contenu du fichier.
public long getDate () public long getLastModified () public long getExpiration ()Renvoie la date à laquelle le fichier a été envoyé, sa date de dernière modification et sa date d'expiration ou 0 si ces renseignements ne sont pas connus.
public String getHeaderField (String name) public int getHeaderFieldInt (String name, int default) public long getHeaderFieldDate (String name, long default) public String getHeaderFieldKey (int n) public String getHeaderField (int n)Ces méthodes renvoient les renseignements correspondant aux clés contenues dans l'entête d'un fichier
public boolean getDoInput () public boolean getDoOutput () public boolean getUseCaches () public boolean getAllowUserInteraction ()Ces méthodes renvoient true, s'il est possible de lire ou d'écrire sur une connexion, si la connexion utilise le cache ou si elle autorise d'interagir avec l'utilisateur (pour lui demander par exemple un mot de passe). Par défaut, une connexion est ouverte en lecture.
public long getIfModifiedSince ()Renvoie la durée de validité du fichier sur la connexion est faite.
public String getRequestProperty (String key)Renvoie la valeur associée à la clé key par lequel une requête est reconnue.
public abstract void connect () throws IOExceptionLes classes dérivées de la classe URLConnection doivent implémenter cette méthode pour réaliser la connexion avec une URL.
public void setDoInput (boolean doinput) public void setDoOutput (boolean dooutput) public void setUseCaches (boolean usecaches) public void setAllowUserInteraction (boolean allowuserinteraction) public void setIfModifiedSince (long ifmodifiedsince) public void setRequestProperty (String key, String value)Ces méthodes permettent d'autoriser l'accès en lecture ou en écriture à un fichier, l'utilisation du cache,... Elles ne peuvent pas être appelées quand la connexion est déjà ouverte.
public boolean getDefaultUseCaches () public void setDefaultUseCaches (boolean defaultusecaches) public static boolean getDefaultAllowUserInteraction () public static void setDefaultAllowUserInteraction (boolean defaultallowuserinteraction) public static String getDefaultRequestProperty (String key) public static void setDefaultRequestProperty (String key, String value) protected static String guessContentTypeFromName (String fname) protected static String guessContentTypeFromStream (InputStream is) throws IOException public static synchronized void setContentHandlerFactory (ContentHandlerFactory fac) throws SecurityExceptionSi le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création d'objets représentés dans un fichier suivant leur type mime, différent de celui fourni par défaut. Cette méthode static ne peut être appelée qu'une seule fois.
public String toString ()Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant la connexion.
Voici un exemple simple d'applet qui lit le texte contenu dans le fichier accessible à l'URL http://www.eteks.com/classes/hello.txt pour l'afficher à l'écran :
et le programme Java correspondant (à copier dans un fichier dénommé HelloFromNet.java et invoqué à partir d'un fichier HTML) :
import java.applet.Applet; import java.awt.Graphics; import java.net.*; import java.io.*; public class HelloFromNet extends Applet { String texteLu; // Méthode appelée par le système à l'initialisation de l'applet public void init () { try { // Création de l'URL http://www.eteks.com/classes/hello.txt URL urlFichierHello = new URL ("http", "www.eteks.com", "/classes/hello.txt"); // Ouverture d'une connexion et récupération d'un flux d'entrée URLConnection connexion = urlFichierHello.openConnection (); InputStream fluxFichier = connexion.getInputStream (); // Lecture du contenu du flux d'entrée byte contenuFichier [ ] = new byte [connexion.getContentLength ()]; int octetsLus = fluxFichier.read (contenuFichier); texteLu = new String (contenuFichier, 0, 0, octetsLus); // Fermeture de la connexion fluxFichier.close (); } catch (Exception e) { texteLu = "Probleme..."; } } // Méthode appelée par le système pour mettre à jour le dessin de l'applet public void paint (Graphics gc) { if (texteLu != null) // Affichage du texte lu gc.drawString(texteLu, 10, 20); } }La classe java.net.URLEncoder
L'unique méthode encode () de cette classe est utile si vous voulez créer un formulaire en Java et en envoyer les résultats au serveur pour être traité par exemple par un programme CGI. Cette méthode transforme une chaîne de caractères au format x-www-form-urlencoded, c'est-à-dire que tous les espaces sont remplacés par le signe + et tous les caractères différents des lettres de l'alphabet, des chiffres et du caractère _ par leur code hexadécimal précédé du symbole %.
Méthode
public static String encode (String s)
Vous n'aurez normalement pas à utiliser les interfaces et les classes qui suivent, car la Machine Virtuelle Java fournit et gère automatiquement un certain nombre de protocoles et la création d'objet en fonction du contenu d'un fichier. Ces classes sont utilisées indirectement par les méthodes de la classes URLConnection.
L'interface java.net.URLStreamHandlerFactory
Cette interface est implémentée par les classes désirant gérer l'accès sous forme de flux de données à un ou plusieurs protocoles. Elle est utilisée comme paramètre de la méthode setURLStreamHandlerFactory () de la classe URL, pour modifier le gestionnaire par défaut.
Méthode
public URLStreamHandler createURLStreamHandler (String protocol)Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract URLStreamHandler ou null si le protocole protocol est refusé ou n'est pas reconnu par ce gestionnaire. L'objet renvoyé doit pouvoir créer un flux de données à partir du protocole protocol et d'une URL.
La classe java.net.URLStreamHandler
Cette classe abstract est la super classe de toutes les classes qui gère la création d'un flux de données à partir d'un protocole donné et d'une URL.
La Machine Virtuelle Java fournit un ensemble de classes pour un certain nombre de protocoles. Ces classes sont utilisées par défaut si la méthode setURLStreamHandlerFactory () de la classe URL n'a pas été appelée pour changer de gestionnaire de création de flux de données.Constructeur
public URLStreamHandler ()Méthodes
protected abstract URLConnection openConnection (URL url) throws IOExceptionCette méthode doit ouvrir un connexion avec l'URL url et renvoyer une instance d'une classe dérivant de la classe abstract URLConnection pour permettre d'accéder à l'URL sous forme de flux de données.
protected void parseURL (URL url, String spec, int start, int limit)Analyse la chaîne de caractères spec représentant une URL entre les caractères start et limit (exclu), pour compléter l'instance url de la classe URL. Cette méthode analyse la chaîne spec en respectant la syntaxe du protocole http, et donc s'attend à ce que start désigne le caractère suivant le symbole : du protocole, et limit le caractère # précédant la référence à une ancre.
Outrepassez cette méthode si vous voulez qu'elle analyse différemment la chaîne spec.protected String toExternalForm (URL url)Renvoie une chaîne de caractères correspondant à l'URL mémorisée par url.
protected void setURL (URL url, String protocol, String host, int port, String file, String ref)Permet de modifier le protocole protocol, l'hôte host, le port port, le chemin d'accès au fichier file et la référence ref mémorisés par l'objet url.
L'interface java.net.ContentHandlerFactory
Cette interface est implémentée par les classes désirant gérer le contenu d'un fichier suivant leur type mime (par exemple image/gif ou text/plain). Elle est utilisée comme paramètre de la méthode setContentHandlerFactory () de la classe URLConnection, pour modifier le gestionnaire par défaut.
Méthode
public ContentHandler createContentHandler (String mimetype)Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract ContentHandler. Cet objet doit pouvoir créer un objet correspondant au contenu d'un fichier de type mime mimetype.
La classe java.net.ContentHandler
Cette classe abstract est la super classe de toutes les classes qui gère la création d'un objet correspondant au contenu d'un fichier d'un type mime donné.
La Machine Virtuelle Java fournit un ensemble de classes pour certains types mime. Ces classes sont utilisées par défaut si la méthode setContentHandlerFactory () de la classe URLConnection n'a pas été appelée pour changer de gestionnaire de création d'objet.Constructeur
public ContentHandler ()Méthode
public abstract Object getContent (URLConnection urlc) throws IOExceptionCette méthode doit renvoyer un objet correspondant au contenu du fichier avec lequel la connexion urlc a été établie.
Grâce à une URL, l'accès à un fichier ou une ressource est géré de manière simple : que le fichier soit sur un disque local ou sur Internet, il vous suffit de respecter les règles de construction d'une URL et le tour est joué.
Mais Java permet aussi de gérer des accès réseau plus complexe, pour réaliser par exemple un dialogue entre deux ordinateurs d'un réseau. Ce dialogue est basé sur l'architecture client-serveur, dont voici un rappel des grands principes.Principe
Comme la programmation orientée objet, l'architecture client-serveur est une technologie informatique très utilisée actuellement.
Elle est surtout connue dans le monde des SGBD (Système de Gestion de Base de Données), mais elle s'applique aussi à d'autres domaines comme Internet, les terminaux X utilisés sous UNIX (que l'on appelle aussi serveurs X).
Son principe est assez simple mais ce sont des exemples qui vous permettront de mieux comprendre comment cette architecture s'applique concrètement.Comme son nom l'indique, le client-serveur implique qu'il y ait au moins deux acteurs en jeu, un client et un serveur, qui communiquent entre eux, souvent à travers un réseau.
Un serveur est un programme ou par extension une machine programmée pour rendre toujours le même service, suite à une requête qui lui est adressée.
Un client est un programme ou par extension une machine qui demande à un serveur un service, en lui adressant une requête.
Gardez-bien toujours en tête ce mot service, car c'est souvent en pensant à qui rend service à l'autre que vous arriverez à déterminer qui est le client du serveur dans une architecture client-serveur.Exemples d'utilisation
figure 12. Architecture client-serveur sur InternetInternet que vous utilisez en ce moment, est un bon exemple d'architecture client-serveur : Les sites que vous consultez sont souvent appelés aussi des serveurs, car les programmes qui les gèrent rendent toujours le même service au client qu'est votre navigateur.
Quel service ? Le programme d'un serveur est en attente que vous lui demandiez une page du site que vous voulez visualiser. Quand votre requête lui parvient, il vous renvoie cette page. Votre navigateur de son côté est un client du serveur car il lui demande une page qu'il visualise une fois téléchargée.Le client-serveur est souvent utilisé pour accéder à de grandes bases de données utilisées par plusieurs utilisateurs. La base de données est stockée sur une machine utilisée comme serveur. Le programme sur ce serveur a pour mission de répondre à des requêtes SQL que lui adressent des clients. Les requêtes SQL que le serveur reçoit lui permettent de modifier ou d'interroger la base de données qu'il gère, et de renvoyer au client la réponse attendue. Le client est généralement une machine de type PC dont le programme est utilisé pour mettre en page les réponses reçues du serveur.
Le principale avantage de cette architecture est qu'elle permet de séparer complètement le serveur qui généralement gère le stockage de données, du client qui cherche à y accéder.
Ainsi, dans le cas d'Internet, il est possible d'avoir des navigateurs qui tournent sous différents systèmes et même des logiciels de navigation différents sur un même système. Tous ces navigateurs sont des clients du serveur d'un site Internet auquel ils peuvent accéder simultanément.
A l'opposé, le serveur d'un site Internet peut fonctionner sur n'importe quel système, que vous n'avez d'ailleurs pas à connaître. Ce qu'il compte, c'est qu'il réponde correctement à vos requêtes, quand vous voulez voir une page hébergée sur ce site.La clé de voûte qui permet à ce système de fonctionner sans que le client et le serveur n'aient à savoir comment ils fonctionnent l'un l'autre est l'utilisation d'un protocole commun. Ce protocole établit la norme que doit respecter le client et le serveur pour communiquer entre eux : comment établir une communication, comment un client doit envoyer une requête à un serveur et comment un serveur doit répondre à la requête du client.
Protocoles
Deux types de protocoles peuvent être utilisés sur Internet :
- TCP (Transport Control Protocol) : Sans que vous ayez à le savoir, ce protocole est utilisé par d'autres protocoles de plus haut niveau comme HTTP (HyperText Transfer Protocol) et FTP (File Transfer Protocol).
Ce protocole permet d'établir une connexion entre deux machines (un client et un serveur) : Une fois la connexion établie, les programmes client et serveur peuvent s'échanger des données, et ce protocole garantit que les données émises d'une machine arriveront sur l'autre, et dans l'ordre où elles ont été émises. Ainsi suite à une requête, le transfert d'un fichier peut être effectué en étant assuré de son intégrité à l'arrivée.
Les classes URL et URLConnection utilisent TCP implicitement et permettent à un client d'accéder ne manière simple à un fichier ou une ressource d'un serveur Internet. Ces classes sont généralement suffisantes pour les communications utilisant les protocoles comme HTTP ou FTP. Mais Java permet aussi de programmer des communications quelconques entre un client et un serveur en utilisant TCP, grâce aux classes Socket et ServerSocket.- UDP (User Datagram Protocol) : A l'opposé de TCP, ce protocole est utilisé pour transmettre des données entre deux machines sans garantir que ces données arriveront à destination et dans l'ordre où elles ont été émises. Ces données sont envoyées par paquets appelés aussi des datagrammes.
Ce protocole évite la contrainte d'avoir à établir et maintenir une connexion entre les deux machines.
Il peut être utile par exemple pour un serveur envoyant les tics d'une horloge sur requête d'un client. Si un datagramme contenant une heure donnée ne parvient pas au client ou son acheminement est retardé, il est inutile de s'en inquiéter car au tic suivant le serveur renverra un nouveau datagramme contenant l'heure actualisée. Si ces datagrammes arrivent dans le désordre, il suffira au client de ne pas tenir compte des datagrammes contenant une heure obsolète.
Les classes DatagramSocket et DatagramPacket permet d'utiliser UDP pour programmer ce type de communication entre un client et un serveur.Port
Les serveurs des sites auxquels vous vous connectez sur Internet sont accessibles grâce à un mnémonique qui est le nom de la machine hôte (par exemple java.sun.com, www.yahoo.fr). Chaque mnémonique est associé à un identifiant numérique représentant la machine hôte qui héberge un serveur. Cet identifiant est un nombre 32 bits appelé adresse IP que l'on note généralement sous la forme d'une suite de 4 nombres séparés par des points (par exemple 194.51.83.2) : certains navigateurs affichent dans la barre d'état l'adresse IP de l'hôte d'un site, quand vous essayez de vous connectez à ce site.
Chaque machine reliée à Internet possède une adresse IP différente qui permet de la désigner de manière unique sur le réseau Internet.Tous les systèmes d'exploitation permettent de faire fonctionner plusieurs programmes en même temps sur une machine. Si plusieurs serveurs sont hébergés sur une même machine, ils vont devoir partager l'accès physique au réseau sur cette machine. Un deuxième niveau d'identification est donc nécessaire pour désigner le programme avec lequel vous voulez vous connecter sur une machine donnée.
A chaque programme désirant communiquer sur Internet est associé un port unique, qui est un nombre 16 bits compris entre 0 et 65535. Les ports dont le nombre est compris entre 0 et 1023 sont réservés à des services particuliers et ne doivent pas être utilisés par les serveurs que vous programmez.Globalement, un programme est identifié sur Internet par l'adresse IP de la machine sur lequel il fonctionne et le numéro du port auquel il est associé.
Chaque extrémité d'une connexion entre un client et un serveur est appelée un socket. Un socket est une représentation logique du point de branchement d'un programme au réseau.
Pour programmer une architecture client serveur utilisant le protocole TCP (Transport Control Protocol), il vous faut réaliser, grâce aux classes ServerSocket et Socket qui suivent, un programme faisant office de serveur sur une machine hôte et un autre utilisé comme client sur une autre machine.
Pour vous permettre de tester les programmes serveur et client sur la même machine, le client peut essayer de se connecter à la machine localhost qui représente la machine locale.
figure 13. Utilisation des sockets en JavaComme le montre la figure précédente, le programme serveur doit créer une instance de la classe ServerSocket en précisant le numéro de port sur lequel il attend les demandes de connexion des programmes clients. Le serveur appelle ensuite la méthode accept () de cette classe pour se mettre en attente de demande de connexion.
Une fois qu'un client s'est connecté, une instance de la classe Socket est renvoyée. Le serveur peut alors grâce au méthodes de cette classe obtenir des flux de données en lecture et en écriture pour communiquer avec le programme client.Le programme client de son côté se connecte à un programme serveur en créant une instance de la classe Socket, en précisant l'adresse Internet de la machine hôte et le numéro de port sur lequel le serveur attend les demandes de connexions.
Comme pour le programme serveur, une fois qu'il a obtenu une instance de cette classe, il peut obtenir des flux de données en lecture et en écriture.Chacun des programmes serveurs et clients pouvant envoyer et lire des données, tout est alors prêt pour communiquer des données dans les deux sens entre les programmes.
Pour un fonctionnement correct, il suffit que quand le client écrit sur le flux de données de sortie de son socket, le serveur soit en lecture sur le flux de données d'entrée de son socket et inversement.La classe java.net.InetAddress
Cette classe final permet de manipuler l'adresse Internet d'une machine hôte. Elle est utilisée par les classes ServerSocket, Socket et DatagramPacket.
Méthodes
public static synchronized InetAddress getByName (String host) throws UnknownHostExceptionRenvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine host.
public static synchronized InetAddress [ ] getAllByName (String host) throws UnknownHostExceptionRenvoie toutes les adresses Internet de la machine host.
public static InetAddress getLocalHost () throws UnknownHostExceptionRenvoie une instance de la classe InetAddress représentant l'adresse Internet de la machine locale. Très pratique pour tester sur une même machine les programmes client et serveur. Equivalent à getByName (null) ou getByName ("localhost").
public String getHostName ()Renvoie le nom de la machine hôte.
public byte [ ] getAddress ()Renvoie l'adresse IP mémorisée par une instance de la classe InetAddress. Le tableau renvoyé contient les 4 nombres d'une adresse IP qui sont séparées par des points (par exemple 194.51.83.2).
public int hashCode () public boolean equals (Object obj) public String toString ()Ces méthodes outrepassent celles de la classe Object, pour renvoyer un code de hash, comparer un objet de classe InetAddress à un objet ou renvoyer une chaîne de caractères décrivant une adresse Internet.
Exemples
Applets EchoClient et PaperBoardClient.
La classe java.net.Socket
Cette classe final permet de manipuler un socket. Vous pouvez en obtenir une instance de deux manières différentes, suivant que vous réalisez le programme client ou le serveur :
- Quand un programme client essaye de se connecter à un serveur, il doit créer une instance de la classe Socket grâce à l'un des constructeurs de cette classe (par exemple new Socket ("www.eteks.com", 26197)). Chacun de ses constructeurs prend comme paramètres l'adresse Internet de la machine hôte du serveur et le numéro de port sur lequel le serveur est en attente de connexion avec un client.
- Quand un programme serveur en attente sur la méthode accept () reçoit une demande de connexion avec un programme client, cette connexion est créée puis accept () renvoie une instance de la classe Socket.
Une fois obtenue une instance de la classe Socket, il est possible d'obtenir côté client comme côté serveur un flux de données d'entrée et un flux de données de sortie, grâce aux méthodes getInputStream () et getOutputStream (). Ces méthodes renvoient des instances de classes dérivant des classes InputStream et OutputStream dont les méthodes permettent de de lire et d'envoyer des données entre le client et le serveur.
Constructeurs
public Socket (String host, int port) throws UnknownHostException, IOException public Socket (String host, int port, boolean stream) throws IOExceptionCes constructeurs permettent de créer une instance de la classe Socket et d'obtenir pour un programme client une connexion avec le programme serveur de la machine host en attente sur le port port. Si stream (égal à true par défaut) est égale à false, un socket de datagrammes est créé.
public Socket (InetAddress address, int port) throws IOException public Socket (InetAddress address, int port, boolean stream) throws IOExceptionCes constructeurs fonctionnent comme les deux premiers, mais prennent comme premier paramètre une instance de la classe InetAddress.
Méthodes
public InputStream getInputStream () throws IOException public OutputStream getOutputStream () throws IOExceptionCes méthodes permettent d'obtenir un flux de données en lecture ou un flux de données en écriture, pour communiquer avec le socket distant.
public synchronized void close () throws IOExceptionFerme la connexion avec le socket distant.
public InetAddress getInetAddress () public int getPort ()Ces méthodes renvoient l'adresse Internet et le port du socket distant auquel le socket est connecté.
public int getLocalPort ()Renvoie le port local sur lequel le socket est connecté.
public static synchronized void setSocketImplFactory (SocketImplFactory fac) throws IOException, SecurityExceptionSi le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création de sockets, différent de celui fourni par défaut. Cette méthode static ne peut être appelée qu'une seule fois.
public String toString ()Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le socket.
Exemples
Applications EchoServer et PaperBoardServer.
Applets EchoClient et PaperBoardClient.La classe java.net.ServerSocket
Cette classe final est utilisée par un programme serveur pour gérer l'attente de demande de connexion d'un programme client sur un port de la machine hôte du serveur.
Constructeurs
public ServerSocket (int port) throws IOException public ServerSocket (int port, int count) throws IOExceptionCes constructeurs permettent de créer un socket de serveur en attente sur le port port. Pour autoriser la connexion anonyme d'un client, utilisez un port est égal à 0. count (égal à 50 par défaut) permet de spécifier le nombre maximum de demande de connexions en attente que le serveur termine d'exécuter la méthode accept ().
Méthodes
public Socket accept () throws IOExceptionAttend la demande de connexion d'un programme client, et renvoie un socket une fois la connexion établie.
public void close () throws IOExceptionFerme le socket du serveur.
public InetAddress getInetAddress () public int getLocalPort ()Ces méthodes renvoient l'adresse Internet et le port local sur lequel le socket du serveur est connecté.
public static synchronized void setSocketFactory (SocketImplFactory fac) throws IOExceptionSi le gestionnaire de sécurité l'autorise, cette méthode permet de positionner un gestionnaire de création de socket de serveur, différent de celui fourni par défaut. Cette méthode static ne peut être appelée qu'une seule fois.
public String toString ()Méthode de la classe Object outrepassée pour renvoyer une chaîne de caractères décrivant le serveur.
Exemples
Applications EchoServer et PaperBoardServer.
Le client serveur d'écho
Ce premier exemple de client serveur se décompose en deux programmes : une application pour le serveur et une applet pour le client. Vous pouvez tester cet exemple sur votre ordinateur car l'applet se connecte sur le serveur de la machine locale, où vous pouvez lancer l'application serveur.
Cet exemple réalise une opération très simple d'écho : le serveur ne fait que renvoyer en l'état les données que le client lui envoie, jusqu'à ce qu'il rencontre un retour chariot.
Il faut lancer l'application du serveur avant de lancer l'applet du client.Voici le programme du serveur. Recopiez-le dans un fichier EchoServer.java, que vous compilez avec l'instruction javac EchoServer.java pour ensuite l'exécuter avec java ou Java Runner, grâce à l'instruction java EchoServer : :
import java.net.*; import java.io.*; public class EchoServer { public static void main (String args []) { try { // Création d'un serveur sur le port 13267 ServerSocket server = new ServerSocket (13267); // Attente de la connexion d'un client Socket socket = server.accept (); System.out.println ("Connexion sur le socket : " + socket); // Récupération des flux d'entrés et de sortie InputStream fluxEntree = socket.getInputStream (); OutputStream fluxSortie = socket.getOutputStream (); char caractereLu; // Renvoie de chaque caractère lu au client // jusqu'à ce que ce caractère soit un retour chariot while ((caractereLu = (char)fluxEntree.read ()) != '\n') fluxSortie.write (caractereLu); server.close (); } catch (IOException e) { System.out.println (e); } } }Une fois programmé le serveur, il faut réaliser un programme client. Celui-ci est une applet simple qui envoie au serveur tous les caractères saisis au clavier et affiche les caractères lus à partir du serveur sous la forme d'une chaîne de caractères (à mettre dans un fichier EchoClient.java) :
import java.applet.Applet; import java.awt.*; import java.net.*; import java.io.*; public class EchoClient extends Applet { String texteLu = ""; InputStream fluxEntree; OutputStream fluxSortie; // Méthode appelée par le système à l'initialisation de l'applet public void init () { try { // Récupération de l'adresse de l'hôte local InetAddress adresse = InetAddress.getLocalHost (); // Ouverture d'une connexion sur le port 13267 de cet hôte Socket socket = new Socket (adresse, 13267); // Récupération des flux d'entrés et de sortie fluxEntree = socket.getInputStream (); fluxSortie = socket.getOutputStream (); } catch (IOException e) { texteLu = "Probleme de connexion"; } } // Méthode appelée par le système pour mettre à jour le dessin de l'applet public void paint (Graphics gc) { gc.drawString (texteLu, 10, 20); } // Méthode appelée par le système quand une touche du clavier est enfoncée public boolean keyDown (Event evt, int key) { if (fluxSortie != null) try { // Envoie au serveur du caractère lu puis relecture fluxSortie.write(key); char caractereLu = (char)fluxEntree.read (); // Ajout du caractère et redessin de l'applet texteLu += caractereLu; repaint(); } catch (IOException e) { } return true; } }Voici un exemple de code HTML qui vous permettre d'appeler cette applet (à mettre par exemple dans un fichier EchoClient.html et à exécuter avec appletviewer ou Applet Runner, grâce à l'instruction appletviewer EchoClient.html) :
<applet code="EchoClient" width=200 height=30> </applet>Le paper board Internet
Ce deuxième exemple de client serveur beaucoup plus élaboré se décompose toujours en deux programmes : une application pour le serveur et une applet pour le client.
Chaque utilisateur visualisant cette applet peut partager un dessin qu'il dessine avec la souris, avec toutes les autres personnes connectées au serveur. Pour saisir un élément de dessin, il suffit de déplacer la souris en maintenant son bouton enfoncé.
Pour permettre plusieurs connexions simultanées de clients au serveur, ce dernier lance un thread gérant chaque connexion. Chacun de ces threads a pour mission d'attendre les requêtes de chacun des clients.
Quatre types différents de requêtes sont gérées :
- AJOUT : Le client envoie cette requête pour indiquer au serveur que l'utilisateur de l'applet client a saisi une nouvelle suite de points (polyline), avec la souris. Cette requête est suivie d'une liste de valeurs entières représentant les couples x y de chacun des points de la polyline. Le serveur ajoute cette liste à l'ensemble des polylines qu'il a déjà reçues.
- LISTE : Le client envoie cette requête pour demander au serveur l'ensemble des polylines actuellement enregistrées sur le serveur. Le serveur lui renvoie chacune des listes de points séparée par des tabulations (caractère '\t'). Envoyée à intervalle régulier, cette requête permet à chacun des clients du serveur de mettre à jour sa zone d'affichage en affichant toutes les polylines qu'ont saisies l'ensemble des utilisateurs de l'applet client connectée au serveur.
- CONNEXION : Le client envoie cette requête pour demander le nombre actuelle de clients connectés au serveur. Ce dernier renvoie ce nombre.
- FIN : Le client envoie cette requête pour indiquer au client son intention de se déconnecter du serveur.
L'applet du client ne peut fonctionner que si le serveur du paper board est en marche sur www.eteks.com. Si l'applet ci-dessous vous indique au moins une connexion, tout est OK ! Si vous êtes connectés à Internet via un proxy, il est possible que cela ne puisse pas fonctionner.
Trouvez d'autres personnes pour se connecter au serveur et partagez vos dessins : Plus on est de fous, plus on rit !....
Voici le programme du serveur qui tourne sur www.eteks.com (A mettre dans un fichier PaperBoardServer.java, et exécuté avec java ou Java Runner) :
import java.net.*; import java.io.*; import java.util.*; public class PaperBoardServer implements Runnable { public static int port = 26197; public static String requeteFin = "FIN"; public static String requeteListe = "LISTE"; public static String requeteAjout = "AJOUT"; public static String requeteConnexion = "CONNEXION"; private static ServerSocket serveur; public static void main (String args []) { if (lancerServeur () == null) System.exit (-1); } // lancerServeur () peut être appelée par une servlet // pour éviter de lancer une Machine Virtuelle supplémentaire // sur le serveur où est hébergé le site public static ServerSocket lancerServeur () { if (serveur == null) try { // Création d'un serveur sur le port 26197 serveur = new ServerSocket (port); // Lancement d'un thread d'attente de connexion new Thread (new PaperBoardServer ()).start (); } catch (IOException e) { System.out.println (e); return null; } return serveur; } public void run () { try { // Boucle sans fin d'attente de connexion while (true) { // Attente de la connexion d'un client Socket socket = serveur.accept (); // Démarrage d'un nouveau thread pour gérer la connexion new ThreadSocket (socket).start (); } } catch (IOException e) { System.out.println (e); } finally { try { // Fermeture du serveur serveur.close (); serveur = null; } catch (IOException e) { System.out.println (e); } } } } class ThreadSocket extends Thread { static private Vector listePolylines = new Vector (100); static private int connexions; private Socket socket; public ThreadSocket (Socket socket) { this.socket = socket; } public void run () { try { // Récupération des flux d'entrés et de sortie DataInputStream fluxEntree = new DataInputStream ( new BufferedInputStream (socket.getInputStream ())); PrintStream fluxSortie = new PrintStream ( new BufferedOutputStream (socket.getOutputStream ()), true); // Augmentation du nombre de connexions au serveur connexions++; String chaineLue; // Lecture sur le flux d'entrée tant que la requête FIN n'est pas arrivée while ( (chaineLue = fluxEntree.readLine ()) != null && !chaineLue.equals (PaperBoardServer.requeteFin)) { if (chaineLue.startsWith (PaperBoardServer.requeteListe)) { // Si la liste des polylines est demandée, elles sont // renvoyées séparées par des tabulations for (Enumeration e = listePolylines.elements (); e.hasMoreElements (); ) fluxSortie.print (e.nextElement ().toString () + '\t'); fluxSortie.write ('\n'); } else if (chaineLue.equals (PaperBoardServer.requeteConnexion)) { // Renvoi du nombre de connexions au serveur fluxSortie.print (String.valueOf (connexions)); fluxSortie.write ('\n'); } else if (chaineLue.startsWith (PaperBoardServer.requeteAjout)) { // Si le vecteur arrive à saturation, il est vidée // (ceci pour éviter que le dessin ne devienne trop dense) if (listePolylines.size () == listePolylines.capacity ()) listePolylines.removeAllElements (); // La nouvelle polyline reçue est ajoutée à la liste listePolylines.addElement ( chaineLue.substring (PaperBoardServer.requeteAjout.length ())); } } fluxEntree.close (); fluxSortie.close (); socket.close (); } catch (IOException e) { System.out.println (e); } connexions--; } }Une fois programmé le serveur, il faut réaliser un programme client. Celui-ci est l'applet ci-dessus qui envoie au serveur les polylines saisies par l'utilisateur, et demande au serveur toutes les secondes la liste courante de toutes les polylines que celui-ci mémorise (à mettre dans un fichier PaperBoardClient.java) :
import java.applet.Applet; import java.awt.*; import java.net.*; import java.io.*; import java.util.*; public class PaperBoardClient extends Applet implements Runnable { private String message; private Socket socket; private DataInputStream fluxEntree; private PrintStream fluxSortie; private Vector listePolylines = new Vector (100); private Polygon polylineCourante; private int connexions; // Méthode appelée par le système à l'affichage de l'applet public void start () { setBackground (Color.white); try { // Récupération de l'adresse de l'hôte www.eteks.com InetAddress adresse = InetAddress.getByName ("www.eteks.com"); // Ouverture d'une connexion sur le port 26197 de cet hôte socket = new Socket (adresse, PaperBoardServer.port); // Récupération des flux d'entrés et de sortie fluxEntree = new DataInputStream ( new BufferedInputStream (socket.getInputStream ())); fluxSortie = new PrintStream ( new BufferedOutputStream (socket.getOutputStream ()), true); // Lancement d'un thread qui interroge à intervalle régulier // la liste des polylines enregistrées sur le serveur new Thread (this).start (); } catch (IOException e) { message = "Probleme de connexion avec le serveur"; } } // Méthode appelée par le système à la disparition de l'applet public void stop () { try { // Envoi d'une requête FIN et fermeture des flux fluxSortie.println (PaperBoardServer.requeteFin); fluxSortie.close (); fluxEntree.close (); socket.close (); } catch (IOException e) { } fluxSortie = null; } public void run () { try { while (fluxSortie != null) { // Envoi d'une requête CONNEXION pour récupérer le // nombre de clients connectés au serveur fluxSortie.print (PaperBoardServer.requeteConnexion); fluxSortie.write ('\n'); message = fluxEntree.readLine () + " connexions"; // Envoi d'une requête LISTE pour récupérer // la liste de toutes les polylines du serveur fluxSortie.print (PaperBoardServer.requeteListe); fluxSortie.write ('\n'); String liste = fluxEntree.readLine (); // Vidange de la liste pour la remettre à jour listePolylines.removeAllElements (); StreamTokenizer tokens = new StreamTokenizer ( new StringBufferInputStream (liste)); tokens.parseNumbers (); tokens.ordinaryChar ('\t'); tokens.whitespaceChars (' ', ' '); // Décodage de la liste de points while (tokens.nextToken () != StreamTokenizer.TT_EOF) { Polygon polyline = new Polygon (); // Récupération des couples de valeurs (x,y) // d'une polyline jusqu'à la prochaine tabulation while (tokens.ttype != '\t') { int x = (int)tokens.nval; tokens.nextToken (); int y = (int)tokens.nval; tokens.nextToken (); polyline.addPoint (x, y); } // Ajout de la polyline à la liste listePolylines.addElement (polyline); } repaint (); // Arrête le thread pendant 1 s avant de lancer // une nouvelle demande de mise à jour Thread.sleep (1000); } } catch (InterruptedException e) { } catch (IOException e) { } } // Méthode appelée par le système pour mettre à jour le dessin de l'applet public void paint (Graphics gc) { if (message != null) gc.drawString (message, 10, 20); // Dessin de toutes les polylines // et de la polyline courante si elle existe for (Enumeration e = listePolylines.elements (); e.hasMoreElements (); ) drawPolyline (gc, (Polygon)e.nextElement ()); if (polylineCourante != null) drawPolyline (gc, polylineCourante); } private void drawPolyline (Graphics gc, Polygon polyline) { for (int i = 1; i < polyline.npoints; i++) gc.drawLine (polyline.xpoints [i - 1], polyline.ypoints [i - 1], polyline.xpoints [i], polyline.ypoints [i]); } // Les méthodes mouseDown (), mouseDrag (), mouseUp () sont // appelées par le système à l'enfoncement du bouton de la souris, // au déplacement du pointeur, et au relâchement du bouton public boolean mouseDown (Event evt, int x, int y) { polylineCourante = new Polygon (); polylineCourante.addPoint (x, y); return true; } public boolean mouseDrag (Event evt, int x, int y) { polylineCourante.addPoint (x, y); paint (getGraphics ()); return true; } public boolean mouseUp (Event evt, int x, int y) { polylineCourante.addPoint (x, y); // Construction d'une requête AJOUT avec la liste des points // de la nouvelle polyline fluxSortie.print (PaperBoardServer.requeteAjout); for (int i = 0; i < polylineCourante.npoints; i++) fluxSortie.print (String.valueOf (polylineCourante.xpoints [i]) + ' ' + polylineCourante.ypoints [i] + ' '); fluxSortie.write ('\n'); listePolylines.addElement (polylineCourante); paint (getGraphics ()); return true; } }Vous pouvez tester éventuellement ce programme sur votre machine locale en lançant le serveur PaperBoardServer, et en remplaçant dans l'applet PaperBoardClient le nom du site Internet "www.eteks.com" par "localhost".
Ce programme est inspiré du Groupboard disponible à l'adresse http://www.groupboard.com.
Vous n'aurez normalement pas à utiliser la classe et l'interface qui suivent, utilisées par la Machine Virtuelle Java pour réaliser l'implémentation des sockets.
La classe java.net.SocketImpl
Cette classe abstract permet de gérer un socket et la connexion au socket d'un serveur. Elle est utilisée pour les sockets côté client comme côté serveur. Elle comporte des champs et des méthodes protected qui sont utilisées pour réaliser les services des méthodes des classes précédentes Socket et ServerSocket.
Champs
protected FileDescriptor fd protected InetAddress address protected int port protected int localportConstructeur
public SocketImpl ()Méthodes
protected abstract void create (boolean stream) throws IOException protected abstract void connect (String host, int port) throws IOException protected abstract void connect (InetAddress address, int port) throws IOException protected abstract void bind (InetAddress host, int port) throws IOException protected abstract void listen (int count) throws IOException protected abstract void accept (SocketImpl s) throws IOException protected abstract InputStream getInputStream () throws IOException protected abstract OutputStream getOutputStream () throws IOException protected abstract int available () throws IOException protected abstract void close () throws IOException protected FileDescriptor getFileDescriptor () protected InetAddress getInetAddress () protected int getPort () protected int getLocalPort () public String toString ()L'interface java.net.SocketImplFactory
Cette interface est implémentée par les classes désirant gérer la création de sockets. Elle est utilisée comme paramètre des méthodes setSocketImplFactory () de la classe Socket et setSocketFactory () de la classe ServerSocket, pour modifier le gestionnaire par défaut.
Méthode
public SocketImpl createSocketImpl ()Cette méthode doit renvoyer un objet dont la classe dérive de la classe abstract SocketImpl.
Pour programmer une architecture client serveur utilisant le protocole UDP (User Datagram Protocol), il faut utiliser les classes DatagramPacket représentant un paquet de données (ou datagramme) à recevoir ou envoyer, et DatagramSocket utilisé pour recevoir et envoyer des datagrammes.
Contrairement au protocole TCP, le protocole UDP ne maintient pas de connexion permanente entre deux machines. Chaque datagramme est indépendant l'un de l'autre : il comporte un tableau de données, l'adresse Internet et le port de la machine à laquelle il est destiné.
La classe java.net.DatagramPacket
Cette classe final représente un datagramme. Le premier constructeur est utilisé pour créer un datagramme à recevoir, le second pour créer un datagramme à envoyer à une machine.
Constructeurs
public DatagramPacket (byte ibuf [ ], int ilength)Construit une instance de la classe DatagramPacket utilisé pour recevoir un datagramme dont les paquets de données de longueur ilength seront stockées dans le tableau ibuf. Si le datagramme reçu est plus long que ilength, les données restantes ne sont pas recopiées dans ibuf.
public DatagramPacket (byte ibuf [ ], int ilength, InetAddress iaddr, int iport)Construit une instance de la classe DatagramPacket utilisé pour envoyer un paquet de données ibuf de longueur ilength, au port iport d'une machine d'adresse Internet iaddr.
Méthodes
public InetAddress getAddress ()Renvoie l'adresse Internet de la machine dont provient ou auquel est destiné ce datagramme.
public int getPort ()Renvoie le port de la machine dont provient ou auquel est destiné ce datagramme.
public byte[] getData () public int getLength ()Ces méthodes renvoient le tableau où sont stockées les données d'un datagramme et la longueur des données à recevoir (ou reçues) ou à envoyer.
La classe java.net.DatagramSocket
Cette classe permet de recevoir et envoyer des datagrammes, grâce aux méthodes receive () et send ().
Constructeurs
public DatagramSocket () throws SocketExceptionConstruit une instance de la classe DatagramSocket. Le port utilisé pour recevoir ou envoyer des datagrammes est le premier disponible sur la machine locale.
public DatagramSocket (int port) throws SocketExceptionConstruit une instance de la classe DatagramSocket. Le port port sur la machine locale est utilisé pour recevoir ou envoyer des datagrammes.
Méthodes
public int getLocalPort ()Renvoie le port de la machine locale sur lequel l'envoi ou la réception de datagrammes s'effectue.
public void synchronized receive (DatagramPacket packet) throws IOExceptionPermet de recevoir un paquet de données dans le datagramme packet. Cette méthode met en attente le thread courant jusqu'à réception d'un datagramme.
public void send (DatagramPacket packet) throws IOExceptionPermet d'envoyer le datagramme packet.
public synchronized void close ()Ferme le socket.
protected synchronized void finalize ()
|