:: Enseignements :: Master :: M1 :: 2016-2017 :: Programmation d'applications réseaux ::
[LOGO]

Fiabilisation d'une succession d'échanges UDP


Exercice 1 - Paquets identifiés envoyés et acquittés un par un

On souhaite disposer d'un programme qui utilise les services d'un serveur de mise en majuscule pour réaliser les opérations suivantes:
  • lire un fichier texte ligne par ligne
  • stocker toutes ces lignes dans une liste,
  • envoyer ces lignes au serveur et stocker toutes les réponses dans une autre liste
  • écrire ces lignes mises en majuscule dans un fichier.
Cependant, on a constaté pour cela qu'il faut que le client ait un moyen d'identifier, lorsqu'il reçoit une réponse, à quelle requête elle correspond.

Pour cela, nous améliorons notre protocole "BetterUpperCase" en "IdBetterUpperCase": nous allons préfixer les données envoyées au serveur par une nouvelle information: un entier de type long, stocké en Big Endian, qui identifie de manière unique la requête et sa réponse: si une requête doit être ré-envoyée parce qu'elle n'a pas reçu de réponse, le même identifiant est utilisé. Chaque requête (émanant d'une nouvelle chaîne à mettre en majuscule) doit avoir un identifiant différent.

Ainsi, pour chaque chaîne de caractère à envoyer au serveur pour mise en majuscule, la représentation utilisée sera la suivante:
  • Un long en BigEndian identifiant la requête (chaque requête a un identifiant unique, qui est également utilisé dans la représentation de la réponse à cette requête)
  • Un int en BigEndian correspondant à la longueur du nom de l'encodage écrit en ASCII;
  • Le nom de l'encodage en ASCII;
  • Les octets de la chaîne encodée avec cet encodage.

Nous vous fournissons un nouveau serveur ServerIdBetterUpperCase.jar qui implémente ce protocole en utilisant l'identifiant, et vous pouvez réutiliser le proxy UDP UDPProxy.jar qui introduit des délais ou des pertes dans les échanges de paquets UDP.
$ java -jar ServerIdBetterUpperCaseUDP.jar 4545
$ java -jar UDPProxy.jar 7777 localhost 4545 -no-swap 

Les classes suivantes doivent vous permettre de construire une application cliente qui permettra d'interroger ce serveur en passant par le proxy afin de vérifier que l'ensemble des chaînes récupérées correspond bien à l'ensemble des chaînes envoyées.
  • La classe ClientIdBetterUpperCaseUDP.java qui correspond au client qui lit les lignes du fichier pour les envoyer au serveur.
  • Pour envoyer au serveur, ce client utilise un objet de la classe Requester: cette classe abstraite permet au client d'interroger le serveur en utilisant le protocole, et propose des méthodes génériques pour construire un paquet, décoder unee chaîne dans un paquet, envoyer au serveur, etc... La méthode d'envoi de la liste de chaînes au serveur est abstraite, et son implémentation est laissée à la classe suivante.
  • La classe OneByOneRequester hérite de Requester et implémente cette méthode en réalisatn l'envoi une par une des chaînes.
Vous devez compléter ce qui manque de ces classes et tester par:
$ java fr.upem.net.udp.ClientIdBetterUpperCaseUDP utf-8 fichier.txt localhost 7777

Après avoir vérifié que le comportement attendu est obtenu, et que votre protocole est fiable, essayer d'évaluer le temps de réponse total pour un fichier de N lignes, avec une probabilité p de perte de paquet (via le proxy). Mesurez expérimentalement le temps pris par votre application et comparez.

Exercice 2 - Paquets identifiés envoyés en rafale et acquités de manière asynchrone

Le temps d'attente systématique (synchrone) de la réponse après l'envoi d'un paquet n'est pas optimal. Il serait intéressant de pouvoir envoyer tous les paquets, sans se soucier de leur réponse ou de leur perte, et de recevoir les réponses de manière asynchrone, l'idée étant que l'on aura à ré-envoyer toutes les requêtes pour lesquelles aucune réponse n'a été retenue.

Cette solution semble bien plus performante, mais elle nécessite de mettre en place un mécanisme permettant de savoir, pour l'ensemble des paquets envoyés, lesquels ont reçu (ou pas) une réponse, et d'assurer que les réponses seront bien remises dans l'ordre où elles doivent être: dans notre exemple de client ci-dessus qui met en majuscule toutes les lignes d'un fichier, il faut bien entendu respecter l'ordre des lignes du fichier...

Pour cela, vous allez écrire une classe BurstRequester héritant, comme OneByOneRequester, de la classe abstraite Requester. L'implémentation de toUpperCase() dans BurstRequester pourra utilser un java.util.BitSet du nombre de lignes -- paquets -- à envoyer pour représenter lesquelles de ces lignes ont reçu une réponse (au départ aucune). La méthode toUpperCase() devra cette fois réaliser les opérations suivantes:
  • créer et intitialiser ce BitSet
  • créer et démarrer une thread d'envoi des paquets qui envoit un paquet pour chaque ligne de la liste qui n'a pas encore reçue de réponse (il est plus facile de faire une thread pour l'envoi, contrairement à l'exercice précédent où il était plus facile de faire une thread d'écoute). Le processus est répété toutes les TIMEOUT millisecondes.
  • écouter les réponses reçues du serveur, les stocker et mettre à jour le BitSet
Bien entendu, vous tiendrez compte du fait que le BitSet est accédé par la thread qui exécute toUpperCase() en concurrence avec la thread d'envoi des paquets.

Pour tester votre classe, il suffira de modifier une ligne dans le main du client ClientIdBetterUpperCaseUDP:
		Requester requester = new BurstRequester(dest);