Dans ce sujet, nous vous demandons de réaliser un mini client
FTP permettant de télécharger un fichier d'un dépôt d'un serveur
FTP. La version finale pourra lancer plusieurs téléchargements en
parallèle. Les échanges réseau et les écritures sur fichier
doivent être écrits en utilisant la bibliothèque Java
NIO.
Importez l'archive Eclipse. Renommez le projet
CSC4509-TP-Note avec le nom tpnote-VotreNom. Créez aussi dans cette
arborescence du projet un fichier readme.txt dans
lequel vous mettrez vos notes.
Le barème est donné à titre d'indication.
En fin de TP :
si ce n'est pas déjà fait, créez un fichier readme.txt dans lequel vous mettez vos
notes à destination des correcteurs ;
exportez votre projet Eclipse sous la forme d'une archive :
cliquez avec le bouton droit sur votre projet ;
puis, choisissez export ;
ensuite, dans general,
choisissez archive ;
donnez le nom de l'archive. Eclipse complète le
nom du fichier par le suffixe .zip ;
cliquez sur finir ;
déposez le fichier sur moodle comme devoir ;
vérifiez avec l'enseignant présent dans la salle que le
fichier téléchargé est correct.
1. Présentation du sujet
Objectif : écrire des classes qui déroulent la partie du
protocole FTP qui permet de télécharger un fichier.
Un client et un serveur FTP contiennent chacun deux composants
distincts :
un premier composant appelé Protcol Interpreter (PI)
échange avec le PI distant les demandes et les réponses du
protocole. Ces échanges sont tous faits à l'aide d'une unique
ligne de texte (une chaîne de caractères ASCII se terminant
par le caractère « \n »). Une demande = une
seule ligne. Tous ces échanges se font à travers la connexion
qu'a établie le client avec le serveur lors de son
lancement ;
le second composant appelé Data Transfer Process (DTP)
échange les informations qu'il n'est pas possible de faire
passer sur une seule ligne. Par exemple, cela peut être le
résultat la commande « dir », ou bien le
contenu du fichier à échanger. Un nouveau DTP est créé de
chaque côté pour chaque nouvel échange. Une connexion est
établie entre ces deux DTP et les données sont transmises à
travers cette connexion.
Voici deux exemples de scénarios utilisés dans notre
application :
un client (déjà connecté au serveur) change le répertoire
courant du serveur. Voici les différentes étapes du
protocole :
le client envoie au serveur sur la connexion ouverte au
début (celle qui relie les PI du client et du serveur) la
ligne « CWD repertoireOuAller\n »
(notez qu'il n'y a aucune espace avant le caractère
« \n ») ;
le serveur envoie au client sur la connexion ouverte au
début (celle qui relie les PI du client et du serveur) la
réponse sous la forme d'une ligne commençant par le code
de réponse, comme par exemple « 550 Failed to
change directory.\n », ou bien « 250
Directory successfully changed.\n » ;
un client (déjà connecté au serveur) télécharge un fichier du
répertoire courant du serveur. Voici les différentes étapes du
protocole :
le client envoie au serveur sur la connexion ouverte au
début (celle qui relie les PI du client et du serveur) une
demande pour que le DTP du serveur prépare une nouvelle
adresse de connexion. Cette demande se fait avec la
commande PASV. Il envoie la ligne
« PASV\n » au PI du serveur ;
le serveur crée pour son composant DTP un
nouveau socket serveur sur un nouveau port et
envoie au PI du client les informations sur
ce socket avec le message réponse semblable
à : « 227 Entering Passive Mode
(157,159,11,37,198,65)\n ». Les quatre premiers
nombres décimaux sont les quatre octets de l'adresse IP,
et les deux derniers sont les deux octets du port choisi
par le DTP du serveur ;
le client crée un DTP qui utilise ces informations pour se
connecter à cette adresse ;
une fois cette connexion réussie, le PI du client envoie
une requête au serveur pour qu'il commence le
transfert : « RETR
nomDuFichier\n » ;
le PI du serveur envoie la confirmation du début du
téléchargement « 150 Opening BINARY mode data
connection for nomDuFichier (61584 bytes).\n »,
ou son échec « 550 Failed to open
file.\n » ;
en cas de succès, le DTP serveur envoie tout le fichier
sur la connexion établie entre les DTP ;
une fois le fichier intégralement envoyé, le PI du serveur
envoie la réponse suivante : « 226 Transfer
complete.\n ».
Tous les messages envoyés par le PI du serveur commencent par un
code de 3 chiffres. Le premier de ces chiffres indique le succès
ou l'échec de la demande faite par le client. Si le code
commence par 1, 2 ou 3, la demande a réussi (éventuellement
partiellement). Si le code commence par 4 ou 5, la demande a
échoué. Vous devrez tenir compte de ce code de retour à chaque
étape.
2. Connexion du PI du client FTP et premiers échanges
(6 points)
Dans cette partie, vous commencez à compléter la
classe FtpProtocolInterpreter. En guise de serveur de
test, vous utilisez l'adresse ftp.int-evry.fr
(port 21), avec comme identifiant
« anonymous » et comme mot de passe, votre
login DISI (le login et surtout pas le
mot de passe).
Le PI du client FTP peut être dans les trois états différents
listés dans l'énumération FtpPIStatus :
NotConnected : état de départ et état de retour
en cas d'erreurs graves (identification échouée et connexion
perdue),
Connected : le PI du client FTP est connecté au
PI du serveur, mais l'identification n'est pas encore réalisée,
LoginOk : le PI du client FTP est connecté au PI
du serveur et l'identification est réussie.
Il faut veiller à maintenir l'état du PI du client FTP
(attribut status) de façon à ce qu'il soit cohérent
avec son historique.
2.1 Connexion au serveur (2 points)
Écrivez la méthode connectTo(String servHostname, int
port) qui permet à un client TCP de se connecter au serveur
tournant sur le machine servHostname et le
port port.
Le protocole de connexion se fait en 2 étapes:
Établissement de la connexion par le client
Réponse du PI du serveur avec un message indiquant la réussite ou l'échec de cette connexion
Notez que pour tout ce TP, vous pouvez considérer que les échanges entre les 2 PI ne sont jamais morcelés.
Donc une seule lecture suffit à lire une réponse du PI du serveur. Vous ne pourrez pas faire
cette supposition pour les échanges entre les DTP.
Testez votre méthode en construisant
un FtpProtocolInterpreter dans la classe
d'application MiniFtp et le connectant au serveur de
test.
2.2 Identification (3 points)
Écrivez la méthode logToServer(String user, String
pass) qui, une fois la connexion établie, déroule le
protocole d'identification. Il se déroule en 4 étapes :
envoi par le PI du client au PI du serveur du message
« USER identifiant\n » ;
envoi par le PI du serveur au PI du client de la réponse avec
le code de réussite ou d'échec ;
en cas de réussite, envoi par le PI du client au PI du serveur
du message « PASS password\n » ;
envoi par le PI du serveur au PI du client de la réponse avec
le code de réussite ou d'échec.
Testez votre méthode dans la classe
d'application MiniFtp en vous identifiant sur le
serveur de test.
2.3 Commande CWD (1 point)
Écrivez la méthode cwdCmd(String directory) qui, une
fois l'identification réussie, permet de demander au serveur de
changer son répertoire courant. Le protocole se déroule en
2 étapes :
envoi par le PI du client au PI du serveur du message
« CWD repertoire\n ».
envoi par le PI du serveur au PI du client de la réponse avec
le code de réussite ou d'échec.
Testez votre méthode dans la classe
d'application MiniFtp en demandant au serveur d'aller
dans le répertoire /pub/rfc
3. Écriture du DTP du client (4 points)
Dans cette partie, vous écrivez la
classe FtpDataTransferProcess. Le DTP du client se
connecte à l'adresse TCP/IP donnée à sa construction, et reçoit
les données du DTP du serveur lorsque nous utilisons la
méthode receiveData(). Une fois les données reçues, le
DTP ferme la connexion.
Toutes les lectures et écritures sont faites en utilisant les
canaux de JAVA NIO.
Écrivez le constructeur de la classe.
Écrivez la méthode close().
Écrivez la méthode receiveData().
4. Téléchargement d'un fichier (5 points)
Dans cette partie, vous écrivez la méthode retrCmd(String
fileName). Cette méthode déroule le protocole de
récupération d'un fichier du répertoire courant du serveur (voir
la figure 1 dans la section « 1. Présentation du
sujet ») :
envoi par le PI du client au PI du serveur du message
« PASV\n »" ;
envoi par le PI du serveur au PI du client de la
réponse. Cette réponse contient le code de réussite ou
d'échec, et en cas de réussite, les informations sur l'adresse
TCP/IP qu'a préparée le DTP du serveur pour l'envoi du
fichier ;
connexion du DTP du client au DTP du serveur ;
envoi par le PI du client au PI du serveur du message
« RETR nomDuFichier\n » ;
envoi par le PI du serveur au PI du client de la réponse de
confirmation du début de l'envoi des données sur la connexion
DTP ;
échange du fichier entre les DTP : le serveur écrit, le
client lit ;
envoi par le PI du serveur au PI du client d'un message
signalant la fin du transfert.
Dans le paquetage util, vous trouvez la
classe PasvResponse. Elle vous fournit les méthodes qui
analysent la réponse de la commande PASV et donnent
les adresses et ports TCP/IP correspondants.
Testez votre méthode dans la classe
d'application MiniFtp en demandant au serveur d'envoyer
le fichier rfc959.txt du
répertoire /pub/rfc. En cas de réussite, le fichier
arrive dans le répertoire workspace/tpnote-VotreNom.
5. Multiconnexion, multitéléchargement (5 points)
Dans cette partie, le PI du client FTP est modifié pour que nous
puissions en lancer plusieurs en même temps dans
des threads différents.
Modifiez la classe FtpProtocolInterpreter pour qu'elle
puisse être lancée dans un nouveau thread.
La méthode run() doit:
réaliser la connexion;
réaliser l'identification;
faire le changement de répertoire;
déclencher téléchargement du fichier.
Il faut donc aussi écrire un nouveau
constructeur à cette classe pour initialiser toutes les
informations nécessaires à cette méthode.
Testez la nouvelle version de la classe en créant dans la classe
d'application quatre threads afin de (tenter de) télécharger les quatre
fichiers /pub/rfc/rfc114.txt, /pub/rfc/rfc454.txt, /pub/rfc/nexistepas
et /pub/rfc/rfc949.txt.