Examen 2h sur machine

Consignes

L'examen est composé de trois exercices indépendants qui peuvent être traités dans l'ordre que vous voulez. Les exercices 2 et 3 utilisent le même protocole. Il faut donc lire la présentation du protocole au début de l'exercice 2 pour pouvoir faire l'exercice 3.


Rappel : si ce n'est pas déjà le cas, vous devez configurer le workspace d'Eclipse (File > Switch WorkSpace) pour qu'il corresponde au répertoire EXAM présent dans le home de votre session de TP noté.

Vérifiez 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).

Vous avez le droit de consulter les transparents du cours.

La Java doc est ici.

Client TCP PokemonObserver

Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.net.tcp.exam2025.ex1

Dans cet exercice, vous devez écrire un client TCP en mode bloquant pour le protocole PokemonObserver décrit ci-dessous. Ce protocole permet à un client de recevoir des notifications sur les Pokémons observés dans la nature avec leur différentes caractéristiques.

Protocole PokemonObserver

Le client peut s'enregistrer pour recevoir des notifications pour les Pokémons observés dans une zone géographique donnée. Il envoie une trame contenant le nom de la zone géographique au format suivant: un INT en BigEndian qui donne le nombre d'octets de la chaîne encodée en UTF-8, puis la chaîne encodée en UTF-8.

Dès qu'un Pokémon est observé dans une zone géographique pour laquelle le client est enregistré, le serveur enverra toutes les informations sur le Pokémon observé au format suivant:

  • le nom du Pokémon observé sous la forme d'un INT en BigEndian qui donne le nombre d'octets du nom encodé en UTF-8, puis le nom encodé en UTF-8.
  • le nom de la zone géographique dans laquelle il a été observé sous la forme d'un INT en BigEndian qui donne le nombre d'octets du nom encodé en UTF-8, puis le nom encodé en UTF-8.
  • un SHORT en BigEndian qui donne le nombre de caractéristiques du Pokémon observé.
  • Pour chaque caractéristique, un INT en BigEndian qui donne le nombre d'octets du nom de la caractéristique encodée en UTF-8, puis le nom de la caractéristique encodée en UTF-8, puis un LONG en BigEndian qui donne la valeur de la caractéristique.

Le taille des chaînes de caractères une fois encodées en UTF-8 ne peut pas dépasser 1020 octets.

Par exemple, si le client veut les informations sur les Pokémons observés dans la zone géographique "Paris", il enverra la trame suivante :

00 00 00 05 50 61 72 69 73
qui se décomposent comme suit :
00 00 00 05 \\ un INT en BigEndian valant 5
50 61 72 69 73 \\ les octets de la chaîne "Paris" en UTF-8

Si le serveur détecte un Pokémon "Ronflex" dans la zone géographique "Paris" avec les caractéristiques suivantes :

Il enverra la trame suivante :

 
    // Pokémon name "Ronflex" (7 bytes)
    00 00 00 07       // INT: length of "Ronflex" in UTF-8 (7 bytes)
    52 6F 6E 66 6C 65 78  // "Ronflex" in UTF-8
    
    // Zone name "Paris" (5 bytes)
    00 00 00 05       // INT: length of "Paris" in UTF-8 (5 bytes)
    50 61 72 69 73    // "Paris" in UTF-8
    
    00 03             // SHORT: number of characteristics (3)
    
    // Characteristic 1: "size"
    00 00 00 04       // INT: length of "size" in UTF-8 (4 bytes)
    73 69 7A 65       // "size" in UTF-8
    00 00 00 00 00 00 01 2C  // LONG: value 300
    
    // Characteristic 2: "weight"
    00 00 00 06       // INT: length of "weight" in UTF-8 (6 bytes)
    77 65 69 67 68 74  // "weight" in UTF-8
    00 00 00 00 00 00 00 64  // LONG: value 100
    
    // Characteristic 3: "swag"
    00 00 00 04       // INT: length of "swag" in UTF-8 (4 bytes)
    73 77 61 67       // "swag" in UTF-8
    00 00 00 00 00 00 23 28  // LONG: value 9000    

On veut écrire un client qui lit au clavier des noms de zones géographiques et qui envoie la trame correspondante au serveur. Et parallèle, le client affiche toutes les informations sur les Pokémons observés reçues du serveur. Le squelette de la classe est donné et il contient déjà les deux threads nécessaires. Il vous faut implémenter :

En partant du squelette ClientPokemonObserver.java, implémentez le client bloquant demandé.

Vous pouvez tester votre client avec le serveur ServerPokemonObserver.jar. Pour vérifier la réponse, vous pouvez regarder l'affichage du serveur.

$java -jar ServerPokemonObserver.jar 7777

Votre client prendra en paramètre l'adresse et le port du serveur comme ci-après (en gras, ce que l'utilisateur saisit sur l'entrée standard).

$java fr.uge.net.tcp.exam2025.ex1.ClientPokemonObserver localhost 7777
What region are you interested in ?
Paris
What region are you interested in ?
==============
Pokemon : Bulbasaur
Location : Paris
energy -> 92
agility -> 20
specialDefense -> 64
accuracy -> 46
luck -> 49
attack -> 37
==============
==============
Pokemon : Kangaskhan
Location : Paris
speed -> 65
accuracy -> 14
==============

Serveur TCP PokeSpot bloquant fixed prestarted

Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.net.tcp.exam2025.ex2

Dans cet exercice, vous devez implémenter un serveur TCP multi-thread en mode bloquant, suivant l'architecture fixed prestarted vue en cours, qui implémente le protocole PokeSpot présenté ci-dessous.

Protocole PokeSpot

Dans le protocole PokeSpot, peuvent envoyer le nom de Pokémon qu'ils ont observés dans la nature et demander pour un Pokémon donné combien de fois il a été observé.

Pour dire qu'il a observé un Pokémon, le client envoie une trame contenant le nom du Pokémon observé au format suivant :

  • un INT en BigEndian valant 1 qui indique qu'il s'agit d'une observation ;
  • le nom du Pokémon observé sur 256 octets. Les 256 octets sont les octets du nom encodé en UTF-8 suivis d'autant d'octets valant 33 que nécessaire pour compléter à 256 octets.

L'octet valant 33 correspond au caractère '!' en UTF-8. On supposera donc que le nom du Pokémon ne contient pas de '!'.

Quand le serveur reçoit une trame d'observation, il n'envoie aucune trame en réponse.

Pour demander des informations sur un Pokémon, le client envoie une trame contenant le nom du Pokémon observé au format suivant :

  • un INT en BigEndian valant 2 qui indique qu'il s'agit d'une demande d'informations.
  • le nom du Pokémon sur 256 octets. Les 256 octets sont les octets du nom encodé en UTF-8 suivis d'autant d'octets valant 33 que nécessaire pour compléter à 256 octets.
Quand le serveur reçoit une trame de demande d'informations, il envoie une trame contenant les informations sur ce Pokémon au format suivant :
  • un INT en BigEndian qui donne le nombre de fois que le Pokémon a été observé par le client qui a envoyé la demande.
  • un INT en BigEndian qui donne le nombre de fois que le Pokémon a été observé par tous les clients connectés au serveur au moment de la réception de la demande.
  • un INT en BigEndian qui donne le nombre de fois que le Pokémon a été observé par tous les clients du serveur depuis le démarrage du serveur.

Par exemple, si un client veut signaler qu'il a vu Ronflex, il enverra la trame suivante :

00 00 00 01 52 6F 6E 66 6C 65 78 33 ... 33 

qui se décompose comme suit :

00 00 00 01 // INT: 1
52 6F 6E 66 6C 65 78 33 ... 33 // "Ronflex" en UTF-8 suivi de 250 octets valant 33

Si un client veut demander des informations sur Ronflex, il enverra la trame suivante :

00 00 00 02 52 6F 6E 66 6C 65 78 33 ... 33 

Supposons qu'aucun client n'est connecté au serveur et qu'un client A se connecte au serveur et envoie un trame disant qu'il a observé Ronflex. Si le client A envoie une demande d'information sur Ronflex, le serveur renverra [1,1,1]. Si le client A envoie une autre demande d'information sur Pikachu, le serveur renverra [0,0,0].

Si un client B se connecte et envoie une demande d'information sur Ronflex, le serveur renverra [0,1,1]. Si il envoie une trame d'observation sur Ronflex et qu'il fait une demande d'information sur Ronflex, le serveur renverra [1,2,2].

Si le client A se déconnecte puis que le client B envoie une trame d'observation sur Ronflex, le serveur renverra [1,1,2].

À partir du squelette de la classe ServerFixedPrestartedPokeStop.java, écrivez un serveur en mode bloquant suivant l'architecture fixed prestarted vue en cours, pour ce protocole. Le serveur prend comme paramètre le nombre maximum de clients qui peuvent se connecter simultanément.

Pour tester votre serveur, vous pouvez utiliser le client ClientPokeSpot.jar. Ce client envoie un trame à chaque fois que vous appuyez sur la touche entrée. Si la ligne commence par un OBSERVE, il envoie une trame d'observation. Si la ligne commence par un ASK, il envoie une trame de demande d'information. Il affiche les valeurs reçues par le serveur.

Par exemple, vous pouvez lancer votre serveur, qui accepte 3 clients en simultané, sur le port 7777 :

$java fr.uge.net.tcp.exam2025.ex2.ServerFixedPrestartedPokeSpot 7777 3

Vous pouvez le tester depuis au plus 3 terminaux simultanément en lançant un client avec :

$java -jar ClientPokeSpot.jar localhost 7777
Type your command OBSERVE Pokémon or INFO Pokémon. QUIT to stop
INFO R
Information for R
Spotted by this client 0 many times
Spotted by a connected client 0 many times
Spotted by since start of server 0 many times
OBSERVE R
INFO R
Information for R
Spotted by this client 1 many times
Spotted by a connected client 1 many times
Spotted by since start of server 1 many times
QUIT
Closing client

Serveur TCP PokeSpot non-bloquant

Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.uge.net.tcp.exam2025.ex3

Dans cet exercice, vous devez écrire un serveur TCP en mode non-bloquant, qui implémente le protocole PokeSpot décrit à l'exercice précédent.

À partir du squelette de la classe ServerNonBlockingPokeSpot.java, écrivez un serveur en mode non-bloquant pour ce protocole. On demande que votre serveur puisse (quand c'est possible) être en OP_READ|OP_WRITE. Attention, le squelette fourni est un serveur pour le protocole Echo, qui suit l'architecture vue en cours. Il est nécessaire de l'adapter et de le faire évoluer : c'est simplement un point de départ.
Tous vos buffers doivent faire 1024 octets.

Pour tester votre serveur, vous pouvez utiliser le client ClientPokeSpot.jar.

Par exemple, vous pouvez lancer votre serveur sur le port 7777 :

$java fr.uge.net.tcp.exam2025.ex3.ServerNonBlockingPokeSpot 7777 
$java -jar ClientPokeSpot.jar localhost 7777