Programmation Réseau en Java - Partie II - La Programmation Réseau en Java
Programmation Réseau en Java - Partie II - La Programmation Réseau en Java
Programmation Réseau en Java - Partie II - La Programmation Réseau en Java
Le client lance une requête pour l’acquisition d’une ressource (Texte, Image, Son, Etc.) ;
Le serveur exécute la requête (Lancement des scripts, Communication avec une base de
données, Etc.) ;
Le résultat de la requête (la réponse) est renvoyé au client.
Dans les applications Client / Serveur, la communication se passe généralement entre deux
ordinateurs ; l’un des deux jouant le rôle de serveur (celui abritant la base de données de
l’application) et l’autre celui de client. Le serveur fournit quelques services (exécution des
requêtes de base de données par exemple), et le client est le consommateur de ces services
(affichage du résultat des requêtes dans le cas de notre exemple).
XIV.2. Le modèle OSI
OSI est un modèle théorique présentant la circulation des données dans un réseau. Il présente
très précisément la liaison qui existe entre deux nœuds successifs d’un réseau d’une manière
descendante et décomposée.
Il est décrit en sepr (7) couches : Application, Présentation, Session, Transport, Réseau, Liaison
et Physique.
Ce modèle a été mis au point par l’organisation internationale de normalisation (ISO) afin de
standardiser les communications entre ordinateurs.
XIV.3. Le modèle TCP/IP (Transmission Control Protocol/Internet Protocol)
TCP/IP est l’architecture réseau la plus répandue dans le monde. Il est basé sur le modèle OSI.
On y trouve quatre (4) couches : Application, Transport, Réseau et Liaison. L’architecture
TCP/IP inclut de nombreux services d’application, des protocoles élaborés et complexes, sur
lesquels les applications distribuées peuvent s’appuyer.
TCP/IP contient l’ensemble des règles de communication sur Internet et se base sur la notion
d’adressage IP. De ce fait, chaque ordinateur du réseau se voit attribuer une adresse IP afin de
pouvoir acheminer des paquets de données. Les fonctions principales du TCP/IP sont les
suivantes :
L’utilisation d’un système d’adresse ;
Le fractionnement des messages en paquets ;
L’acheminement des données par paquets à travers le réseau (routage) ;
Le contrôle des erreurs de transmission de données ;
Etc.
Le schéma suivant illustre les diverses couches TCP/IP où interviennent les différents
protocoles :
Lorsque vous écrivez un programme qui doit tourner sur un réseau, vous programmez au niveau
de la couche Application. Vous pouvez utiliser les classes du package java.net pour utiliser les
protocoles TCP et UDP pour le transport. Ces classes fournissent la communication
indépendamment des systèmes sur lesquels elles sont introduites. Cependant pour décider
quelle classe votre programme doit utiliser, vous avez besoin de comprendre la différence entre
les protocoles TCP et UDP.
XIV.4. Le protocole TCP (Transmission Control Protocol)
Lorsque deux applications veulent communiquer entre elles (dans le cas d’un appel
téléphonique par exemple), elles utilisent un support de transmission à travers lequel se font les
envois et les réceptions. Le TCP garantit l’envoi des données entre les deux applications. Ce
mode de communication est appelé Mode connecté.
TCP est un protocole qui permet une connexion de type point entre deux applications. C’est un
protocole fiable qui garantit la réception dans l’ordre d’envoi des données. Par contre, ce
protocole offre de moins bonnes performances gage de sa fiabilité.
En exploitant les ports, plusieurs applications peuvent utiliser TCP simultanément sur le même
ordinateur.
XIV.5. Le protocole UDP (User Datagram Protocol)
Le protocole UDP assure une communication non garantie entre deux applications sur un réseau
par envoi de paquets indépendants de données (appelés datagrams) d’une application à l’autre.
A la manière de l’envoi de courrier par le service postal, l’ordre de la livraison n’est pas
important donc n’est pas non garantie. Chaque paquet est indépendant de l’autre. Ce mode de
communication est dit Mode non connecté.
Le protocole UDP offre de bonnes performances car il est très rapide par rapport à TCP, mais
ne garantit pas l’envoi des données dans l’ordre d’émission. Il est pratiquement réservé à des
taches peu importantes. Pour assurer les échanges, UDP utilise la notion de port ; ce qui permet
à plusieurs applications d’utiliser UDP sans que les échanges interfèrent les uns avec les autres.
UDP est utilisé dans de nombreux services tel que echo (port 7), DayTime (13), etc. et dans
plusieurs applications peer-to-peer (Ex. : Emule, Kazaa, etc.).
Une différence importante au niveau de l’utilisation des ports TCP et UDP est que le TCP
réserve pour chaque connexion une file d’attente de messages, au contraire de UDP qui associe
à chaque port sa propre file d’attente.
XIV.6. Le protocole IP (Internet Protocol)
Le protocole IP dans la couche réseau du TCP/IP du routage et du contrôle dans les réseaux
interconnectés. Il permet également l’échange de datagrammes (en mode non connecté), entre
des hôtes connectés à divers réseaux physiques. Le protocole IP est un protocole ouvert et
indépendant du système d’exploitation.
Des nœuds d’un réseau sont potentiellement reliés par plusieurs chemins. Chaque paquet suit
un chemin particulier pour atteindre sa destination, mais une succession de paquets provenant
d’un même bloc de données ne suivent pas forcément le même chemin. Pour programmer via
le protocole IP, on utilise le protocole TCP ou le protocole UDP.
XIV.7. Les ports
La liaison d’un ordinateur avec un réseau se fait par une connexion physique unique. Mais les
données qui passent par cette connexion sont utilisées par diverses applications (ou protocole).
Les ports sont utilisés pour distinguer les données propres à chaque processus.
A cause de la diversité des services fournis par un serveur, certains ports sont réservés à des
services bien spécifiés : http (80), FTP (21), SMTP (25), etc. ces ports sont appelés Well Known
Ports (WKP).
Chaque machine sous IP possède quelques 65 535 ports. De 1 à 1024 sont des ports réservés
aux services fondamentaux, de 1025 à 5000 sont disponibles aux utilisateurs.
XIV.8. Les Sockets
La notion de sockets a été introduite dans le système UNIX BSD. En effet, UNIX est un système
multi-tâches et multi-utilisateurs. Les processus peuvent coopérer entre eux. Différents outils
de communication inter-processus ont été développés: les IPC (Inter Process Communication).
La communication interprocess peut se faire à travers les signaux et les pipes. Mais tandis que
la version d'ATT était orientée gestion de ressources (sémaphores, messages, mémoire
partagée), l'université de Berkeley a développé une version orientée réseaux en implémentant
les protocoles Internet ARPA/DOD et en offrant l'interface des "Sockets".
Les Sockets forment une API (Application Program Interface): ils offrent aux programmeurs
une interface entre le programme d'application et les protocoles de communication. En aucun
cas, les sockets ne forment une norme de communication ou une couche de protocole à l'instar
de TCP/IP.
L'interface des "sockets" n'est pas liée à une pile de protocoles spécifique. Dans ce cours, nous
nous intéresserons, à l'utilisation des sockets dans le monde TCP/IP.
La notion de socket en tant que prise de raccordement vient d’une analogie avec le réseau
électrique et le réseau téléphonique :
Les sockets représentent donc d'une part une API c'est à dire un ensemble de primitives de
programmation et d'autre part les extrémités de la communication (notion de prise). La mise en
œuvre des sockets nécessite donc la connaissance des primitives ainsi que le modèle
client/serveur utilisé. C’est ce qui fera l’objet de ce cours.
Les sockets se situent dans la couche transport (protocole UDP ou TCP), elle-même utilisant
les services de la couche réseau.
Dans une communication, au moins 2 sockets sont utilisés. Chaque socket est identifié par une
adresse IP et un numéro de port.
Les sockets représentent donc d'une part une API c'est à dire un ensemble de primitives de
programmation et d'autre part les extrémités de la communication (notion de prise). Les
extrémités de communication sont identifiées dans le monde TCP/IP par trois informations :
une adresse IP, le protocole utilisé (TCP ou UDP) et un numéro de port (entier sur 16 bits donc
de 0 à 65535. Etant donné que ces informations sont uniques dans l’Internet (les adresses IP
sont uniques et les numéros de ports sont uniques pour un protocole donné) ces trois
informations permettent d’identifier de façon unique une extrémité de communication (à
l’instar d’un numéro de téléphone dans un réseau téléphonique).
Pour maintenir l’unicité des numéros de port (par protocole) on les a répartis en trois catégories
:
- Les ports systèmes (appelés aussi ports bien connus – well known ports) de 0 à 1023 sont
réservés aux processus démarrés automatiquement par le système d’exploitation et
peuvent être déposés auprès de l’organisme IANA (Internet Assigned Numbers
Authority).
- Les ports utilisateurs (appelés aussi ports déposés – registered ports) de 1024 à 49151
sont disponibles pour les utilisateurs et peuvent eux aussi être déposés auprès de
l’organisme IANA.
- Les ports dynamiques (appelés aussi ports privés) de 49152 à 65535.
Le site web de IANA permet de connaître la liste des ports systèmes et utilisateurs assignés à
un protocole applicatif ou une application donnée (http://www.iana.org/assignments/port-
numbers). Il propose de plus deux formulaires permettant de déposer des numéros de ports
systèmes ou utilisateurs (http://www.iana.org/cgibin/sys-port-number.pl et
http://www.iana.org/cgi-bin/usr-port-number.pl ).
Pour les TPs il est recommandé d’utiliser les numéros de ports dynamiques.
En fonction du protocole transport utilisé les sockets vont fonctionner différemment. Nous
présenterons dans un premier temps les sockets utilisant le protocole TCP qui fonctionnent en
utilisant le modèle client/serveur et offrent une communication par flux. Les sockets utilisant
le protocole UDP seront ensuite présentés. Ils ont un fonctionnement beaucoup plus proche de
ce qui se passe au niveau de la couche réseau (échange de paquets – appelés datagrammes dans
le monde TCP/IP – acheminés indépendamment). Les sockets UDP peuvent de plus être utilisés
pour une communication entre une application et un groupe de machine (diffusion plus ou
moins restreinte – broadcast ou multicast).
XIV.9. La diffusion
Le protocole IP a prévu de pouvoir envoyer des paquets à plusieurs machines sans faire la
duplication de ces paquets. Pour garantir la transmission des paquets par la diffusion, les
routeurs doivent accepter le passage de ces paquets, et les serveurs de diffusion doivent
posséder une adresse IP de classe D, c’est-à-dire un numéro compris entre 224.0.0.1 et
239.255.255.255 (toutes ces adresses dont déjà réservées).
Lorsqu’on diffuse un paquet, celui-ci a la possibilité de parcourir tout l’Internet. Pour éviter ce
long chemin, les paquets possèdent un champ de type entier appelé TTL (Time To Live), qui
est décrémenté à chaque passage dans les routeurs. Lorsque cette valeur passe à 0, le routeur
détruit le paquet et ne le diffuse pas. Dans le cas d’un paquet en diffusion, on doit explicitement
spécifier la valeur de ce champ :
Une URL (Uniform Ressource Locator) ou localisateur de ressources uniforme, est une chaîne
de caractères qui désigne une ressource précise accessible par Internet ou Intranet. Une URL
est donc une référence à un objet dont le format dépend du protocole utilisé pour accéder à la
ressource.
http://<serveur>:<port>/<chemin>?<param1>&<param2>&...&<paramn>
Elle se compose du protocole http, d’une adresse IP ou du nom du serveur avec un numéro de
port (80 par défaut), d’un chemin d’accès à un fichier ou un nom de service et, éventuellement,
des paramètres sous la forme clé=valeur.
Exemple :
http://www.google.com/convert?value=100&src=miles&dest=kilometers
ftp://<user>:<password>@<serveur>:<port>/<chemin>
Où l’URL est composée d’un compte utilisateur et un mot de passe avec le nom du serveur
auquel on veut accéder. Le port par défaut du FTP est le port 21.
Exemple :
ftp://monusername:[email protected]/docspersos
mailto:<email>
Exemple :
mailto:[email protected]
file://<serveur>/<chemin>
Elle se compose de la désignation du serveur (non utilisé dans le cas du système de fichier local)
et du chemin absolu de la ressource.
Exemple :
file:///F:/LAWSON/COURS/ESGIS/Programmation%20r%C3%A9seau%20Java/Programma
tion%20r%C3%A9seau%20en%20JAVA%20_%20.PDF
En plus de tout ça, notons que Java est le premier langage à fournir une bibliothèque riche,
puissant et multiplateforme qui couvre divers domaines comme ceux qui sont cités ci-dessus.
Les classes et interfaces utiles au développement réseau en Java sont regroupés dans le package
java.net. Ces classes sont les suivantes :
Une machine (appelée aussi hôte ou host) est identifiée dans l’Internet par son adresse.
L’adresse IP d’une machine correspond à un numéro qui est unique dans le monde. Pour des
raisons mnémoniques, il est possible de donner un nom à une machine (ex : Toto, Garonne,
Mimosa, Etc.). Certains hôtes ont plusieurs noms. Ce nom est géré de façon hiérarchique par
le DNS (Domain Name System ou Système de Noms de Domaines).
L’adresse utilisée par la version actuelle du protocole IP (adresse IP), comporte deux champs:
le champ adresse réseau (Network) dans l’Internet et le champ adresse hôte (Host) dans le
réseau. Sa taille est de 4 octets (32 bits). Elle est souvent donnée en notation décimale pointée
(ex: 127.95.35.54).
Comme l’adresse IP contient l’adresse réseau, une station changeant de réseau change
d’adresse. D’autre part, une station multi domiciliée (qui dispose de plusieurs interfaces réseau)
ou un routeur ont plusieurs adresses.
Il existe actuellement cinq classes d’adresses IP. Les trois premières permettent de gérer des
réseaux de tailles diverses. La classe D permet de gérer une communication multipoint (un
message est envoyé à plusieurs machines à la fois). La classe E est réservée et ne sera
probablement jamais utilisée puisqu’on devrait bientôt migrer vers la nouvelle version d’IP
IPv6 qui stockera les adresses IP dans 16 octets.
La classe java.net.InetAddress permet de représenter les adresses IP. Chaque objet de cette
classe possède deux champs hostName et address contenant respectivement une chaîne de
caractère et un tableau d’octets.
Le champ hostName stocke le plus souvent le nom de l’hôte (www.esgis.org par exemple) et le
champ address l’adresse IP.
Cette classe ne possède pas de constructeurs publics. Pour créer un objet de type InetAddress il
faut donc utiliser l’une des méthodes statiques suivantes :
Méthode Rôle
Renvoie l’adresse Internet associée au nom
InetAddress getByName(String)
d’hôte passé en paramètre.
Renvoie un tableau des adresses Internet
InetAddress[] getAllByName(String)
associées au nom d’hôte passé en paramètre.
Renvoie l’adresse Internet de la machine
InetAddress getLocalHost()
locale.
Renvoie un tableau contenant les 4 octets de
Byte[] getAddress()
l’adresse Internet.
Renvoie l’adresse Internet sous la forme
String getHostAddress()
d’une chaîne de caractères.
String getHostName() Renvoie le nom du serveur.
Cette méthode utilise le DNS pour renvoyer une instance de la classe InetAddress représentant
l'adresse Internet de la machine de nom nom_hote. Voici un exemple d’utilisation (en supposant
que la classe java.net.InetAddress a été précédemment importée) :
Lorsque la recherche DNS n'aboutit pas, une exception UnknownHostException est levée.
Cette méthode renvoie toutes les adresses Internet de la machine de nom nom_hote.
Autres méthodes :
Renvoie le nom de la machine hôte, ou bien l'adresse IP si la machine n'a pas de nom.
Renvoie l'adresse IP stockée par une instance de la classe InetAddress sous la forme d'un
tableau d'octets rangés dans l'ordre standard du réseau (network byte order). Ainsi l'octet
d'indice 0 contient l'octet de poids fort de l'adresse.
La longueur du tableau retourné est actuellement 4 dans la plupart des cas (IPv4) mais devrait
passer à 16 lorsque les adresses IP sur 128 bits auront cours (IPv6).
Méthodes surchargées
Ces méthodes surchargent celles de la classe Object, pour renvoyer un code de hashing,
comparer un objet de classe InetAddress à un objet ou renvoyer une chaîne de caractères
décrivant une adresse Internet.
Remarque importante : la première méthode est directement utilisée par la classe InetAddress
qui contient une HashTable en membre statique. Ainsi, une adresse requise plusieurs fois sera
immédiatement extraite du cache et donc obtenue beaucoup plus rapidement que par l'envoi
d'une requête au serveur DNS.
Exemple :
Classe InetAddressSample.java :
package ga.esgis.l3iirtsrs.inet;
import java.net.InetAddress;
System.out.println();
// Renvoie les adresses IP du serveur Yahoo
InetAddress[] adressesServeurYahoo =
InetAddress.getAllByName("yahoo.com");
System.out.println("Voici les adresses IP de Yahoo : ");
for(int i = 0; i < adressesServeurYahoo.length; i++)
System.out.println(" " +
adressesServeurYahoo[i].getHostAddress());
}
catch(Exception e) {
e.printStackTrace();
}
}
Résultat :
Exercice : tentez d'afficher à l'aide de ce programme les adresses IP renvoyées par la méthode
public byte [] getAddress (). Que remarquez-vous ?
Remarque : la manipulation d'octets non signés pose des problèmes en Java car il n'y a pas
d'équivalent au type C unsigned char. Ainsi, les octets supérieurs à 127 sont traités comme des
nombres négatifs.
La classe Java NetworkInterface permet d’obtenir la liste des interfaces de connexion au réseau
de la machine. Une interface de connexion à un réseau se caractérise par un nom court, une
désignation et une liste d’adresses IP. Les méthodes de cette classe sont les suivantes :
Méthode Description
String getName() Renvoie le nom court de l’interface.
String getDisplayName() Renvoie la désignation de l’interface.
Renvoie une énumération d’objet
Enumeration getInetAddresses() InetAddress contenant la liste des adresses IP
associées à l’interface.
Le programme suivant donne la liste de toutes les interfaces liées à notre machine :
Classe NetworkInterfaceSample.java :
package ga.esgis.l3iirtsrs.inet;
import java.io.*;
import java.net.*;
import java.util.*;
import static java.lang.System.out;
Résultat :
Les Sockets sont des objets permettant la gestion de deux flux de données : un flux d’entrée
(InputStream), garantissant la réception de données, et un flux de sortie (OutputStream), servant
à envoyer des données. En Java, on distingue deux types de Sockets : les Sockets Simples dits
« Sockets clients » et les « Sockets serveurs ».
Le protocole TCP offre un service en mode connecté et fiable. Les données sont délivrées dans
l’ordre de leur émission.
Les deux schémas suivants présentent les algorithmes de fonctionnement des clients et serveurs.
Il est à noter que côté serveur on utilise deux sockets : l’un, appelé socket d’écoute, reçoit les
demandes de connexion et l’autre, appelé socket de service, sert pour la communication. En
effet, un serveur peut être connecté simultanément avec plusieurs clients et dans ce cas on
utilisera autant de sockets de service que de clients.
Les classes URL et URLConnection fournissent un mécanisme de haut niveau pour l’accès aux
ressources Internet. Mais parfois, certains programmes nécessitent une communication réseau
de bas niveau, c’est les cas lorsqu’on développe des applications Client/Serveur.
Le package java.net offre une communication bidirectionnelle entre deux programmes qui
s’exécutent sur un réseau. Les classes Socket et ServerSocket sont utilisées pour assurer la
liaison entre le programme client et le programme serveur.
La classe Socket représente en Java les sockets utilisés côtés client ou les sockets de service.
Les Sockets constituent l’élément de base des communications à travers un réseau informatique.
Comme les flux Java sont transformés en format TCP/IP, il est possible de communiquer avec
l’ensemble des ordinateurs qui utilisent ce protocole quel que soit leur système d’exploitation.
La seule chose importante au niveau du système d’exploitation est qu’il soit capable de gérer
ce protocole.
Cette classe encapsule la connexion à une machine distante via le réseau. Elle gère ainsi la
connexion, l’envoi de données, la réception de données et la déconnexion.
a. Constructeurs
La classe Socket possède plusieurs constructeurs dont les principaux sont les suivants :
Constructeur Rôle
Server() Constructeur par défaut.
Crée un Socket sur la machine dont le nom et
ServerSocket(string, int)
le port sont fournis en paramètre.
Crée un Socket sur la machine dont l’adresse
ServerSocket(Inetaddress, int)
IP et le port sont fournis en paramètre.
Ce constructeur crée un socket TCP et tente de se connecter sur le port indiqué de l’hôte visé.
Le premier paramètre de ce constructeur représente le nom de la machine serveur. Si l’hôte est
inconnu ou que le serveur de noms de domaine est inopérant, le constructeur générera une
UnknownHostException. Les autres causes d’échec, qui déclenchent l’envoi d’une IOException
sont multiples : machine cible refusant la connexion sur le port précisé ou sur tous les ports,
problème lié à la connexion Internet, erreur de routage des paquets, etc.
Ce constructeur fonctionne comme le premier, mais prends comme premier paramètre une
instance de la classe InetAddress. Ce constructeur provoque une IOException lorsque la
tentative de connexion échoue mais il ne renvoie pas d’UnknownHostException puisque cette
information est connue lors de la création de l’objet InetAddress.
Comme les deux précédents, ce constructeur crée un socket et tente de se connecter sur le port
indiqué de l’hôte visé. Le numéro du port et le nom de la machine sont fournis dans les deux
premiers paramètres et la connexion s’établit à partir de l’interface réseau physique (choix
d’une carte réseau sur un système possédant plusieurs accès réseau) ou virtuelle (système multi-
adresse) et du port local indiqués dans les deux derniers paramètres.
Si portLocal vaut 0, la méthode (comme les deux premières d’ailleurs) choisit un port
disponible (parfois appelé port anonyme) compris entre 1024 et 65 535.
Comme la première méthode cette méthode peut générer une IOException en cas de problème
de connexion et lorsque le nom d’hôte donné n’est pas correct (il s’agit dans ce cas d’une
UnknownHostException).
Cette méthode est identique à la précédente à la seule différence que le premier paramètre est,
comme pour la seconde méthode, un objet InetAddress.
protected Socket()
protected Socket(SocketImpl impl) throws SocketException
Ces deux derniers constructeurs sont protégés. Ils créent un socket sans le connecter. On utilise
ces constructeurs pour implanter un socket original qui permettra par exemple de chiffrer les
transactions ou d’interagir avec un Proxy.
Il existe de plus deux autres constructeurs qui permettent le choix du protocole via un booléen
mais ces méthodes sont dépréciées et ne doivent pas être utilisées. Pour créer un socket utilisant
UDP on utilisera DatagramSocket.
En dehors de ces constructeurs, la classe Socket possède plusieurs autres méthodes parmi
lesquelles :
Méthode Rôle
Renvoie l’adresse IP à laquelle le Socket est
InetAddress getInetAddress()
connecté.
int getPort() Renvoie le port auquel le Socket est connecté.
void close() Ferme le Socket.
Renvoie un flux en entrée pour recevoir des
InputStream getInputStream()
données du Socket.
Renvoie un flux en sortie pour transmettre
OutputStream getOutputStream()
des données au Socket.
b. Méthodes informatives
public InetAddress getInetAddress ()
public int getPort ()
Ces méthodes renvoient l'adresse Internet et le port distants auquel le socket est connecté.
Ces méthodes renvoient l'adresse Internet et le port locaux que le socket utilise.
Cette méthode renvoie un flux d’entrées brutes grâce auquel un programme peut lire des
informations à partir d’un socket. Il est d’usage de lier cet InputStream à un autre flux offrant
davantage de fonctionnalités (un DataInputStream par exemple) avant d’acquérir les entrées.
Cette méthode renvoie un flux de sortie brutes grâce auquel un programme peut écrire des
informations sur un socket. Il est d’usage de lier cet OutputStream à un autre flux offrant
davantage de fonctionnalités (un DataOutputStream par exemple) avant d’émettre des données.
Bien que Java ferme tous les sockets ouverts lorsqu’un programme se termine ou bien lors d’un
« garbage collect », il est fortement conseillé de fermer explicitement les sockets dont on n’a
plus besoin à l’aide de la méthode close.
Une fois un socket fermé on peut toujours utiliser les méthodes informatives, par contre toute
tentative de lecture ou écriture sur les « input/output streams » provoque une IOException.
Java permet l’accès à un certain nombre d’options qui modifient le comportement par défaut
des sockets. Ces options correspondent à celles que l’on manipule en C via ioctl (ou ioctlsocket
avec Windows).
Si l’on programme une application très interactive on pourra valider l’option TCP_NODELAY.
supprimés lorsque la fermeture à lieu. Si la durée est positive et que valide est à vrai, la méthode
close se fige pendant le nombre de secondes spécifiées en attendant l’expédition des données
et la réception de l’acquittement. Une fois les prolongations écoulées, le socket est clos et les
données restantes ne sont pas transmises.
Si cette option n’est pas validée (valide est à faux) on obtient le comportement par défaut et
l’appel de la méthode getSoLinger() retourne –1, sinon la méthode renvoie la durée des
prolongations.
Par défaut, lors d’une tentative de lecture sur le flux d’entrée d’un socket, read() se bloque
jusqu’à la réception d’un nombre d’octets suffisant. Lorsque SO_TIMEOUT est initialisée,
cette attente ne dépasse pas le temps imparti, exprimé en millisecondes. Tout dépassement de
temps se solde par une une java.net.SocketTimeoutException. Le socket reste malgré tout
connecté.
La valeur par défaut est 0 qui signifie, ici, un laps de temps infini. Il peut être préférable de
choisir une durée raisonnable (>0) pour éviter que le programme se bloque en cas de problème.
L’option doit être validée avant l’appel à l’opération bloquante pour être prise en compte.
Le paramètre doit être >= 0 sans quoi une exception SocketException est générée.
Ces méthodes permettent, grâce aux options SO_SNDBUF et SO_RCVBUF, de préciser une
indication sur la taille à allouer aux tampons d’entrée/sortie bas niveaux. Il s’agit uniquement
d’une indication donc il est conseillé après l’appel d’une méthode set… d’appeler la méthode
get… correspondante pour vérifier la taille allouée.
f. Méthode surchargée
public String toString()
La seule méthode surchargée de la classe Object est toString(). Cette méthode provoque
l’affichage d’une chaîne ressemblant à cela :
En générale, l’algorithme déroulé pour établir une connexion via un Socket est le suivant :
package ga.esgis.l3iirtsrs.inet;
import java.io.*;
import java.net.*;
try {
InetAddress serveur = InetAddress.getByName(args[0]);
// creation d’une socket et connexion à la machine dont le nom est passé
en argument sur le port numéro 9632
leSocket = new Socket(serveur, PORT);
System.out.println("Connecté sur : "+leSocket);
// création d’un flux de type PrintStream lié au flux de sortie du socket
fluxSortieSocket = new PrintStream(leSocket.getOutputStream());
// creation d’un flux de type BufferedReader lié au flux d’entrée du socket
fluxEntreeSocket = new BufferedReader(new
InputStreamReader(leSocket.getInputStream()));
// envoi de données vers le serveur
fluxSortieSocket.println("Bonjour le monde!");
// attente puis réception de données envoyées par le serveur
reponse = fluxEntreeSocket.readLine();
System.out.println("Reponse du serveur : " + reponse);
leSocket.close();
} // try
catch (ConnectException ex)
{
System.err.println("Erreur de connexion au serveur : "+ex);
ex.printStackTrace();
}
catch (UnknownHostException ex)
{
System.err.println("Machine inconnue : "+ex);
ex.printStackTrace();
}
catch (IOException ex)
{
System.err.println("Erreur : "+ex);
ex.printStackTrace();
}
} // main
} // class
package ga.esgis.l3iirtsrs.inet;
import java.net.*;
import java.io.*;
Console c = System.console();
out.println("Veuillez saisir votre compte : ");
String message = c.readLine(in.readLine());
out.println(message);
System.out.println(in.readLine());
socket.close();
} catch (Exception e){
e.printStackTrace();
}
}
La classe ServerSocket est utilisée côté serveur. Elle attend simplement les appels du (ou des)
client(s). Une instance de cette classe est un objet du type Socket qui prend en charge la
transmission de données.
Cette classe permet de créer des sockets qui attendent des connexions sur un port spécifié et
lors d’une connexion retournent un Socket qui permet de communiquer avec l’appelant.
Cette classe représente la partie serveur du Socket. Un objet de cette classe est associé à un port
sur lequel il va attendre les connexions d’un client. Généralement, à l’arrivée d’une demande
de connexion, un thread est lancé pour assurer le dialogue avec le client afin de ne pas bloquer
les connexions des autres clients.
g. Constructeurs
La classe ServerSocket possède plusieurs constructeurs dont les principaux sont les suivants :
Constructeur Rôle
ServerSocket() Constructeur par défaut.
Crée un Socket sur le port fourni en
ServerSocket(int)
paramètre.
Crée un Socket sur le port avec la taille
ServerSocket(int, int)
maximale de la file fournis en paramètre.
Ce constructeur crée un socket serveur qui attendra les connexions sur le port spécifié. Lorsque
l’entier port vaut 0, le port est sélectionné par le système. Ces ports anonymes sont peu utilisés
car le client doit connaître à l’avance le numéro du port de destination. Il faut donc un
mécanisme, comme le port mapper des RPC, qui permet d’obtenir ce numéro de port à partir
d’une autre information.
L’echec de la création se solde par une IOException (ou à partir de Java 1.1 une BindException
qui hérite de IOException) qui traduit soit l’indisponibilité du port choisi soit, sous UNIX, un
problème de droits (sous UNIX les ports inférieurs à 1024 ne sont disponibles que pour le super
utilisateur).
Lorsque la valeur donnée est supérieure à la limite fixée par le système, la taille maximale est
affectée à la file.
Ce constructeur permet en outre de préciser l’adresse Internet locale sur laquelle attendre des
connexions. Ainsi on pourra choisir l’une des interfaces réseau de la machine locale si elle en
possède plusieurs. Si adresseLocale est à null le socket attendra des connexions sur toutes les
adresses locales (ce qui est aussi le cas quand on utilise l’un des deux autres constructeurs).
Méthode Rôle
Socket accept() Attendre une nouvelle connexion.
void close() Fermer le Socket.
Si un client tente de communiquer avec le serveur, la méthode accept() renvoie un Socket qui
encapsule la communication avec ce client.
Cette méthode bloque l’exécution du programme serveur dans l’attente d’une demande de
connexion d’un client. Elle renvoie un objet Socket une fois la connexion établie.
Si vous ne voulez pas bloquer l’exécution du programme il suffit de placer l’appel à accept dans
un thread spécifique.
Cette méthode ferme le socket serveur en libérant le port. Les sockets serveurs sont eux aussi
fermés automatiquement par le système à la fermeture de l’application mais il est fortement
conseillé de les fermer explicitement.
En général, l’algorithme qui est exécuté pour établir une connexion est le suivant :
i. Méthodes informatives
Ces méthodes renvoient l'adresse Internet et le port locaux sur lequel le socket attend les
connexions.
Par défaut, l’appel à accept() se bloque jusqu’à la réception d’une demande de connexion.
Lorsque SO_TIMEOUT est initialisée, cette attente ne dépasse pas le temps imparti, exprimé
La valeur par défaut est 0 qui signifie, ici, un laps de temps infini ce qui convient à la plupart
des serveurs, conçus en général pour s’exécuter indéfiniment.
L’option doit être validée avant l’appel à accept() pour être prise en compte.
Le paramètre doit être >= 0 sans quoi une exception SocketException est générée.
k. Méthode surchargée
La seule méthode surchargée de la classe Object est toString(). Cette méthode provoque
l’affichage d’une chaîne ressemblant à ce qui suit :
ServerSocket[addr=0.0.0.0/0.0.0.0, port=0, localport=80]
Sous Java 1.1 et les précédentes versions addr et port sont toujours nulles. La seule information
utile est donc localport qui indique le numéro du port d’attente des connexions. Cette méthode
est ici aussi plutôt destinée au débogage.
package ga.esgis.l3iirtsrs.inet;
import java.io.*;
import java.net.*;
OutputStream sortieSocket;
try {
System.out.println("Lancement du serveur !");
// Création du socket d’écoute (port numéro PORT)
socketEcoute = new ServerSocket(PORT);
while (true) {
// Attente d’une demande de connexion
socketService = socketEcoute.accept();
System.out.println("Nouvelle connexion : " + socketService);
// Récupération des flux d’entrée/sortie du socket de service
entreeSocket = socketService.getInputStream();
sortieSocket = socketService.getOutputStream();
try {
int b = 0;
while (b != -1) {
b = entreeSocket.read();
sortieSocket.write(b);
} // while
System.out.println("Fin de connexion");
} // try
catch (IOException ex)
{
// Fin de connexion
System.out.println("Fin de connexion : "+ex);
ex.printStackTrace();
}
socketService.close();
} // while (true)
} // try
catch (Exception ex)
{
// Erreur de connexion
System.err.println("Une erreur est survenue : "+ex);
ex.printStackTrace();
}
} // main
} // class
package ga.esgis.l3iirtsrs.inet;
import java.net.*;
import java.io.*;
La limite de ce programme est qu’il ne peut traiter qu’une seule connexion à la fois. Si plusieurs
demandes de connexion arrivent simultanément, une seule est acceptée ; les autres sont mises
en attente jusqu’à ce que la première termine sa connexion. Pour pouvoir traiter plusieurs
connexions en même temps (plusieurs clients), il faut que le serveur soit multithreadé.
Exemple : TCPServerSocketSampleThread.java
package ga.esgis.l3iirtsrs.inet;
import java.net.*;
import java.io.*;
try {
ServerSocket socketServeur = new ServerSocket(PORT);
System.out.println("Lancement du serveur !");
while (true) {
Socket socketClient = socketServeur.accept();
TCPServerSocketSampleThread serveur = new
TCPServerSocketSampleThread(socketClient);
}
} catch (Exception e) {
e.printStackTrace();
}
Le protocole UDP offre un service non connecté et non fiabilisé. Les données peuvent être
perdues, dupliquées ou délivrées dans un ordre différent de leur ordre d’émission.
Le fonctionnement est ici symétrique. Chaque processus crée un socket et l’utilise pour envoyer
ou attendre et recevoir des données.
En Java on utilise la classe DatagramPacket pour représenter les datagrammes UDP qui sont
échangés entre les machines.
a. Constructeurs
public DatagramPacket(byte[] tampon, int longueur)
Ce constructeur crée un objet utilisé pour recevoir les données contenues dans des datagrammes
UDP reçus par la machine. Le paramètre tampon doit correspondre à un tableau d’octets (type
byte en java) correctement créé (Attention ! si le tableau n’est pas assez grand les données en
excès seront détruites). Le paramètre longueur indique la longueur du tableau.
Exemple d’utilisation :
byte[] tampon = new byte[1024];
DatagramPacket datagramme = new DatagramPacket(tampon, tampon.length);
Ce constructeur crée un objet utilisé pour envoyer un datagramme UDP. Le paramètre tampon
doit correspondre aux données à envoyer. Le paramètre longueur doit indiquer la longueur des
données à envoyer. Le paramètre adresse et le paramètre port doivent indiquer respectivement
l’adresse IP et le port de destination.
Il est à noter qu’il existe d’autres constructeurs qui permette d’indiquer un offset dans un tableau
de grande dimension. Ils doivent être utilisés pour envoyer un tableau dont la taille dépasse la
taille maximale des datagrammes IP.
Exemple d’utilisation :
String message = "Bonjour le monde!";
byte[] tampon = message.getBytes();
InetAddress adresse = InetAddress.getByName("marine.edu.ups-tlse.fr");
DatagramPacket datagramme = new DatagramPacket(tampon, tampon.length,
adresse, 7);
b. Accesseurs
public byte[] getData()
public void setData(byte[] buf)
Ces méthodes permettent de récupérer et de changer le tableau d’octets utilisé soit pour recevoir
soit pour envoyer un datagramme UDP.
Ces méthodes permettent de récupérer et de changer la longueur du tableau d’octets utilisé soit
pour recevoir soit pour envoyer un datagramme UDP.
a. Constructeurs
public DatagramSocket() throws SocketException
Ce constructeur crée un socket UDP qui permet d’envoyer et recevoir des datagrammes UDP.
Le port qu’utilise ce socket est choisi par le système d’exploitation. De même l’adresse IP
qu’utilise ce socket est choisie automatiquement par le système.
Ce constructeur crée un socket UDP qui permet d’envoyer et recevoir des datagrammes UDP.
Le port qu’utilise ce socket est indiqué par le paramètre port. L’adresse IP qu’utilise ce socket
est choisie automatiquement par le système.
Ce constructeur crée un socket UDP qui permet d’envoyer et recevoir des datagrammes UDP.
Le port qu’utilise ce socket est indiqué par le paramètre port. L’adresse IP qu’utilise ce socket
est indiqué par le paramètre adresse.
Ces trois constructeurs peuvent générer des exceptions de type SocketException s’il y a un
problème de configuration réseau, si le port choisi est déjà utilisé ou si l’adresse choisie n’est
pas l’une des adresses de la machine locale.
Envoie un datagramme UDP qui contiendra toutes les informations référencées par l’objet p.
Attends un datagramme UDP et lors de sa réception stocke ses informations dans l’objet p.
Des exceptions de type IOException peuvent être générées si un problème d’entrée/sortie
survient.
c. Méthodes informatives
public InetAddress getLocalAddress ()
public int getLocalPort ()
Ces méthodes renvoient l'adresse Internet et le port locaux que le socket utilise.
d. Fermeture du socket
public void close() throws IOException
Bien que Java ferme tous les sockets ouverts lorsqu’un programme se termine ou bien lors d’un
« garbage collect », il est fortement conseillé de fermer explicitement les sockets dont on n’a
plus besoin à l’aide de la méthode close.
Une fois un socket fermé on peut toujours utiliser les méthodes informatives, par contre toute
tentative d’émission ou de réception de datagrammes UDP provoque une IOException.
e. Options
Comme pour les sockets UDP on peut positionner un certain nombre d’options qui modifient
le comportement par défaut des sockets.
Par défaut, lors d’une tentative de réception d’un datagramme UDP, receive se bloque jusqu’à
la réception d’un datagramme. Lorsque SO_TIMEOUT est initialisée, cette attente ne dépasse
pas le temps imparti, exprimé en millisecondes. Tout dépassement de temps se solde par une
java.net.SocketTimeoutException. Le socket reste malgré tout connecté.
La valeur par défaut est 0 qui signifie, ici, un laps de temps infini. Il peut être préférable de
choisir une durée raisonnable (>0) pour éviter que le programme se bloque en cas de problème.
L’option doit être validée avant l’appel à l’opération bloquante pour être prise en compte.
Le paramètre doit être >= 0 sans quoi une exception SocketException est générée.
Ces méthodes permettent, grâce aux options SO_SNDBUF et SO_RCVBUF, de préciser une
indication sur la taille à allouer aux tampons d’entrée/sortie bas niveaux. Il s’agit uniquement
d’une indication donc il est conseillé après l’appel d’une méthode set… d’appeler la méthode
get… correspondante pour vérifier la taille allouée.
Cette classe permet d’utiliser le multicasting IP pour envoyer des datagrammes UDP à un
ensemble de machines repéré grâce à une adresse multicast (classe D dans IP version 4 : de
224.0.0.1 à 239.255.255.255).
a. Constructeurs
b. Abonnement/résiliation
Pour pouvoir recevoir des datagrammes UDP envoyés grâce au multicasting IP il faut s’abonner
à une adresse multicast (de classe D pour IP version 4). De même lorsqu’on ne souhaite plus
recevoir des datagrammes UDP envoyés à une adresse multicast on doit indiquer la résiliation
de l’abonnement.
c. Choix du TTL
Dans les datagrammes IP se trouve un champ spécifique appelé TTL (Time To Live – durée
de vie) qui est normalement initialisé à 255 et décrémenté par chaque routeur que le datagramme
traverse. Lorsque ce champ atteint la valeur 0 le datagramme est détruit et une erreur ICMP est
envoyé à l’émetteur du datagramme. Cela permet d’éviter que des datagrammes tournent
infiniment à l’intérieur d’un réseau IP.
Le multicasting IP utilise ce champ pour limiter la portée de la diffusion. Par exemple avec un
TTL de 1 la diffusion d’un datagramme est limitée au réseau local.
Ces méthodes permettent la consultation et la modification du champ TTL qui sera écrit dans
les datagrammes envoyés par ce socket.
Classe UDPSendDatagram.java
package ga.esgis.l3iirtsrs.inet;
import java.net.*;
import java.io.*;
Classe UDPRecieveDatagram.java
package ga.esgis.l3iirtsrs.inet;
import java.net.*;
import java.io.*;
class UDPRecieveDatagram {
public static void main(String argv[]) throws SocketException, IOException {
byte[] tampon = new byte[1000];
String texte;
// Crée un socket UDP qui attend des datagrammes sur le port 50000
DatagramSocket socket =new DatagramSocket(50000);
XV.2.2.6. Exemple de programme qui envoie et reçoit des datagrammes avec le multicasting
IP
Classe UDPMultiCastingIP.java
package ga.esgis.l3iirtsrs.inet;
import java.net.*;
import java.io.*;
while (true) {
byte[] tampon = new byte[1024];
DatagramPacket reception = new DatagramPacket(tampon, tampon.length);
// Attend les réponses
s.receive(reception);
String texte=new String(tampon, 0, reception.getLength());
System.out.println("Reception de la machine "+
reception.getAddress().getHostName()+
" sur le port "
+reception.getPort()+" :\n"+
texte );
}
// si la boucle n’était pas infinie on pourrait écrire:
// s.leaveGroup(groupe);
} //main
} //class