Projet d'architecture et de programmation réseau

Images animées en pair à pair

Maîtrise d'informatique - Mars 2003 - À réaliser par binôme (de deux personnes !)

L'objectif de ce projet est de réaliser un programme en Java pour échanger des images animées dans un réseau pair à pair (peer-to-peer) de type Gnutella. Il revêt deux principaux aspects : d'une part, la gestion du réseau de "peer" à "peer", et d'autre part, le transfert et la visualisation d'images animées.


Compléments d'informations (ajoutés à la version papier qui a été distribuée)


Le principe général

Il existe de nombreuses variantes et implantations de réseaux peer-to-peer. Dans ce projet, nous nous intéressons à un réseau de type Gnutella, qui est un protocole pour des recherches distribuées. Dans ce type de réseau, chaque machine peut jouer simultanément le rôle de serveur et de client : aucune ne possède particulièrement, de manière prédéfinie, un rôle plutôt que l'autre. Aussi, on appelle ces machines des servents (serveurs-clients). Chaque machine dispose d'un ensemble de ressources qu'elle accepte de partager avec les autres machines du réseau peer-to-peer (dans notre cas, il s'agira d'images animées). Chaque servent dispose d'une interface utilisateur permettant d'effectuer une requête à propos d'une ressource, de rendre compte des réponses à cette requête dans le réseau et, le cas échéant, de télécharger cette ressource. Dans le cas particulier qui nous intéresse, il s'agira de visualiser, en temps réel, les images animées téléchargées.

Toute la partie concernant la gestion du réseau peer-to-peer Gnutella devra être compatible avec la spécification du protocole Gnutella, v0.4. Voici une brève description du mode de fonctionnement d'un tel réseau:

La principale caractéristique de ces réseaux est qu'ils doivent être tolérants aux fautes. En particulier, il doit être possible qu'une machine se déconnecte à tout moment, sans remettre en cause le fonctionnement global du réseau.

Pour toute la partie concernant la gestion des servents du réseau Gnutella, vous vous attacherez à respecter la spécification du protocole Gnutella, v0.4. Néanmoins, la partie de cette spécification qui décrit les requêtes Push (pour les machines situées derrière un firewall) ne sera pas implantée.

Concernant le téléchargement et l'affichage en temps réel, l'objectif est de pouvoir visualiser une ressource sans la stocker sur le disque. Il faut donc disposer d'un mécanisme assurant, au dessus d'UDP, que le client et le serveur de cette ressource collaborent à la fluidité de l'affichage.


Le transfert d'images

Cette partie décrit le processus de transfert d'images, qui intervient lorsqu'un servent (client) a reçu un QueryHit identifiant le servent (serveur) qui met à disposition la ressource (identifiée par le serveur par un index FileIndex) sur une adresse IP et un port UDP (au lieu de TCP dans le protocole original) connu du client.

Nous nous limitons, dans la description ci-dessous, au cas où l'image animée est constituée d'une séquence d'images. De nombreux autres cas peuvent être considérés (formats de films) et peuvent donner lieu à des extensions des formats décrits ci-après.

Pour réaliser le transfert, le client et le serveur vont échanger une suite de datagrammes UDP permettant de télécharger la séquence des images. La taille maximale de ces datagrammes est fixée à 1024 octets et leur format est le suivant (les positions et décalage des champs sont exprimés en octets):

 0         3 4    5 6
+-----------+------+------------------- - - - - - - -----+
|File Index |OpCode|    Données dépendant de OpCode      |
+-----------+------+------------------- - - - - - - -----+

Regardons maintenant les formats des trames correspondant aux différentes valeurs prédéfinies de OpCode.

Attention: comme pour la spécification de Gnutella, tous les champs dans les trames ci-dessous doivent être écrits et lus en respectant l'ordre petit-boutiste des octets (little-endian), à moins qu'il soit précisé autre chose.

Le client, au fur et à mesure de la réception des paquets, doit reconstruire les images. À chaque fois qu'il devient nécessaire d'afficher une image, le client doit se débrouiller avec les paquets qu'il a reçu, même s'il lui en manque, afin de ne pas interrompre la fluidité de l'animation. En fonction des formats d'images utilisés, plusieurs méthodes de "remplissage des trous" peuvent être imaginées (mettre du noir, utiliser l'image précédente, utiliser des formats d'image progressifs, etc.)

Quand le débit devient trop faible comparé à la taille des images, le client devra demander au serveur de baisser la qualité des images envoyées afin que les données à transférer pour chaque image soient plus petites : le mécanisme minimal à mettre en oeuvre est celui décrit plus haut dans la description de la trame NEGO d'OpCode 0x0001, mais d'autres OpCode avec d'autres mécanismes pourront être implantés.


Ce que vous devez faire

Vous devez écrire en Java un programme qui prend en charge le fonctionnement d'un servent Gnutella. Installé et configuré sur une machine, ce programme pourra fonctionner de manière autonome pour ce qui concerne la gestion des connexions du réseau Gnutella, pour les réponses aux requêtes Query des autres servents, ainsi que pour la mise à disposition des ressources par le serveur UDP. Pour la partie recherche, téléchargement et affichage d'images animées, le programme offrira une interface avec l'utilisateur. Par ailleurs, cette interface pourra avantageusement permettre d'observer les caractéristiques de ce servent (nombre et identité de ses "voisins", ressources offertes sur le réseau, activité sur le réseau en tant que "routeur" et en tant que "serveur", etc.).

Il faut implanter au moins le mode de découpage linéaire des images qui correspondra à l'OpCode 0x0001. Lorsque le serveur utilise ce mode de découpage, le fichier image est découpé linéairement en fragments (en tête inclus), et envoyé au client. Il suffit au client de remettre les fragments les uns après les autres pour obtenir un fichier image que l'on peut afficher avec les routines de javax.imageio et notamment la classe javax.imageio.ImageIO. Ce mode de découpage étant peu efficace face aux pertes de paquets (notamment le premier qui contient l'en-tête), on pourra en implanter d'autres.

Vous devrez prendre garde à développer avec soin la partie concurrente de l'application (nombre de threads, nombre de connexions optimum, maximum, nombre de clients simultanés en téléchargement, etc.).

Vous rendrez votre travail sous la forme d'une archive contenant les sources commentés, la Javadoc de ces sources, un jar exécutable de l'application et un fichier README permettant de l'installer et de le configurer.


Référence


Nicolas Bedon, Julien Cervelle, Etienne Duris, Rémi Forax © Université de Marne-La-Vallée - Mars 2003