Vous êtes sur la page 1sur 7

ESCPI TP 3 EI2

TP 3 Mini client/serveur ftp


1 But
Le but du TP est de raliser un client et un serveur de transfert de fichiers distance. Pour transfrer un fichier (par
exemple obtenir un fichier depuis une machine distante), un client tablit une connexion avec le serveur situ sur cette
machine, transmet sa requte, attend l'accus de rception, puis le contenu du fichier est chang. La connexion est
ferme la fin du transfert (il ne peut donc y avoir qu'une seule requte par connexion).
2 Un mini-client/serveur ftp
Le protocole de communication implanter est le suivant (il ne s'agit pas du vrai protocole utilis par ftp) :
Serveur
lancement du serveur (ouverture du port d'coute)
attend une demande de connexion
accepte une demande de connexion
dlgue le traitement de la requte un fils et se remet en attente de demande de connexion
le fils :
lit la requte et l'identifie
envoie la rponse
change les donnes (si ncessaire).
Client
demande une connexion un serveur
construit une requte
envoie la requte au serveur
lit la rponse du serveur
vrifie la rponse
change les donnes (si ncessaire).
2.1 Les requtes du client
Nous spcifions seulement quelques types de requtes (4) pour simplifier notre cahier des charges :
obtention d'un fichier (REQUETE_GET), le client demande rcuprer un fichier qui est sur le
serveur,
envoi d'un fichier (REQUETE_PUT), le client demande dposer un fichier sur le serveur,
suppression d'un fichier sur le serveur (REQUETE_DEL), le client demande qu'un fichier du serveur
soit dtruit (la requte seule est envoye, il n'y a pas de transfert de contenu),
affichage du contenu d'un rpertoire du serveur (REQUETE_DIR), le client demande l'excution de
ls -la sur un chemin du serveur
2.2 Les rponses du serveur
Aprs avoir lu la requte, le serveur renvoie un accus de rception au client. Cet accus peut tre :
positif (ANSWER_OK),
ngatif (ANSWER_ERROR)
ou le serveur peut ne pas avoir compris la requte (ANSWER_UNKNOWN).
En outre, le client peut ne jamais rcuprer d'accus, cause d'une coupure de la connexion.
Dans le cas d'un accus ngatif, le serveur fournit aussi un code d'erreur permettant d'identifier le problme (par
exemple, l'chec peut provenir d'une tentative de rcupration d'un fichier inconnu sur le serveur). Pour simplifier, le
serveur renvoie la valeur de la variable errno suite l'action qui a entran le rejet de la requte.

2.3 La requte
Lors des cas de transfert de fichier (en envoi ou en rcupration), il est ncessaire de connatre la taille du fichier
transfr pour savoir si, lors d'un read dtectant une fin de fichier (d la fermeture de la connexion), tout le contenu a
bien t rcupr, ou si la connexion a t rompue trop tt.
Une requte est donc de la forme :

#define REQUETE_PUT 1
#define REQUETE_GET 2
#define REQUETE_DEL 3
#define REQUETE_DIR 4

G. BENAY 1 2007/2008
ESCPI TP 3 EI2

struct request {
int kind;
char path[MAXPATH];
int nbbytes; /* pour PUT seulement */
};

L'entier kind doit tre REQUETE_PUT, REQUETE_GET, REQUETE_DEL ou REQUETE_DIR. La chane path
contient le nom du fichier crire (put), lire (get), dtruire (del) ou lister (dir). L'entier nbbytes contient la taille du
fichier, lors d'un PUT seulement.

2.4 La rponse
Une rponse contient donc l'accus (ack). Si la rponse est positive (ack vaut ANSWER_OK) et que la requte tait
un GET, nbbytes contient la taille du fichier que le serveur va envoyer. Si la rponse est ngative (ANSWER_ERROR),
errnum contient le code de l'erreur (valeur de la variable errno).

#define ANSWER_OK 0
#define ANSWER_UNKNOWN 1 /* requete inconnue */
#define ANSWER_ERROR 2 /* erreur lors du traitement */

struct answer {
int ack;
int nbbytes; /* pour GET seulement */
int errnum; /* significatif ssi != 0 et ack == ANSWER_ERROR */
};

2.5 Ligne de commande


Le serveur miniftpd est dmarr sans argument. Le port est prdfinit et impos ; on choisira un port libre.
L'excution d'un client peut prendre l'une des formes suivantes :

miniftp hostname port get distfilename localfilename


miniftp hostname port put localfilename distfilename
miniftp hostname port del distfilename
miniftp hostname port dir distpathname

2.6 Contrle d'accs ?


Un serveur ftp doit thoriquement vrifier que les fichiers sont accds avec les droits d'accs du client, et non pas
ceux du serveur (d'o une phase initiale d'authentification avec mot de passe). Dans notre cas, le serveur accde aux
fichiers sous l'uid de l'utilisateur qui a lanc ce serveur. Le client peut ventuellement tre d'un autre uid, mais aucun
contrle n'est effectu.
2.7 Terminaison des processus fils
Pour grer une requte, le serveur fork un nouveau processus. Il est intressant de rcuprer le code de retour
de ce processus fils, pour pouvoir dtecter, par exemple, une terminaison anormale (et cela vite en outre l'apparition de
processus zombies). Il existe deux mcanismes de synchronisation avec la terminaison (qui peuvent tre combins) :

l'appel systme wait attend indfiniment la terminaison d'un processus fils ;


le signal SIGCHLD est envoy au pre lors de la terminaison d'un processus fils. Par dfaut, ce signal est
ignor.
Rappel : lorsqu'un processus est bloqu par un appel systme (par exemple read ou accept) et qu'un signal lui
est envoy, l'appel systme choue et renvoie -1, avec le code d'erreur EINTR (dans la variable errno).

G. BENAY 2 2007/2008
ESCPI TP 3 EI2

3 Rappels sockets
Un socket est un point de communication par lequel le processus peut mettre ou recevoir des informations vers ou
en provenance d'un autre socket.
Serveur Client

Cration et attachement
d'une socket d'coute
socket()
Cration de la socket
cliente
Le serveur cre Cration du service
un fils pour bind() socket()
traiter les Le serveur passe en mode coute
requtes et lui il peut accepter des demandes de connexion
mme se remet
l'coute listen() bind()
Demande de connexion

accept() connect()

fork()

read() write() recv() send() read() write() recv() send()

close() close()

Chaque close() ne ferme quun seul sens de communication !

3.1 Fichiers d'en-tte


#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h> /* constantes, familles... */
#include <netinet/in.h> /* struct sockaddr_in */
#include <arpa/inet.h> /* prototypes pour les fonctions dans inet(3N) */
#include <netdb.h> /* struct hostent */

3.2 Le type sockaddr_in


Une adresse de socket dans la famille Internet est dfinie par :

struct sockaddr_in {
short sin_family; /* la famille de protocole */
u_short sin_port; /* numero de port */
struct in_addr sin_addr; /* adresse IP de la machine */
char sin_zero[8]; /* remplissage pour faire 16 octets */
};

Rappel : L'adresse IP d'une machine (type struct in_addr) est en fait 4 octets, qu'on crit gnralement sous la
forme 147.127.64.7.

3.3 Cot client


u_long htonl (u_long hostlong);
u_short htons (u_short hostshort);
struct hostent *gethostbyname (char *name);

G. BENAY 3 2007/2008
ESCPI TP 3 EI2

int socket (int domain, int type, int protocol);


int connect (int s, struct sockaddr *name, int namelen);

On utilise des sockets dans la famille de protocole Internet, de type stream (fiable, fifo, bi-directionnel), crs par :
socket (PF_INET, SOCK_STREAM, 0). Pour la connexion (serverhost est le nom de la machine que l'on
veut contacter, port est le numro du port sur cette machine) :
{ int sc;
struct hostent *sp;
struct sockaddr_in sins;
/* Obtention d'information au sujet de la machine `serverhost' */
sp = gethostbyname (serverhost);
if (sp == NULL) {
fprintf (stderr, "gethostbyname: %s not found\n", serverhost);
exit (1);
}
/* Creation d'un socket Internet de type stream (fiable, bi-directionnel)
*/
sc = socket (PF_INET, SOCK_STREAM, 0);
if (sc == -1) {
perror ("socket failed");
exit (1);
}
/* Remplissage de la structure `sins' avec la famille de protocoles
Internet,
* le numero IP de la machine a contacter et le numero de port. */
sins.sin_family = AF_INET;
memcpy (&sins.sin_addr, sp->h_addr_list[0], sp->h_length);
sins.sin_port = htons (port);
/* Tentative d'etablissement de la connexion. */
if (connect (sc, (struct sockaddr *)&sins, sizeof(sins)) == -1) {
perror ("connect failed");
exit (1);
}
}

3.4 Cot serveur


int socket (int domain, int type, int protocol);
int setsockopt (int s, int level, int optname, char *optval, int optlen);
int bind (int s, struct sockaddr *name, int namelen);
int listen (int s, int backlog);
int accept (int s, struct sockaddr *addr, int *addrlen);

Ce qui s'utilise (port est le numro du port sur lequel coute le serveur) :
{ struct sockaddr_in soc_in;
int val;
int ss;

/* socket Internet, de type stream (fiable, bi-directionnel) */


ss = socket (PF_INET, SOCK_STREAM, 0);

/* Force la reutilisation de l'adresse si non allouee */


val = 1;
setsockopt (ss, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

/* Nomme le socket: socket inet, port PORT, adresse IP quelconque */


soc_in.sin_family = AF_INET;
soc_in.sin_addr.s_addr = htonl (INADDR_ANY);
soc_in.sin_port = htons (port);

G. BENAY 4 2007/2008
ESCPI TP 3 EI2

bind (ss, &soc_in, sizeof(soc_in));

/* Prepare le socket a la reception de connexions */


listen (ss, 5);

while (1) {
struct sockaddr_in from;
int len;
int f;

/* Accepte une connexion.


* Les parametres `from' et `len' peuvent etre NULL. */
len = sizeof (from);
f = accept (ss, (struct sockaddr *)&from, &len);

/* ... */
}
}

Attention : il manque le traitement d'erreur, qui est indispensable avec les sockets, du fait de la forte probabilit de
dfaillance.
4 Rappels Unix
Tous les appels systmes renvoient -1 en cas d'erreur. Dans ce cas, la variable errno contient le code de l'erreur.
4.1 Obtenir des informations sur un fichier : stat
Pour obtenir des information sur un fichier, utiliser :

int stat (char *pathname, struct stat *buf);

int fstat (int fd, struct stat *buf);

La structure struct stat contient de nombreux champs (voir man stat pour plus de dtails), dont st_uid
(propritaire du fichier), st_size (taille du fichier), st_mode (droit d'accs), st_mtime (date de dernire
modification)...
#include <sys/types.h>
#include <sys/stat.h>
main (int argc, char **argv)
{
struct stat buf;
if (stat (argv[1], &buf) == -1)
perror (argv[1]);
else
printf ("%s: proprietaire %d, taille %d\n", argv[1], buf.st_uid,
buf.st_size);
}

4.2 Ouverture d'un fichier open


Pour obtenir un descripteur de fichier permettant d'accder (lecteur ou criture) un fichier, on utilise l'appel
systme open. Les trois formes d'utilisation habituelles sont :
int fd = open ("toto", O_RDONLY);
int fd = open ("toto", O_WRONLY | O_CREAT | O_TRUNC, 0644);
int fd = open ("toto", O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd == -1) { erreur... }

La premire ligne ouvre le fichier en lecture ; la deuxime ouvre le fichier pour criture, avec cration s'il n'existe
pas et troncature s'il existe dj ; la troisime ligne ouvre le fichier pour criture, avec cration s'il n'existe pas et erreur
s'il existe dj. Le troisime argument est utilis pour dfinir les droits d'accs (ici rw-r--r--) s'il y a cration du

G. BENAY 5 2007/2008
ESCPI TP 3 EI2

fichier.
4.3 Destruction d'un fichier unlink
L'appel systme :
int unlink (char *pathname);
dtruit le lien spcifi par pathname. Si ce lien tait le dernier lien vers le fichier, le fichier est effac.
4.4 Lecture/criture read /write
int read (int fd, void *buf, int nbyte);
int write (int fd, const void *buf, int nbyte);

L'appel systme read lit sur le descripteur au plus nbyte octets et les range l'adresse buf. Il renvoie le nombre
d'octets effectivement lus, ou 0 si la fin de fichier a t atteinte (il n'y a et il n'y aura plus rien lire : socket ferm ou
fichier compltement lu), ou -1 en cas d'erreur.
L'appel systme write crit sur le descripteur au plus nbyte octets rangs l'adresse buf. Il renvoie le nombre
d'octets effectivement crits, ou -1 en cas d'erreur.
Quand read ou write sont appliqus un fichier, le nombre d'octets lus ou crits est toujours le nombre demand
(sauf lorsque la fin du fichier est atteinte) ; quand ils sont appliqus un descripteur associ un socket, le nombre
d'octets lus ou crits peut tre infrieur au nombre demand.
4.5 Les signaux
Un signal est un vnement asynchrone auquel il est possible d'associer un traitement spcifique (une procdure qui
sera invoque par le systme la dlivrance du signal). En absence de traitement, un signal entrane en gnral la mort
du processus destinataire. L'association d'un traitement se fait au moyen de la primitive signal :
void traitement_sig (int sig)
{
signal (SIGCHLD, traitement_sig); /* remise en place du traitement */
...
}
...
main()
{
...
signal (SIGCHLD, traitement_sig);
...
}

5 Droulement des tps


Fournis dans un paquet zip (miniftp.zip) ou tar (miniftp.tgz):
requetes.h : les types et macro-dfinitions utiles ;
common.h, common.c : deux petites procdures bien utiles, notamment copy_n_bytes ;
miniftp.c : l'architecture du client ;
miniftpd.c : l'architecture du serveur ;
Makefile : pour compiler.
5.1 Le Client
criture du client miniftp. Le code get est dj fourni,
compiler et excuter en utilisant le serveur sur kirov ou karkov (port 38590) ;
crire les autres requtes ; crire le code des autres requtes (put, dir, del) ;
valider l'ensemble.
5.2 Le Serveur
criture du serveur miniftpd. La rponse la requte get est fournie.
crire les autres rponses aux requtes (put, dir, del) ;
valider soigneusement.
5.3 Rapport
Rappel vous devez fournir un rapport avec l'ensemble des programmes :
Le serveur,
Le client,
Une prsentation des fonctions ralises.

G. BENAY 6 2007/2008
ESCPI TP 3 EI2

1. Personne seule
TP3ReseauNomGrooupeEI2Nom.zip
NomGrooupeEI2: le nom du groupe EI2 (EI2AD, EI2AG, ou EI-I2B)
Nom1 votre nom si seul,
2. Binme
TP3ReseauNomGrooupeEI2Nom1Nom2.zip
NomGrooupeEI2 : le nom du groupe EI2 (EI2AD, EI2AG, ou EI-I2B)
Nom1 le nom d'un des membres du binme
Nom2 le nom de l'autre membre du binme
Vous envoyez votre rapport par courrier lectronique l'adresse : gerard.benay@cnam.frau plus
tard le :
EII2AG:Vendredi6juin2008
EII2AD:Vendredi30mai2008
EII2B:Vendredi30mai2008

G. BENAY 7 2007/2008