Communication par Sockets - programmer en java, programme java

Communication par sockets
Chapitres traités
Connexion
à
un
serveur
Le langage Java permet une communication entre machines qui s'appuie sur le protocole IP (Internet Protocol),
protocole de base du réseau Internet. Il y a plusieurs façons de faire communiquer des machines.
Nous pouvons simplement télécharger une ressource Internet référencée par une
URL
(
U
niform
R
esource
L
ocator) ; nous avons déjà utilisé cette possibilité dans le chapitre consacré aux applets. Nous pouvons
employer pour cela la classe
java.net.URL
. Si nous voulons disposer d'un certain contrôle sur le téléchargement
de données, nous pouvons obtenir des fonctionnalités supplémentaires grâce à la classe
java.net.URLConnection
qui sera utilisée dans le chapitre communication
Applet-Servlet
.
Nous pouvons communiquer également par le mode
"connecté"
en utilisant le protocole
TCP
(
T
ransmission
C
ontrol
P
rotocol). Nous
établissons entre deux machines une connexion par laquelle nous ouvrons des flux de données, connexion que l'on conserve tout le temps
de la communication. Les classes
ServerSocket
et
Socket
du paquetage
java.net
doivent être employées pour ce type de communication.
Des classes du paquetage
java.io
sont alors utilisées pour établir les flux de données.
Pour en savoir plus sur les Applets.
Pour en savoir flux sur les flux de données.
Connexion à un serveur
Avant d'écrire notre premier programme sur les réseaux, nous allons utiliser le client Telnet pour établir des connexions à certaines machines qui
proposent des services sur le réseau.
Attention, par défaut le client Telnet n'est pas actif sous Windows Vista. Il est nécessaire de l'activer dans la rubrique Fonctionnalités de
Windows du Panneau de configuration.
Vous vous êtes peut-être déjà servi de Telnet pour communiquer avec un ordinateur distant en utilisant les commandes de son propre
système d'exploitation. Vous pouvez également l'utiliser pour établir une connexion à un service quelconque sur des ordinateurs reliés à
votre réseau ou sur Internet (Si le pare-feu le permet). Dans ce cas là, il suffit de respecter le protocole lié au service demandé.
Rappelons qu'un service est un programme qui fonctionne en permanence (démon sous UNIX) dès que l'ordinateur est sous tension. Il est
également à l'écoute du réseau pour capturer la moindre requête venant de l'extérieur, et dès lors, il va rendre le service demandé. Il est
possible d'avoir plusieurs services sur une même machine, et pour les différencier, nous utilisons un numéro qui est appelé numéro de port.
Ces numéros sont standardisés comme par exemple :
1. le 13 pour le service date ITS (Internet Time Service).
2. le 21 pour le service FTP,
3. le 23 pour le service Telnet,
4. le 25 pour le service SMTP,
5. le 80 pour le service HTTP, etc.
Pour établir la connexion avec la machine distante sur le réseau (tube de communication), il suffit de connaître le nom de la
machine (nom de l'hôte) et le numéro du service (numéro de port). Ces tubes de communication sont appelés des Sockets.
Exemple de mise en oeuvre
Nous allons essayer de nous connecter au service date proposé par un serveur adapté sur Internet :
1. Lancez le logiciel client Telnet.
2. Etablissez ensuite la connexion au serveur time.nist.org (Situé aux Etats-Unis à @IP 192.43.244.18) à l'aide du client Telnet. Pour cela nous
utilisons la commande open. Rappelez-vous que par défaut ce service est réglé sur le port 13.
open time.nist.gov 13
ou
open 192.43.244.18 13
3. Que se passe-t-il ? Nous venons de nous connecter au service date que la plupart des serveurs UNIX implémentent en permanence. Le
serveur auquel vous vous êtes connecté se trouve au NIST à Boulder dans le Colorado, et fournit l'heure d'une horloge atomique au
césium. Naturellement, le temps affiché n'est pas parfaitement précis à cause des délais de propagation des informations sur le réseau.
Par convention, le service date est toujours rattaché au port 13.
Le programme du serveur fonctionne en permanence sur la machine distante, attendant un paquet du réseau qui essaierait de
communiquer avec le port 13. Lorsque le système d'exploitation de l'ordinateur distant reçoit le paquet contenant une requête de
connexion sur le port 13, le processus d'écoute du serveur est activé et la connexion est établie. Cette connexion demeure
jusqu'à ce qu'elle soit arrêtée par l'une des deux parties.
Lorsque vous avez ouvert une session Telnet sur le port 13 de l'ordinateur distant, une partie indépendante du logiciel réseau à
converti la chaîne de texte time.nist.gov en une adresse IP associée, c'est à dire 192.43.244.18. Puis le logiciel a envoyé une
requête de connexion à cet ordinateur, en spécifiant le port 13.
Une fois que cette connexion a été établie, le programme de l'ordinateur distant a renvoyé un ensemble de données, puis il a
terminé la connexion. Bien sûr, dans le cas plus général, les clients et les serveurs entament des dialogues beaucoup plus
poussés avant que la connexion ne soit interrompue.
Connexion à un serveur par un petit programme Java
Notre premier exemple de programme réseau effectue la même chose que ce que nous venons de faire avec Telnet, c'est à dire se connecter au
port 13 du service date sur le serveur time.nist.gov.
Le résultat renvoyé par le service est un simple texte qu'il est possible de formater afin de le rendre plus compréhensible pour nous.
Codage correspondant
package reseau;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
import javax.swing.*;
public class Client extends JFrame {
private JTextField résultat = new JTextField("Cliquez sur le bouton de connexion pour connaître la date...");
private JToolBar outils = new JToolBar();
public Client() {
super("Service Date");
résultat.setBackground(Color.YELLOW);
add(outils, BorderLayout.NORTH);
add(résultat);
outils.add(new AbstractAction("Connexion à time.nist.gov") {
public void actionPerformed(ActionEvent e) {
try {
Socket connexion = new Socket("time.nist.gov", 13);
Scanner réponse = new Scanner(connexion.getInputStream());
if (réponse.hasNextLine()) {
réponse.next();
Scanner date = new Scanner(réponse.next());
date.useDelimiter("-");
int année = 2000+date.nextInt();
String mois = date.next();
String jour = date.next();
résultat.setText(" "+jour+"-"+mois+"-"+année+" "+réponse.next());
}
}
catch (UnknownHostException ex) {
setTitle("Impossible de se connecter au service");
}
catch (IOException ex) {
setTitle("Gros problème de communication");
}
}
});
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) { new Client(); }
}
Lorsque nous établissons une communication réseau, nous devons prendre en compte un certain nombre de critères :
1. Localiser la machine distante et plus précisément le service à interroger, respectivement au moyen de son adresse IP et de son
numéro de service (port). Ceci est réalisé très simplement au travers de la classe java.net.Socket.
La première ligne de la gestion d'événement ouvre une socket, qui est en fait une abstraction du réseau et qui permet
d'établir une communication en entrée et en sortie avec le service. L'adresse de l'ordinateur distant est passée au
constructeur de la socket avec le numéro du service désiré. Si la connexion ne peut pas être ouverte, une
UnknowHostException est déclenchée. Si une autre erreur survient, une IOException apparaît.
Socket connexion =new Socket("time.nist.gov",13);
Une fois que la socket est ouverte, les méthodes getOutputStream() et getInputStream() de java.net.Socket peuvent être
sollicitées pour renvoyer respectivement les objets OutputStream et InputStream que vous pouvez utiliser par la suite
comme n'importe quel autre flux.
Evidemment, la classe Socket est très simple d'emploi, car Java masque toute la complexité inhérente à
l'établissement d'une connexion réseau et à l'envoi de données. Le paquetage java.net vous fournit quand à lui
une interface de programmation tout-à-fait semblable à celle que vous obtiendriez avec un simple fichier.
2. Respecter le protocole établi par le serveur. Il faut savoir par exemple qui parle en premier et surtout, il faut prendre en compte
le type de l'information envoyé, ce qui se traduit généralement à choisir un flux d'information adapté.
Dans le cas qui nous préoccupe, seul le serveur dialogue, sans information de retour. La réponse envoyée est un
simple texte qui peut toutefois être interprété très simplement en prenant le flux de lecture de texte représenté par la
classe Scanner.
3. Finalement, pour obtenir la réponse souhaitée, il suffit de se connecter au flot d'octets envoyé par le serveur au moyen de la
méthode getInputStream() de la socket et de le rattacher au flux de plus haut niveau afin de formater correctement l'information :
Scanner réponse =new Scanner(connexion.getInputStream());
4. Tout le reste ensuite est connu. Nous pouvons procéder exactement de la même façon que lors d'une lecture de fichier, sauf
qu'ici l'information n'est pas sur le disque dur mais transite sur le réseau. Finalement les deux lignes les plus importantes sont
celles que nous venons d'évoquer. Grâce à ces deux petites lignes, nous pouvons faire une gestion extrêmement simple des
informations issues du réseau :
Socket connexion =new Socket("time.nist.gov",13);
Scanner réponse =new Scanner(connexion.getInputStream());
Pour en savoir plus sur les flux et les fichiers
§
Le timeout des sockets
Le principe de base d'un dialogue sur le réseau, c'est de lire le flux d'octets provenant du service jusqu'à ce que l'ensemble de l'information soit
récupérée. Dans ce cas de figure, il est tout-fait possible que le temps de transfert soit très long, qu'il soit même beaucoup trop important, suite à
un problème éventuel de connexion réseau.
Il est envisageable de contrôler le temps de transfert maximum toléré pour une application au delà duquel nous considérons qu'un problème
de communication réseau doit certainement se produire. Ce temps maximum est appelé timeout. La classe Socket possède la méthode
setSoTimeout() qui précise la valeur de la durée d'attente maximum en milliseconde à ne pas dépasser.
Socket connexion =new Socket("time.nist.gov",13);
connexion.setSoTimeout(10000); // 10 secondes d'attente maximum
Si le timeout est atteint avant la fin de lecture complète de l'information attendue, une InterruptedIOException est levée. Vous
pouvez ainsi intercepter cette exception et réagir au timeout.
Dans l'écriture précédente il existe toutefois un petit soucis. Effectivement, durant la phase de construction de la connexion, le timeout n'est
pas encore précisé. Le temps d'attente peut alors être infini si un problème réseau apparaît. Nous pouvons palier à ce problème en
proposant la création d'une socket non-connectée, puis en la connectant plus tard avec le timeout requis, comme suit :
Socket connexion =new Socket();
connexion.connect(new InetSocketAddress("time.nist.gov", 13), 10000);
Dans cette étude, nous ne traitons que du protocole TCP (Transmission Control Protocol). Ce protocole établit une connexion fiable entre
deux ordinateurs. La plate-forme Java supporte également le pseudo-protocole UDP (User Datagram Protocol), qui peut être employé pour
envoyer des paquets (aussi appelés datagrammes) plus rapidement qu'avec TCP. L'inconvénient majeur de l'UDP est que les paquets
peuvent arriver dans le désordre, voire même égarés. C'est donc à l'ordinateur qui reçoit ces paquets de les remettre dans le bon ordre, et de
demander un nouvel envoi des paquets perdus. L'UDP est donc plus adapté à des applications pour lesquelles les paquets peuvent être
égarés, comme des flux de données audio ou vidéo, ou de mesures continues.
Demi-fermeture
Lorsqu'un client envoi une requête au serveur, ce dernier doit pouvoir déterminer la fin de celle-ci. Pour cette raison, de nombreux protocoles
Internet (comme SMTP) sont orienté vers la ligne. D'autres protocoles contiennent un en-tête qui spécifie la taille des données de la requête. En
effet, indiquer la fin des données de la requête est plus difficile qu'écrire des données dans un fichier. En effet, il suffit de femer le fichier. En
fermant une socket, vous vous déconnectez immédiatement du serveur.
La demi-fermeture résout ce problème. Vous pouvez fermer le flux de sortie d'une socket, au moyen de la méthode shutdownOutput(), ce qui
indique au serveur la fin des données de la requête, mais permet de conserver le flux d'entrée ouvert afin que vous puissiez lire la réponse.
Dans ce cas, le serveur lit simplement l'entrée jusqu'à ce qu'il atteigne la fin du flux d'entrée. A ce sujet, il existe également la méthode
shutdownInput() qui permet quant-à elle de fermer le flux d'entrée et de concerver le flux de sortie (utilisation plus rare).
Bien entendu, ce protocole n'est utile que pour des services à opération unique comme le HTTP où le client se connecte, émet une requête,
intercepte la réponse, puis se déconnecte.
Exemple de mise en oeuvre
A titre d'exemple, je vous propose de créer une petite application qui nous permet de consulter la page d'accueil d'un site quelconque, sans
interprétation, c'est-à-dire en visualisant uniquement le balisage HTML, ce que les navigateurs appellent couramment le code source :
Attention pour demander la page d'accueil du site, vous devez passer par le protocole HTTP en proposant la commande suivante (il faut tenir
compte des espaces) :
GET / HTTP/1.0 suivi de deux retours chariots
Codage correspondant
package reseau;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.Scanner;
import javax.swing.*;
public class Client extends JFrame {
private JTextField adresse = new JTextField();
private JFormattedTextField port = new JFormattedTextField(80);
private JTextArea éditeur = new JTextArea(20, 38);
private JToolBar outils = new JToolBar();
public Client() {
super("Editeur HTML");
éditeur.setBackground(Color.YELLOW);
1 / 22 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !