Programmation réseaux
Master 1 CSIA
III - Programmation par
Socket UDP
• Jusqu’a present, nous avons discuté des applications réseaux qui
s’exécutent au dessus du protcole TCP, de la couche transport.
• TCP est conçu pour des transmissions fiables des données.
Si des paquets arrivent en désordre, TCP les remet en ordre.
Si des données sont perdues ou endommgées, TCP s’assure qu’elles seront retransmises.
Si les données arrivent trop rapidemment pour la connexion, TCP réduit la vitesse pour que
les paquets ne soient pas perdus.
• Un programe n’a pas à se soucier des données reçues qui sont
incorrectes ou en désordre.
• Cependant, cette fiabilité a un prix. Ce prix est la vitesse.
Etablir et couper une connexion TCP peut prendre un temps non négligeable, surtout pour
des protocoles tels que HTTP, qui tendent à néccesiter plusieurs transmissions courtes.
• Le protocole UDP (un autre protcole, de la couche transport) est une
alternative pour l’envoi de données sur IP de manière trés rapide,
mais non fiable
.
• Lorsqu’on envoie des données UDP nous n’avons aucun moyen de savoir
si ells sont bien arrivées; encore moins de savoir si les données qui
arrivent sont bien dans l’ordre dans lequel elles ont été envoyées.
• Cependant, les morceaux de données qui sont reçues arrivent
généralement rapidemment.
• La question qu’on peut se poser, alors, est: pourquoi quelqu’un
voudrait-il utiliser un protocole non fiable?
• Il y a plusieurs types d’applications dans lequelles la vitesse brute
est plus importante que le fait de recevoir chaque bit correctement.
Par exemple, dans l’audio ou la video temps réel , les paquets de données perdus ou
intervertis apparaissent simplement comme statiques. Ce phénomène est tolérable, mais les
pauses génantes dans le stream audio, lorsque TCP demande une retransmission ou attends
un paquet qui tarde à arriver, sont inacceptables.
• Dans d’autres applications, les tests de fiabilité peuvent être réalisés
dans la couche application.
Par exemple, si un client envoie une courte requête UDP à un serveur, il peut supposer que
le paquet est perdu si aucune réponse n’est retournée au bout d’un certain lapse de temps; à
la manière du DNS (il peut aussi opérer sur TCP).
• Java supporte la communication par Datagrammes à travers les deux
classes suivantes :
DatagramPacket
DatagramSocket
• La classe DatagramPacket compacte des octets de données dans des
paquets UDP appelés datagrammes et permet de décompacter les
datagrammes reçus.
• La classe DatagramSocket envoie et recoit des datagrammes UDP.
Pour envoyer des données, on met les données dans DatagramPacket
et on envoie le paquet en utilisant DatagramSocket. Pour recevoir des
données, on prend un objet DatagramPacket d’un DatagramSocket,
puis on inspecte le contenu du paquet.
• Dans UDP, tout ce qui concerne un datagramme, y compris l’adresse à
laquelle il est destine, est inclu dans le paquet lui même, la socket a,
seulement, besoin de connaître le port local sur lequel écouter ou
envoyer.
• Un seul DatagramSocket peut envoyer des données à, et recevoir des
données de, plusieurs machines indépendantes. La socket n’est pas
dédiée à une seule connexion, comme elle l’est dans TCP. En fait, UDP
n’a aucune notion d’une connexion entre deux machines hôtes.
• Les sockets TCP traitent une connexion réseau comme un stream.
UDP ne supporte pas cela; on travaille toujours avec des paquets
individuels. Un paquet n’est pas nécessairement relié au suivant. Etant
donné deux paquets, il n’y a pas moyen de déterminer lequel fût
envoyé en premier.
• La classe DatagramPacket utilise différents constructeurs selon que
la packet est utilisé pour transmettre ou reçevoir des données.
• Les deux constructeurs qui créent un nouveau objet DatagramPacket
pour recevoir des données du réseau sont :
public DatagramPacket(byte[] buffer, int length)
public DatagramPacket(byte[] buffer, int offset, int length)
• Lorsqu’une socket reçoit un datagramme, elle sauvegarde la partie
données du datagramme dans buffer commencant à buffer[0] (ou
buffer[offset] et continue jusqu’à ce que le paquet soit
complètement sauvegardé ou que length octets soient écrits dans
buffer.
• Les quatre constructeurs qui créent un nouveau objet
DatagramPacket pour transmettre des données sur le réseau sont :
public DatagramPacket(byte[] data, int length,
InetAddress destination, int port)
public DatagramPacket(byte[] data, int offset, int length,
InetAddress destination, int port)
public DatagramPacket(byte[] data, int length,
SocketAddress destination)
public DatagramPacket(byte[] data, int offset, int length,
SocketAddress destination)
DatagramPacket a six méthodes qui récupèrent les différentes parties
d’un datagramme: les données plus plusieurs champs de son entête. Ces
méthodes sont utilisées principalement pour les datagrammes reçus du
réseau.
• public InetAddress getAddress()
Cette méthode est communément utilisée pour déterminer
l’adresse de la machine hôte qui a envoyé un datagramme UDP,
pour que le destinataire puisse répondre.
• public int getPort()
Cette méthode retourne un integer qui spécifie le port distant.
• public SocketAddress getSocketAddress()
Cette méthode retourne un objet SocketAddress qui contient
l’adresse IP et le port de la machine hôte distante.
• public byte[] getData()
Cette méthode retourne un tableau d’octets contenant les
données du datagramme.
• Il est souvent nécessaire de convertir les octets en quelque autre
forme de données avant qu’ils puissent être utiles pour notre
programme.
Une manière de faire est de changer le tableau d’octets en une chaîne de
caractères(String) String s = new String([Link](), "UTF-8");
• Si le datagramme ne contient pas du texte, le convertir en des
données Java est plus difficile.
Une approche est de convertir le tableau d’octets retourné par getData() en un
ByteArrayInputStream. Par exemple :
InputStream in = new ByteArrayInputStream([Link](),
[Link](), [Link]());
On doit spécifier offset et length.
Le ByteArrayInputStream peut alors être chaîné à un DataInputStream:
DataInputStream din = new DataInputStream(in);
Les données peuvent être lues en utilisant les methods de DataInputStream:
readInt(), readLong(), readChar(), et autres.
• public int getLength()
Cette méthode retourne le nombre d’octets de données dans le
datagramme. Ce n’est pas nécessairement le même que la longeur
du tableau retournée par getData().
• public int getOffset()
Cette méthode retourne simplement le point dans le tableau
retourné par getData() où les données du datagramme
commencent.
• Pour échanger des DatagramPacket, on doit ouvrir une socket de
datagramme. En Java, une telle socket est créée et accèdée à travers
la classe DatagramSocket.
• Tout les sockets de datagramme s’attachent à un port local, sur
lequel ils se mettent à l’écoute de données arrivantes et qu’ils placent
dans l’entête des datagrammes partants.
• IL n’y a pas de distinction entre les sockets client et les socket
serveur, comme cela est la cas avec TCP.
Le seul détail qui les différencie est que dans la cas d’une socket client le port est
anonyme(assigné par le système) alors que dans le cas d’une socket serveur le port doit être
connu des clients.
• Les constructeurs de DatagramSocket sont utilisés dans différentes
situations, comme ceux de DatagramPacket.
• public DatagramSocket() throws SocketException
Ce constructeur crée une socket qui est attachée à un port
anonyme (pas de souci pour trouver un port libre). Il est utilisé
pour un client qui initie une conversation avec un serveur.
• public DatagramSocket(int port) throws SocketException
Ce constructeur crée une socket pour se mettre à l’écoute des
datagrammes qui arrivent sur port particulier, spécifié par
l’argument port. Utilser ce constructeur pour écrire un serveur
qui écoute sur un port bien connu.
• public DatagramSocket(int port, InetAddress interface) throws
SocketException
Ce constructeur crée une socket pour se mettre à l’écoute des
datagrammes qui arrivent sur un port et une interface réseau
spécifiques.
• public DatagramSocket(SocketAddress interface) throws
SocketException
Similaire au precedent sauf que l’adresse de l’interface réseau et
le port sont lus à partir d’une SocketAddress.
• La tache première de la classe DatagramSocket est d’envoyer et
recevoir des datagrammes UDP.
Une socket peu aussi bien envoyer que recevoir. En effet, elle peu envoyer et recevoir à et
de multiple machines en même temps.
• public void send(DatagramPacket dp) throws IOException
Envoie le paquet en le passant à la méthode send de la socket.
• public void receive(DatagramPacket dp) throws IOException
Cette méthode recoit un seul datagramme UDP à partir du réseau
et le sauvegarde dans un objet DatagramPacket préexistent.
C’est une opération blocante.
Si le programme fait autre chose en plus d’attendre les datagrammes, on doit penser à
appeler receive() dans un thread séparé
Exemple :
Un simple serveur UDP qui attend des requêtes d’un client, accepte le
datagramme envoyé puis renvoie le même message :
// [Link]: A simple UDP server program.
import [Link].*;
import [Link].*;
Public class UDPServer {
public static void main(String args[]) throws SocketException, IOException, UnknownHostException {
DatagramSocket aSocket = null;
try {
int socket_no = 1234
aSocket = new DatagramSocket(socket_no);
byte[] buffer = new byte[1000];
while(true) {
DatagramPacket request = new DatagramPacket(buffer, [Link]);
[Link](request);
DatagramPacket reply = new
DatagramPacket([Link](),[Link](),[Link](), [Link]());
[Link](reply); }
}
finally { if (aSocket != null) [Link]();}
}}
// [Link]: A simple UDP client program.
import [Link].*;
import [Link].*;
public class UDPClient {
public static void main(String args[]) throws SocketException, IOException, UnknownHostException {
try {
aSocket = new DatagramSocket();
String env = “Bonjour”;
byte [] m = [Link]();
InetAddress aHost = [Link](“ localhost”);
int serverPort = 1234;
DatagramPacket request = new DatagramPacket(m, [Link](), aHost, serverPort);
[Link](request);
byte[] buffer = new byte[1000];
DatagramPacket reply = new DatagramPacket(buffer, [Link]);
[Link](reply);
[Link](“Reply: ” + new String([Link]()));
}
finally { if (aSocket != null) [Link](); }
}}