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.


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 Whois

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

Dans cet exercice, vous devez écrire un client TCP en mode bloquant pour le protocole Whois décrit ci-dessous. Ce protocole permet à un client connecté à un serveur Whois de demander un certain nombre d'adresses (IP+port) d'autres clients connectés à ce serveur.

Protocole Whois

La requête du client est simple entier INT en BigEndian qui correspond au nombre maximum d'adresses qu'il veut recevoir. Le serveur répondra avec une trame au format suivant :

  • Un INT en BigEndian donnant le nombre N d'adresses renvoyées dans la réponse.
  • Puis les N adresses, chacune au format suivant :
    • un octet valant 4 pour une adresse IPv4 ou un octet valant 6 pour une adresse IPv6,
    • les 4 octets de l'adresse IPv4 ou les 16 octets de l'adresse IPv6,
    • un INT en BigEndian représentant le numéro de port qui est compris entre 0 à 65535.
Après sa réponse, le serveur ne ferme pas la connexion, de sorte que le client puisse faire une autre requête. C'est le client qui décide de fermer la connexion lorsqu'il n'a plus de requête à faire.

Par exemple, lorsque le client est connecté, il envoie :

00 00 00 03 // INT en BigEndian valant 3 indiquant que le client veut au maximum 3 adresses d'autres clients. 

Supposons que le serveur a deux autres clients d'adresses respectives 10.0.0.1 sur le port 3306 et 2001:db8:85a3:8a2e:370:7334:370:7334 sur le port 443. Il peut, par exemple, renvoyer :

00 00 00 02 04 0A 00 00 01 00 00 0C EA 06 20 01 0D B8 85 A3 8A 2E 03 70 73 34 03 70 73 34 00 00 01 BB
qui se décompose comme suit :
00 00 00 02  \\ INT en BigEndian valant 2 et indiquant le nombre d'adresses envoyées 

04           \\ BYTE valant 4 indiquant une adresse IPv4
0A 00 00 01  \\ octets de l'adresse 10.0.0.1
00 00 0C EA  \\ INT en BigEndian valant 3306 donnant le numéro de port

06           \\ BYTE valant 6 indiquant une adresse IPv6
20 01 0D B8 85 A3 8A 2E 03 70 73 34 03 70 73 34 \\ octets de l'adresse 2001:db8:85a3:8a2e:370:7334:370:7334
00 00 01 BB  \\ INT en BigEndian valant 443 donnant le numéro de port

Dans le squelette ClientWhois.java, implémentez la méthode List<InetSocketAddress> performRequest(int max) qui va :

La méthode ne ferme pas la socketChannel. Si le serveur ne respecte pas le protocole ou envoie plusieurs réponses, la méthode renverra null.

Vous pouvez construire un adresse IP à partir d'un byte[] avec la méthode factory InetAddress.getByAddress(byte[] bytes). Si le tableau est de taille 4, l'adresse créée sera une adresse IPv4 et s'il fait 16 octets, ce sera une adresse IPv6.
Vous pouvez remplir un byte[] tab à partir des octets se trouvant au début de la zone de travail du ByteBuffer buffer, en utilisant buffer.get(tab).

Vous pouvez tester votre client avec le serveur ServerWhoisDummy.jar. Ce serveur n'est pas un vrai serveur, il renvoie simplement une réponse au hasard. Pour vérifier la réponse, vous pouvez regarder l'affichage du serveur.

$java --enable-preview -jar ServerWhoisDummy.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.upem.net.tcp.exam2023.ex1.ClientWhois localhost 7777
How many addresses maximum do you want to get ?
1
[/10.0.0.1:3306]
How many addresses maximum do you want to get ?
2
[/[fe80:0:0:0:1:2:3:4]:8080]
How many addresses maximum do you want to get ?
4
[/172.16.0.1:443, /192.168.1.1:8080, /[2001:db8:85a3:0:0:8a2e:370:7334]:3306, /[fe80:0:0:0:1:2:3:4]:8080]
How many addresses maximum do you want to get ?
...

Serveur TCP ChatInt bloquant fixed prestarted

Toutes les classes demandées dans cet exercice doivent se trouver dans le package fr.upem.net.tcp.exam2223.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 ChatInt présenté ci-dessous et vu en TP.

Protocole ChatInt

Les trames circulant entre les clients et le serveur sont des INT en BigEndian.

Quand un message est reçu par le serveur, il le transmet à tous les clients.

À partir du squelette de la classe ServerFixedPrestartedChatInt.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.

On ne vous demande pas de gérer le cas où socketChannel.write est bloquant. Si ce commentaire vous intrigue, ignorez-le.

Pour tester votre serveur, vous pouvez utiliser le client ClientChatInt.jar. Ce client lit au clavier des entiers et les envoie au serveur. Il affiche les entiers reçus du serveur.
Par exemple, vous pouvez lancer votre serveur, qui accepte 3 clients en simultané, sur le port 7777 :

$java fr.upem.net.tcp.exam2223.ex2.ServerFixedPrestartedChatInt 7777 3

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

$java -jar ClientChatInt.jar localhost 7777

Serveur TCP Hash non-bloquant

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

Dans cet exercice, vous devez écrire un serveur TCP en mode non-bloquant, qui implémente le protocole Hash décrit ci-dessous. Dans ce protocole, le client envoie au serveur une chaîne de caractères et le serveur renvoie en entier correspondant au hashcode Java de cette chaîne.

Protocole Hash

Le client envoie les octets d'une chaîne de caractères terminée par \n encodé en ASCII. Le serveur renvoie un INT en BigEndian dont la valeur est le résultat de la fonction de hashcode de Java sur cette chaîne (sans le \n).
Le client peut continuer à faire des requêtes tant qu'il ne ferme pas la connexion.

Par exemple si le client veut envoyer la chaîne de caractères toto, il enverra :

74 6F 74 6F 0A 
qui se décompose comme suit :
74 6F 74 6F  \\ les octets de la chaîne toto
0A           \\ l'octet correspondant \n
et le serveur répond avec :
 
00 36 6A 36   \\ 3566134

À partir du squelette de la classe ServerNonBlockingHash.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 ClientHash.jar. Ce client lit au clavier une chaîne de caractères, l'envoie au serveur et affiche la réponse du serveur.
Par exemple, vous pouvez lancer votre serveur sur le port 7777 :

$java fr.upem.net.tcp.exam2223.ex3.ServerNonBlockingHash 7777 
$java -jar ClientHash.jar localhost 7777
Connecting to server
toto
Sent: -->toto<--
Received: 3566134
tata
Sent: -->tata<--
Received: 3552666