TP noté - IR2
Applications Réseau - Barème et correction

Lundi 30 Novembre 2009

Durée: 2 heures

Remarques importantes :

Prenez le temps de lire correctement les énoncés, y compris ces remarques pour ne pas répondre à côté de la question...

La partie sur UDP est complètement indépendante de la partie TCP. Vous pouvez traiter ces parties dans l'ordre de votre choix. Elles ont le même poids (10 points) dans le barême.

Vous avez le droit de consulter le polycopié du cours. Vous n'avez pas le droit de consulter de notes personnelles sur un autre support, ni d'accéder à vos propres sources sur votre compte.

Vous avez le droit d'utiliser eclipse ou un autre editeur ainsi que des fenêtres de terminal pour l'édition, la compilation et l'exécution de vos programmes.

Vous ne devez ouvrir aucune autre application (navigateur de fichier, navigateur internet, courrier électronique, messenger, etc.).

Vous devez impérativement réaliser ce TP noté en placant vos sources Java et les classes associées dans le répertoire /home/shares/i2000/prof/Java_Reseau/loginlogin est le login de votre compte. Vous ne devez pas changer les droits de ce répertoire ni ceux des fichiers ou des sous-répertoires qu'il contient.

Vous devez travailler sous eclipse avec un workspace spécifique placé dans ce répertoire (différent de votre workspace habituel). Pour cela, le plus simple est de vous placer dans ce répertoire et de lancer dans une fenêtre de terminal eclipse -data .. Ensuite, créez dans ce nouveau workspace un projet Java nommé tpnote. Vous ne devez avoir aucun autre projet dans ce workspace. Si vous n'utilisez pas eclipse, vous devez tout de même travailler dans ce répertoire (sources et classes).

Lorsque le temps imparti pour cette épreuve sera écoulé, les droits de ce répertoire seront fermés et vous n'y aurez plus accès en écriture. Si les sources, les classes ou les documents requis dans ce répertoire ne s'y trouvent pas au moment de la fermeture des répertoires, ils ne pourrront pas être acceptés sous une autre forme et ne seront donc pas pris en compte.

Vous devez respecter les noms des classes et des packages spécifiés dans le sujet.

Partie sur UDP

Question 1. Un serveur UDP qui affiche les chaînes de caractères qu'il reçoit

Barème: 3 points
Correction: DisplayUDPServer.java
Erreurs principales:
- champs ou méthodes déclarés static
- encodage non pris en compte
- non respect des noms de classse/package
- non respect de l'argument sur la ligne de commande 
- la chaîne créée et affichée ne tient pas compte du nombre d'octets effectivement reçus
- la taille de zone de réception n'est pas remise à jour entre 2 réceptions de paquet
- un new DatagramPacket créé dans une variable locale à chaque tour de boucle d'acceptation de paquet 
- gestion des exceptions délirante
- usage inadapté de canaux ou de pseudo-connexion  

Dans cette question, on veut écrire une classe fr.umlv.guess.DisplayUDPServer représentant un serveur UDP qui attend de reçevoir de ses clients des datagrammes contenant des chaînes de caractères encodées en "iso-8859-15". Ce serveur se contente d'afficher sur la sortie standard la chaîne reçue préfixée par l'adresse de la socket du client. On considèrera que le serveur doit accepter des chaînes de caractères dont la taille une fois encodée peut aller jusqu'à 1024 octets; au delà, le serveur tronque les données reçues au delà de cette taille.

Le serveur doit avoir au minimum un constructeur, acceptant le numéro de port d'écoute du serveur en argument, une méthode launch() qui démarre le serveur, et une méthode main() qui permet de démarrer un serveur comme dans l'exemple ci-dessous:

$ java fr.umlv.guess.DisplayUDPServer 7777

Pour tester ce serveur, vous pouvez dans un premier temps utiliser la commande netcat, depuis un autre terminal, par exemple avec:

$ nc -u localhost 7777
abcdef
éÿ€

Pour la première ligne abcdef, le serveur devrait afficher quelque chose du genre:
/127.0.0.1:43391 send me: abcdefg
tandis que pour la seconde, il devrait afficher des caractères plus bizarre...

Question 2. Un client UDP qui envoit au serveur les lignes lues au clavier dans le bon encodage

Barème: 2 points
Correction: KbdToServerUDPClient.java
Erreurs principales: (en plus des précédentes)
- lecture de la chaîne entrée mot par mot (et non ligne par ligne)
- mauvaise gestion des buffers des paquets et de leur taille entre 
  l'émission et la réception
- arrêt avec Ctrl-D non prévu
- fermeture du datagram socket

Afin d'éviter ces désagréments et pour améliorer l'ergonomie du client par rapport à netcat, on souhaite maintenant écrire une classe fr.umlv.guess.KbdToServerUDPClient qui lit sur l'entrée standard les lignes de texte saisies par l'utilisateur et les envoie, encodées en "iso-8859-15", dans un datagramme UDP à destination du serveur spécifié en argument sur la ligne de commande. Par exemple, avec le serveur DisplayUDPServer précédemment démarré, on lancerait un client par:

$ java fr.umlv.guess.KbdToServerUDPClient localhost 7777

Ce client s'arrête lorsqu'on ferme le flot d'entrée standard, par exemple par Ctrl-D.

Question 3. Le serveur tente de faire deviner un secret...

Barème: 3 points
Correction: ClueUDPServer.java
Erreurs principales: (en plus des précédentes)
- mauvaise initialisation/gestion/misee à jour du secret
- secret en Integer plutôt qu'int
- distance pas calculée correctement entre le secret et la ligne reçue du client
- mauvaise représentation de la distance pour réponse au client
- confusion sur le nombre d'octets d'un int (4) avec la taille (32) 

Recopiez la classe du serveur précédent dans une nouvelle classe fr.umlv.guess.ClueUDPServer, que vous allez modifier pour qu'elle réponde au besoin suivant.

En plus de ce que faisait le serveur DisplayUDPServer, le serveur ClueUDPServer renvoit au client un datagramme contenant un entier (transmis en "network order", c'est à dire big-endian ou encore l'octet de poids fort stocké à l'indice le plus faible du tableau) représentant la distance entre la chaîne reçue et une chaîne "secrète" qu'il a tirée aléatoirement dès son démarrage. Cette distance est celle retournée par la méthode compareTo() de la classe String.

Pour chaque client, ce serveur donneur d'indice répond donc à chaque chaîne reçue jusqu'à ce que la "bonne" chaîne s soit proposée par le client (celle telle que s.compareTo(secret) soit nulle). A ce moment, le serveur doit choisir une nouvelle chaîne secret. Pour simplifier, on vous propose d'utiliser la fonction suivante:

  /**
   * Returns a randomly generated String of size between 6 and 10 characters
   * and that only contains characters among the following:
   * !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
   *    
   * @return the random String
   */
  private static String generateSimpleSecret() {
    Random rand = new Random(System.currentTimeMillis());
    // size of secret will be between 6 and 10 characters 
    char[] secret = new char[6 + rand.nextInt(5)];
    for(int i=0; i<secret.length; i++) {
      secret[i] = (char) ('!' +  rand.nextInt(94));
      if (secret[i] == ' ')
        System.out.println("J'ai tiré l'espace" + ((int)secret[i]));
    }
    return new String(secret,0,secret.length);
  }

Vous pouvez tester votre serveur en lui faisant afficher sur la sortie standard "ce qu'il fait" ou en répondant à la question 4. suivante.

Question 4. Un client UDP qui envoit au serveur les lignes lues au clavier dans le bon encodage et affiche les entiers reçus

Barème: 2 points
Correction: GuessUDPClient.java
Erreurs principales: (en plus des précédentes)
- problèmes de décodage des octets reçus en entier
- absence de vérification du bon nombre d'octets reçu pour faire un int
- absence/mauvais affichage de la distance
- non spécification d'un timeout pour limiter l'attente en cas de paquet perdu  

Recopiez la classe fr.umlv.guess.KbdToServerUDPClient dans une nouvelle classe fr.umlv.guess.GuessUDPClient, que vous allez modifier pour qu'elle réponde au besoin suivant.

En plus d'envoyer chaque ligne lue au clavier vers le serveur, le client GuessUDPClient s'attend à reçevoir un entier en réponse et doit l'afficher sur la sortie standard.

Par ailleurs, il est important que le client ne soit pas bloqué dans le cas de la perte d'un datagramme.

Partie sur TCP

Question 5. Un client TCP qui envoit des lignes en ASCII à un serveur

Barème: 3 points
Correction: LineTCPClient.java
Erreurs principales: 
- lecture d'un mot au lieu d'une ligne sur le clavier
- problèmes d'encodage et/ou d'utilisation du flux depuis le clavier
- problèmes d'encodage et/ou d'utilisation du flux vers/depuis le serveur
  en particulier, gestion du retour chariot (newLine) et de la purge (flush)
- gestion des exceptions
- gestion de la terminaison du client et fermmeture de la socket

Ecrire une classe fr.umlv.lexico.LineTCPClient qui établit une connexion TCP avec le serveur spécifié sur la ligne de commande. Une fois la connexion établie, ce client lit sur l'entrée standard une ligne de texte saisie par l'utilisateur, l'envoie au serveur après l'avoir encodée en ASCII, et lit en guise de réponse la ligne de texte encodée en ASCII renvoyée par le serveur. Le client répète cette opération jusqu'à ce que l'entrée standard soit fermée par l'utilisateur (Ctrl-D).

Votre client se lance en spécifiant le nom du serveur et le numéro de port de connexion sur la ligne de commande. Vous pouvez par exmple le tester avec la machine gaspard.univ-mlv.fr qui rend un service ECHO sur le port 7:

$ java fr.umlv.lexico.LineTCPClient gaspard.univ-mlv.fr 7

Question 6. Un serveur TCP concurrent qui conserve le min et le max des lignes envoyées par chacun de ses client

Barème: 5 points
Correction: MinMaxTCPServer.java
Erreurs principales: (en plus des précédentes)
- acceptation de connexions concurrente non protégées dans plusieurs threads
- pas de gestion efficace des threads et de leur limitation
- création d'un nouvel ExecutorService à chaque nouvelle connexion
- mauvaise gestion (ou sous-optimale) des mises à jour du min et du max
- encodage incorrect, ou sous-optimal, en lecture ou en écriture sur la connexion
- non respect du format requis pour l'envoi de l'intervalle au client

On souhaite maintenant écrire un serveur concurrent (qui peut accepter et traiter jusqu'à 10 clients simultanément), dans une classe fr.umlv.lexico.MinMaxTCPServer.

Pour chaque client qui se connecte et qui envoie des lignes de texte encodées en ASCII, le serveur conserve la plus petite et la plus grande (au sens lexicographique) des lignes de texte et renvoie une ligne au client, encodée en ASCII, représentant l'intervalle dans lequel sont contenues toutes les lignes qu'il a reçu de ce client, dans la syntaxe suivante (un blanc avant et après chacune des 2 chaînes):

[ chaineMin .. chaineMax ]

Plus grande et plus petite signifient ici "après" ou "avant" dans l'ordre lexicographique, qui est facilement obtenu en Java par le signe de l'entier retourné par la méthode compareTo() entre deux String.

Le server MinMaxTCPServer devra avoir au minimum un constructeur, qui accepte le port d'écoute, une méthode launch(), qui démarre le serveur et le met en attente d'acceptation de connexion des clients sur ce port, et un main qui permet de le démarer en spécifiant le port comme dans l'exemple:

$ java fr.umlv.lexico.MinMaxTCPServer 7777

Vous testerez ce serveur en utilisant votre client LineTCPClient. Voici quelques exemples:

$ java fr/umlv/lexico/LineTCPClient localhost 7777
abcd
[ abcd .. abcd ]
efgh
[ abcd .. efgh ]
bbb
[ abcd .. efgh ]
aa
[ aa .. efgh ]

Ce serveur doit traiter chacun de ses clients simultanément, mais de manière indépendante (un min et un max propre à chaque client).

Question 7. Le serveur TCP concurrent conserve le min et le max de chaque client et un min et un max global

Barème: 2 points
Correction: MinMaxPlusGlobalTCPServer.java
Erreurs principales: (en plus des précédentes)
- aucune protection, ou protection inadaptée, 
  des variables accédées en concurrence par plusieurs clients
- utilisation de lock.lock() en dehors d'un 
  try { ... } finally { lock.unlock(); } 
- pas d'affichage sur le serveur, ou affichage inadapté

Recopiez la classe du serveur précédent dans une nouvelle classe fr.umlv.lexico.MinMaxPlusGlobalTCPServer, que vous allez modifier pour qu'elle réponde au besoin suivant.

En plus de conserver la ligne la plus petite et la plus grande indépendament pour chaque client, on veut maintenant en plus que le serveur MinMaxPlusGlobalTCPServer conserve et mette à jour un min et un max global de toutes les lignes qu'il a pu reçevoir de tous les clients.

La réponse envoyée au client est la même que pour le serveur MinMaxTCPServer, mais le serveur MinMaxGlobalTCPServer affichera sur la sortie standard le min et le max global à chaque fois qu'il répond à un client.


© Université Paris-Est Marne-La-Vallée - Novembre 2009