L'examen est composé de trois exercices indépendants qui peuvent être traités dans l'ordre que vous voulez.
A titre indicatif, le premier exercice sera noté sur environ 9 points, le deuxième exercice 6 points et le troisième sur 5 points. Ce barème est fournit à titre indicatif et est susceptible d'évoluer. Il vous est donné pour vous permettre de répartir votre temps.
Rappel : vous devez créer un projet Java dont les sources seront stockées dans répertoire EXAM présent dans le home de votre session de TP noté (au lieu d'être dans le workspace d'Eclipse comme c'est le cas par défaut).
Vérifier bien que tous vos fichiers sont dans le répertoire EXAM. Tout ce qui n'est pas dans ce répertoire est perdu quand vous vous déconnectez (ou en cas de panne).
La Java doc est ici.
Le but de l'exercice est de réaliser un client UDP bloquant pour protocole Pokemon décrit ci-dessous.
Par exemple, si un client veut des informations sur le Pokemon "Sylveon" :
00 00 00 07 53 79 6C 76 65 6F 6E
Comme ce pokemon a les caractéristique suivantes : Heroism=18, Sp€€d=20 et Charm=0, le serveur répondra avec le paquet suivant :
53 79 6C 76 65 6F 6E 00 48 65 72 6F 69 73 6D 00 00 00 00 12 53 70 E2 82 AC E2 82 AC 64 00 00 00 00 14 43 68 61 72 6D 00 00 00 00 00qui se décompose comme suit :
53 79 6C 76 65 6F 6E // Sylveon en UTF8 00 // octet valant 0 48 65 72 6F 69 73 6D // Heroism en UTF8 00 // octet valant 0 00 00 00 12 // 18 : Int en BigEndian 53 70 E2 82 AC E2 82 AC 64 // Sp€€d en UTF8 00 // octet valent 0 00 00 00 14 // 20 : Int en BigEndian 43 68 61 72 6D // Charm en UTF8 00 // octet valant 0 00 00 00 00 // 0 : Int en BigEndian
Dans un premier temps, on va supposer qu'aucun paquet ne peut être perdu ou retardé et vous implémenterez un client qui fonctionne dans ce cas là pour le protocole Pokemon. Ce client lira une liste de noms de Pokemons dans un fichier et demandera, pour chacun, ses caractéristiques à un serveur implémentant le protocole Pokemon. Il écrira les résultats dans un fichier. Si le paquet correspondant à un Pokemon dépasse la taille maximum du protocole Pokemon, ce Pokemon est simplement ignoré.
Plus précisément, le client prendra en paramètres sur la ligne de commande les informations suivantes :
Dans une classe ClientPokemon
,
écrire un client UDP bloquant comme décrit ci-dessus.
Vous pouvez utiliser la trame ci-après comme point de départ :
ClientPokemon.java. La trame contient le code qui parse le fichier d'entrée en une List<String>
. Elle contient un record Pokemon(String name, Map<String,Integer> characteristics)
et le code nécessaire pour écrire une List<Pokemon>
dans un fichier au format demandé.
Vous testerez votre client en utilisant le serveur fourni ServerPokemon.jar qui se démarre en fournissant le numéro de port d'écoute :
% java -jar ServerPokemon.jar 7777
Vous pouvez testez votre client avec cette liste de Pokemons : pokemons.txt.
Si vous lancez votre client avec cette liste :
% java fr.uge.net.udp.exam2223.ex1.ClientPokemon pokemons.txt pokemons-out.txt localhost 7777
vous devriez obtenir une fichier pokemons-out.txt contenant :
Charizard;Gluttony:12;Heroism:7;Stealth:20 Pikachu;Heroism:19;Gluttony:17 Gardevoir;Heroism:12;Str€ngth:2;Gluttony:1;Mon$y:20;Stealth:11 Sylveon;Sp€€d:20;Heroism:18;Charm:0 Lucario;Loneliness:20 Gengar;Charm:16;Heroism:1;Gluttony:2;Mon$y:10;Stealth:15 Umbreon;Charm:3;Str€ngth:8;Sp€€d:3;Heroism:11;Stealth:11 Garchomp;Str€ngth:2 Mimikyu;Mon$y:12;Stealth:3;Charm:10;Str€ngth:4 Rayquaza;Gluttony:2;Mon$y:18;Sp€€d:20;Str€ngth:2;Heroism:20;Stealth:16 Greninja;Mon$y:18;Stealth:17;Sp€€d:15;Str€ngth:17
Les caractéristiques peuvent apparaître sur la ligne dans un ordre différent. Ce n'est pas grave du moment que les valeurs sont les mêmes.
On veut maintenant prendre en compte le fait que des paquets UDP peuvent être perdus entre le client et le serveur, dans un sens comme dans l'autre. Les identifiants de requêtes vont donc devoir être utilisés par le client pour identifier à quelle requête correspond un paquet reçu.
L'attente en réception d'un paquet ne doit pas dépasser TIMEOUT
millisecondes. Attention, la contrainte est moins forte que pour les TPs, nous n'exigeons pas qu'en cas de perte ou de retard, la question soit reposée exactement après TIMEOUT
millisecondes.
Le TIMEOUT
sera une constante de votre client qui vaudra 300.
Recopiez votre classe ClientPokemon
dans une nouvelle classe
ClientPokemonFull
que vous allez faire évoluer pour tenir compte des pertes de paquets.
Récupérez le jar UDPProxy.jar et démarrez-le entre votre client et le serveur. Dans trois fenêtres de terminal différentes, exécutez :
% java -jar ServerPokemon.jar 4545
$ java -jar UDPProxy.jar 7777 localhost 4545 -no-swap
$ java fr.uge.net.udp.exam2223.ex1.ClientPokemonFull pokemons.txt pokemons-out.txt localhost 7777
et vérifiez que le fichier pokemons-out.txt produit est correct.
Dans cet exercice, on cherche à réaliser un serveur bloquant pour le protocole Chat décrit ci-après. Le protocole fournit un service extrêmement rudimentaire de messagerie par UDP.
Dans le protocole Chat, les clients choisissent un pseudonyme et envoient par le serveur des messages à d'autres clients en donnant leur pseudonyme.
Plus précisément,les clients envoient des paquets qui contiennent 3 chaînes de caractères :
+--------------------------------------------------------------------+ | login_sender (STRING) | login_receiver (STRING) | message (STRING) | +--------------------------------------------------------------------+
Chaque chaîne str
est encodée de la manière suivante :
str
encodée en UTF8,str
encodée en UTF8.Le chaîne login_sender correspond au pseudonyme de l’expéditeur, login_receiver correspond au pseudonyme du destinataire et la chaîne message est le message que l'on veut envoyer.
La taille maximale d'un paquet ne peut pas excéder 2048 octets.
Le serveur va associer aux adresses des clients des identifiants de la manière suivante.
Lorsque le serveur reçoit un paquet d'un client, si c'est la première fois que le serveur reçoit un paquet d'un client, il attribue à ce client l'identifiant login_sender qui se trouve dans le paquet.
Sinon, si cet identifiant est déjà attribué à un autre client, il ignore le paquet (s'il ne contient pas le même pseudonyme dans le champs login_sender.
Enfin, s'il a un client dont l'identifiant est login_receiver, il lui envoie le paquet contenant le login_sender de l'expéditeur suivi du message :
+------------------------------------------+ | login_sender (STRING) | message (STRING) | +------------------------------------------+
Par exemple supposons que le serveur vient de démarrer, il n'a reçu aucun paquet. Un client qui prend le pseudonyme Alice veut envoyer au client Bob le message Hello€. Il envoie au serveur le paquet :
00 00 00 05 41 6C 69 63 65 00 00 00 03 42 6F 62 00 00 00 08 48 65 6C 6C 6F E2 82 ACqui se décompose comme suit :
00 00 00 05 // 5 : la taille de la chaîne Alice encodée en UTF-8 41 6C 69 63 65 // la chaîne Alice encodée en UTF-8 00 00 00 03 // 3 : la taille de la chaîne Bob encodée en UTF-8 42 6F 62 // la chaîne Bob encodée en UTF-8 00 00 00 08 // 8 : la taille de la chaîne Hello€ encodée en UTF-8 48 65 6C 6C 6F E2 82 AC // la chaîne Hello€ encodée en UTF-8
Quand le serveur reçoit ce paquet, il va associer l'adresse d'Alice et le pseudonyme Alice. Comme il ne connaît pas de client pour le pseudonyme Bob, il ignore le paquet.
Maintenant un client ayant le pseudonyme Bob envoie le message Hello pour Alice, en envoyant le paquet suivant au serveur :
00 00 00 03 42 6F 62 00 00 00 05 41 6C 69 63 65 00 00 00 05 48 65 6C 6C 6Fqui se décompose comme suit :
00 00 00 03 // 3 : la taille de la chaîne Bob encodée en UTF-8 42 6F 62 // la chaîne Bob encodée en UTF-8 00 00 00 05 // 5 : la taille de la chaîne Alice encodée en UTF-8 41 6C 69 63 65 // la chaîne Alice encodée en UTF-8 00 00 00 05 // 5 : la taille de la chaîne Hello encodée en UTF-8 48 65 6C 6C 6F // la chaîne Hello encodée en UTF-8Le serveur va associer l'adresse de Bob et le pseudonyme Bob. Il va ensuite renvoyer à l'adresse d'Alice (qu'il connaît), le message suivant :
00 00 00 03 42 6F 62 00 00 00 05 48 65 6C 6C 6Fqui se décompose comme suit :
00 00 00 03 // 3 : la taille de la chaîne Bob encodée en UTF-8 42 6F 62 // la chaîne Bob encodée en UTF-8 00 00 00 05 // 5 : la taille de la chaîne Hello encodée en UTF-8 48 65 6C 6C 6F // la chaîne Hello encodée en UTF-8
Dans une classe ServerChat
,
écrire un serveur UDP bloquant pour le protocole Chat.
Vous pouvez utiliser la trame ci-après comme point de départ :
ServerChat.java.
Si vous lancez votre serveur comme ci-dessous :
% java fr.uge.net.udp.exam2223.ex2.ServerChat 7777
Vous pouvez le tester avec le client ClientChatUDP.jar. Ce client prend en paramètre l'adresse du serveur et le pseudonyme du client.
Comme dans l'exemple ci-dessus, commencez par envoyer un message pour Bob venant d'Alice puis dans un autre terminal, envoyez un message de Bob à Alice. Vous devriez observer un comportement du type suivant.
% java -jar ClientChatUDP.jar localhost 7777 Alice Please enter the message you want to send in the format loginReceiver:message Bob:Hello€ févr. 07, 2023 9:34:50 AM fr.upem.net.udp.exam2223.ex2.ClientChatUDP listener INFO: Received 16 bytes from /127.0.0.1:7777 févr. 07, 2023 9:34:50 AM fr.upem.net.udp.exam2223.ex2.ClientChatUDP getUTF8String INFO: Size of the STRING : 3 févr. 07, 2023 9:34:50 AM fr.upem.net.udp.exam2223.ex2.ClientChatUDP getUTF8String INFO: Extracted string : Bob févr. 07, 2023 9:34:50 AM fr.upem.net.udp.exam2223.ex2.ClientChatUDP getUTF8String INFO: Size of the STRING : 5 févr. 07, 2023 9:34:50 AM fr.upem.net.udp.exam2223.ex2.ClientChatUDP getUTF8String INFO: Extracted string : Hello Receive message "Hello" from Bob
% java -jar ClientChatUDP.jar localhost 7777 Bob Please enter the message you want to send in the format loginReceiver:message Alice:Hello
Dans cet exercice, vous devez écrire un serveur UDP en mode non-bloquant qui implémente le protocole très simple décrit ci-après.
long
en BigEndian.
Quand le serveur reçoit un de ces paquets, il renvoie plusieurs paquets contenant chacun un des longs en BigEndian contenus dans le paquet qu'il a reçu.
Si le serveur reçoit le paquet :
+-------------------------------------------------+ | 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 03 | +-------------------------------------------------+qui contient les longs 5 et 3...
... le serveur enverra au client les deux paquets suivants :
+-------------------------+ | 00 00 00 00 00 00 00 05 | +-------------------------+ +-------------------------+ | 00 00 00 00 00 00 00 03 | +-------------------------+qui correspondent aux deux longs envoyés.
À partir du squelette de la classe
ServerSlice.java
,
écrivez un serveur en mode non-bloquant pour ce protocole.
Pour tester votre serveur, vous pouvez utiliser le client ClientSlice.jar. Ce client prend en arguments l'adresse et le port du serveur. Il demande d'entrer au clavier les longs à envoyer au serveur séparés par des points virgules. Il affiche les réponses reçues par le serveur.
Par exemple, si votre serveur est lancé sur le port 7777 :
$java fr.uge.net.udp.exam2223.ex3.ServerSlice 7777
Vous pouvez le tester depuis un autre terminal (en gras, ce que l'utilisateur saisit sur l'entrée standard) :
$java -jar ClientSlice.jar localhost 7777 Please enter your packet in the format long1;long2;...;longN: 5;3 Sending the packet for [5, 3] Please enter your packet in the format long1;long2;...;longN: févr. 06, 2023 2:14:14 PM fr.upem.net.udp.exam2223.ex3.ClientSlice listener INFO: Received 8 bytes from /127.0.0.1:7777 Received packet [5] févr. 06, 2023 2:14:14 PM fr.upem.net.udp.exam2223.ex3.ClientSlice listener INFO: Received 8 bytes from /127.0.0.1:7777 Received packet [3]