:: Enseignements :: Master :: M1 :: 2013-2014 :: Programmation d'applications réseaux ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Adresses IP et UDP |
Exercice 1 - Adresses IP, client et serveur UDP
Copier-coller le code ci-dessous dans un nouveau projet Java eclipse.
-
Quelles informations sont utilisées lors de l'exécution de ce
programme sans arguments?
-
Utiliser ce programme avec un argument sur la ligne de commande
pour afficher les noms et adresses IP des machines suivantes :
-
la machine connue sous le nom www.u-pem.fr ;
-
la machine dont l'adresse IP est 193.55.63.80 ;
-
la machine connue sous le nom igm.univ-mlv.fr ;
-
la machine dont l'adresse IP est 300.55.63.80.
Indiquer, pour chacune de ces machines, s'il y a résolution de nom.
-
Modifier le code pour connaitre le nom (HostName) et le nom
canonique (CanonicalHostName) de chaque machine ci-dessus.
Tester l'affichage par défaut (toString()) avant et après
la recherche du nom et du nom canonique: que peut on en déduire?
-
Tester le programme ci-dessus plusieurs fois consécutives avec le
nom www.google.com. Que peut on en déduire?
Compléter le programme pour que toutes les adresses associées au nom
passé en argument sur la ligne de commande soient affichées.
Exercice 2 - Client echo UDP
Écrire un client UDP permettant d'interroger le démon
Echo (RFC 862) qui est accessible sur le port 7 d'une machine.
Vous pouvez tester ce service depuis les machines des salles de TP (*) en lancant la commande
nc -u gaspard 7
Contrairement à
nc, on attend de votre client qu'il soit verbeux
et donne de nombreuses informations. Par exemple :
$ java fr.upem.net.udp.EchoUDPClient gaspard 7 "Mon beau message, roi des machines"
socket locale attachée à l'adresse 0.0.0.0/0.0.0.0:1044
34 octets émis vers gaspard/193.55.63.81:7
capacité de la zone de stockage : 44
34 octets recus
contenant : Mon beau message, roi des machines
provenant de : gaspard.univ-mlv.fr/193.55.63.20:7
Vous utiliserez pour cela les classes
java.net.DatagramSocket et
java.net.DatagramPacket.
Vous utiliserez le même DatagramPacket pour la réception et pour l'envoi.
Puisqu'on utilise le protocole Echo, on utilisera l'encodage par
défaut de la machine cliente lors de l'envoi et de la réception
des messages texte.
(*) si vous travaillez sur une autre machine qui ne peut pas accéder à
gaspard,
vous pouvez utiliser le serveur
UDPUpperCaseServer.jar
en le démarrant dans un autre terminal avec:
java -jar UDPUpperCaseServer.jar 7777
et en attaquant ce serveur sur le port
7777 de
localhost. Par exemple:
nc -u localhost 7777
Exercice 3 - Serveur echo UDP
Écrire maintenant un serveur EchoUDPServer très simple qui
va pouvoir jouer le rôle du service fourni par la machine gaspard.
Ce serveur doit contenir une méthode main(), qui accepte sur la ligne
de commande un entier représentant le port d'écoute, un constructeur, qui
accepte ce numéro de port en paramètre et crée et attache une DatagramSocket
sur ce port, et enfin une méthode launch() sans argument qui place le serveur
en attente de réception de paquet UDP en provenance de clients; lorsqu'un paquet est
reçu, le serveur se contente de renvoyer son contenu à l'expéditeur, et recommance
ainsi de suite en boucle.
Faut-il fixer une taille maximale des données que le serveur peut reçevoir dans un paquet et pourquoi?
Testez votre serveur d'une part avec nc et d'autre part avec le client
EchoUDPClient précédemment écrit; quel est l'encodage de caractère
utilisé par les clients? Par le serveur?
Exercice 4 - Respecte mon encodage
On souhaite écrire un client UDP permettant d'interroger un
serveur de mise en majuscule de chaînes de caractères.
Pour illustrer l'importance de l'encodage des chaînes de
caractères, nous allons utiliser un serveur qui est paramétré
pour prendre en charge un encodage précis. Pour ce
faire récupérez le serveur
UDPUpperCaseServer.jar
et lancez le sur votre machine avec un encodage différent de celui
par défaut (e.g. Latin1 si vous êtes sous UTF-8 et inversement).
Vous pouvez tester avec netcat (nc) que si l'encodage de la
machine cliente et celui précisé au lancement du serveur
correspondent, la mise en majuscule s'effectue sans problème, mais
que dans le cas où ils ne correspondent pas la mise en majuscule ne
s'effectue pas (e.g. pour les lettres accentuées).
Par exemple, si on lance:
$ java -jar UDPupperCaseServer.jar 4545 Latin1
alors, dans un terminal configurés en UTF-8, on devrait obtenir ces problèmes:
$ nc -u localhost 4545
bla
BLA
éï€
éï�
Tandis que si le serveur est lancé par:
$ java -jar UDPupperCaseServer.jar 4545 UTF-8
alors la mise en majuscule fonctionne:
$ nc -u localhost 4545
bla
BLA
éï€
ÉÏ€
Écrire un client UDP permettant d'interroger le serveur de mise en
majuscule lancé sur un port donné d'une machine host.
On attend un fonctionnement du type :
$ java fr.upem.net.udp.EncodedUDPClient 10.2.5.10 7777 "Autant arrêter le Java si c'est pour ça!" Latin1
socket locale attachée :
à l'adresse 0.0.0.0/0.0.0.0 au port 1044
34 octets émis vers 10.2.5.10/10.2.5.10
capacité de la zone de stockage : 44
34 octets recus
contenant : AUTANT ARRÊTER LE JAVA SI C'EST POUR ÇA!
provenant : de 10.2.5.10/10.2.5.10:7777
Transformez votre code pour lire les chaînes de
caractères au clavier et
permettre plusieurs mises en majuscules.
Le jar accepte un 3ème argument (optionnel) sur la ligne de commande, qui correspond
à la fiabilité (1, par défaut, si on le passe à 0.5, il jettera un paquet sur 2).
Modifiez votre client de manière à afficher
Le serveur n'a pas répondu
si le serveur n'a pas répondu en moins d'une seconde.
Exercice 5 - Manipulation de bits et de bytes d'adresses
En général, l'ordre de transmission des octets sur le réseau,
quelque fois appelé "network order", est l'ordre Big Endian:
l'octet de poids fort de l'entier enregistré à l'adresse mémoire la plus petite.
Voir
Endianness sur Wikipedia
pour plus d'infos.
Par ailleurs, en Java, tous les types primitifs numériques sont signés, ce qui signifie
par exemple que l'octet (
byte) dont tous les bits sont à
1 vaut
-1
et non
255 (voir encore
Wikipédia)!
Autre problème: lorsqu'on fait une opération sur entier d'un type primitif
plus petit que
int, Java réalise systématiquement une
promotion entière,
c'est à dire qu'il convertit cette valeur entière en un
int (32 bits) avant de réaliser
l'opération sur cette valeur. Ainsi par exemple, si
b est de type
byte et
vaut
-1 (il est donc représenté sur un octet constitué de 8 bits à 1), alors pour réaliser
l'opération
b + 1, la valeur de
b va être promue en un entier de type
int.
Cet
int sera donc codé sur 32 bits, et ils seront tous à
1 pour que la valeur de l'entier
soit toujours
-1 (c'est l'extension du bit de signe). Le résultat de l'addition sera donc
-1 + 1 = 0
mais sera stockée dans un
int sur 32 bits, et donc le code ci-dessous
byte b = -1;
b = b + 1; // erreur de compilation: Type mismatch: cannot convert int to byte
ne compile pas. Il faudra donc caster le résultat en
byte pour pouvoir réaliser l'affectation:
b = (byte) (b + 1); // OK
Si l'on s'intéresse à la valeur "non signée" représentée dans un
byte (pour obtenir par exemple
255 si tous les bits d'un octet sont à 1), il faut appliquer un
masque qui
permettra de "cacher" l'effet de l'extension du bit de signe. Ce masque fait un ET bit à bit avec une
valeur dont la représentation binaire vaut 0 sur les bits qu'on veut cacher et 1 sur les bits qu'on
veut garder. Exemple (
0xFF en Hexa vaut
11111111 en binaire et
255 en décimal):
byte b = -1;
System.out.println(b); // affiche: -1
System.out.println(b & 0xFF); // affiche: 255
-
Sachant que les long sont codés sur 8 octets,
implémenter deux méthodes de conversion
void longToByteArray(long value, byte[] array) et long byteArrayToLong(byte[] array)
permettant les conversions entre long et tableau de
byte en Big Endian. Tester ces méthodes en les appelant
l'une après l'autre et en vérifant que vous récupérez bien l'entier orginal,
par exemple en complétant cette classe:
package fr.upem.net.udp;
/**
* This utility class offers conversion static methods between primitive types
* and byte arrays for network transport.
*/
public class Converter {
/**
* Returns the long primitive value being represented by the argument byte
* array in network order (big endian). The lowest index in the array
* contains the most significant byte of the long value.
* @param array the byte array representing a long value in network order.
* @return the long value represented by the byte array.
* @throws IllegalArgumentException if the byte array size is incompatible
* with the long representation.
*/
public static long byteArrayToLong(byte[] array) throws IllegalArgumentException {
// TODO
}
/**
* Assigns in the specified array the bytes of the specified long
* primitive value in network order representation (big endian).
* The most significant byte in the long value is stored at the lowest
* index in the array.
* @param value the primitive long value.
* @param array the byte array representing a long value in network order.
* @throws IllegalArgumentException if the byte array size is incompatible with
* the long representation.
*/
public static void longToByteArray(long value, byte[] array) {
// TODO
}
public static void main(String[] args) {
long l = Long.parseLong(args[0]);
byte[] array = new byte[8];
longToByteArray(l, array);
long l2 = byteArrayToLong(array);
System.out.println((l==l2));
}
}
© Université de Marne-la-Vallée