Vous êtes sur la page 1sur 7

ADJIDO Idjiwa, CLOIREC Olivier.

TP de réseaux : Les sockets.

Introduction ................................................................................................................................ 2
Qu’est qu’une socket ? ............................................................................................................... 2
Différents modes de communication...................................................................................... 2
Définition d’une connexion.................................................................................................... 2
Comment utiliser les sockets ? ................................................................................................... 3
Socket et serveur .................................................................................................................... 3
Socket et client ....................................................................................................................... 3
Mise en œuvre ............................................................................................................................ 4
Echange entre un client et un serveur..................................................................................... 4
Limites de l’application...................................................................................................... 4
Protocole............................................................................................................................. 4
Echange entre plusieurs clients et un serveur......................................................................... 6
Conclusion.................................................................................................................................. 7
Introduction
A travers ce TP, nous devions comprendre les mécanismes et fonctionnement des sockets,
base de tout échange de donnée entre programme.
Nous avons du réaliser une application mettant en œuvre l’utilisation des sockets, nous avions
le choix entre un « chat » ou un pseudo ftp. Nous avons choisi de faire le pseudo ftp.
Nous ferons donc un rappel de ce que sont les sockets, puis nous présenterons le travail
effectué et les choix effectués afin de réaliser l’application client/serveur.

Qu’est qu’une socket ?

Différents modes de communication

« Les sockets sont des mécanismes d’interface de programmation. »


Les sockets permettent d’établir des communications entre applications afin d’échanger des
données par exemple.
Il existe plusieurs modes de communication, on distingue ainsi trois modes différents :
- le mode connecté
- le mode non connecté
- le mode raw

Le mode non connecté (utilisant UDP sous IP) ne garantit pas la séquentialité des transferts,
ce n’est pas donc un mode fiable.
Le mode en caractère (raw) fonctionne en mode datagramme et est destiné au développement
de nouveaux protocoles.
Nous utilisons dans la mise en œuvre de notre application client/serveur le mode connecté qui
nous permet d’établir un circuit virtuel de communication point à point.

Définition d’une connexion

Une connexion est définie par cinq paramètres. Ces paramètres sont le type de protocole
utilisé (UDP, TCP), les adresses de la première et seconde machine, et enfin les deux numéros
de port associés au processus s’exécutant sur les deux machines.
Cependant, le type de protocole étant commun aux deux applications, localement, chaque
machine n’aura que trois paramètres à fournir au système lors de la création du socket.
Ainsi, du côté serveur doivent être définis le protocole utilisé, le numéro du port local sur
lequel les connexions seront attendues. Du côté client, il faudra indiquer le protocole,
l’adresse de la machine distante et le numéro du port sur lequel cette machine attend les
connexions. Les autres données sont déduites par le système, par exemple, le serveur connaît
sa propre adresse, etc.
Comment utiliser les sockets ?
Nous n’étudierons ici que le mode que nous avons utilisé pour la mise en œuvre de
l’application : le mode connecté.
Toutes les applications utilisant les sockets suivent un squelette type selon le mode de
connexion.

Socket et serveur

L’enchaînement des primitives du côté serveur est le suivant :


Socket, bind, listen, accept, read, write, close.

L’appel système de la primitive socket indique qu’une application désire communiquer avec
un autre programme. L’identificateur retourné par l’appel permettra de faire référence à cette
socket (tel un descripteur de fichier).

La primitive bind nous permet d’indiquer au noyau de système les paramètres nécessaires au
fonctionnement du socket (le port sur lequel on attendra les communications).

Listen, place la socket dans un état d’attente des requêtes de connexion.

Quand une connexion est établie par un client, accept (qui est une primitive blocante)
retourne un identificateur de socket sur lequel les données pourront être échangées.

Les appels systèmes read et write, permettent l’échange effectif des données, ces deux
primitives sont les même que pour les fichiers.

Close, permet de fermer localement la connexion, une séquence de fermeture est envoyée sur
le réseau.

Socket et client

L’enchainement des primitives du côté du client est le suivant :


Socket, connect, write, read, close

La création du socket est identique à celle du serveur.


Bind, est facultatif, car le système alloue dynamiquement le numéro de port local identifiant
la connexion.
Connect permet d’ouvrir une connexion avec l’application serveur, une séquence d’ouverture
est envoyée sur le réseau et la connexion est établie si il n’y aucune erreur en retour.
Mise en œuvre
Echange entre un client et un serveur

Afin de réaliser cet échange, nous nous sommes d’abord définis les limites de l’application et
le protocole utilisé.

Limites de l’application

Notre application prend en compte huit commandes exécutables du coté client :


- a : aide
- uls : nous permet de lister les fichiers se trouvant sur le client ;
- ls : nous permet de lister les fichiers se trouvant sur le serveur ;
- ucd: nous permet de changer de niveau dans l’arborescence du client ;
- cd : nous permet de changer de niveau dans l’arborescence du serveur ;
- get : nous permet de récupérer un fichier du serveur ;
- put : nous permet de transférer un fichier vers le serveur.
- exit : quitter

Protocole

Une première proposition

Une première analyse nous à conduit au protocole que nous décrivons dans les lignes ci-
dessous.

Nous avions défini deux structures principales.


La première que nous avons nommé request et la seconde answer.
Request {char * path, int kind, int taille}
Answer {int ack, int err, int taille}

La type de la requête (ls, put, get, etc.) est indiqué via le second paramètre (kind).
Le premier paramètre sert pour indiquer le chemin permettant d’accéder au fichier, il ne sert
pas lorsqu’un put est exécuter par exemple.
Un test est fait afin de vérifier qu’un fichier à transférer n’existe pas déjà sur le coté ou l’on
souhaite transférer. Une erreur est déclenchée (voir paragraphe suivant) si c’est le cas.

La requête obtient alors du serveur une réponse lui indiquant la suite du déroulement.
Le champ ack indique quel type de réponse on obtient, une erreur, une validation.
Si c’est une erreur, alors le second paramètre de la structure answer est renseigné selon le type
de l’erreur détectée.
Lorsque tout se passe bien, alors si nécessaire, par exemple dans le cas d’un get lancé du
client, la taille du fichier est passé dans le troisième champ de la requête afin d’indiquer au
client combien fait le fichier.

Ainsi, sans pour autant suivre les RFC, nous avions un système de dialogue avec accusé
réception des requêtes. Accusé qui devait être valide avant toute échange de données.
Pour cause de mauvais organisation, le temps nous manquait, nous avons alors préféré faire
une analyse annexe, plus légère et plus rapide à mettre en place dans le cas ou n’aurions pas le
temps d’implémenter notre analyse première.
Ce qui est arrivé, c’est donc la seconde solution, celle présentée dans le paragraphe qui suit
que nous avons fini par implémenter afin de respecter les délais de la présentation.

La solution implémentée

Le ls à été implémenté en utilisant la structure dirent (struct dirent **namelist;) et un scandir.


Le uls utilise un simple system(« ls »).
Le ucd et cd utilise la commande chdir.
Avant le transfert des fichiers, on indique quelle est la taille du fichier à transférer.
Une fonction EnvoiTout envoi la taille puis les données via la fonction Envoyer ci-dessous.

La fonction permettant d’envoyer des données au serveur :

int Envoyer (int socket, void * buf, unsigned taille) {


unsigned send=0;
char * buffer=(char *)buf;
while (send<taille) {
int lon=write(socket,buffer+send,taille-send);
if (lon<=0) return -1;
send+=lon;
}
return 0;
}

La fonction permettant de recevoir des données du serveur :

int Recevoir (int socket, void * buf, unsigned taille) {


unsigned lu=0;
char * buffer=(char *)buf;
while (lu<taille) {
int lon=read(socket,buffer+lu,taille-lu);
if (lon<=0) return -1;
lu+=lon;
}
return 0;
}

Nous avons ensuite définis les fonctions d’envoi et de réception de fichier.


Fonction permettant d’envoyer un fichier sur le serveur :
void put_fichier( int soc, char * fichier )
{ char buf[256];
FILE * f = fopen( fichier, "w" );
int taille = 0;
int i;
while ( taille != -1 )
{
Recevoir(soc, &taille, sizeof(taille) );
Recevoir(soc, &buf, taille );
for ( i=0 ; i<taille ; i++ )
fputc( buf[i], f );
}
fclose( f );
}
Fonction permettant de recevoir un fichier du serveur :

void get_fichier(int soc, char * fichier)


{
char buf[256];
int taille;
int i,c;

FILE * f = fopen( fichier, "r" );


if ( f != 0 )
{
c = fgetc( f );
while ( c != EOF )
{
i = 0;
while ( (i < 256) && (c != EOF) )
{
buf[i] = c;
c = fgetc( f );
i++;
}
taille = i ;
Envoyer( soc, &taille, sizeof(taille) );
Envoyer( soc, &buf, taille );
}
fclose( f );
taille = -1 ;
Envoyer( soc, &taille, sizeof(taille) );
}
else
{
taille = strlen("Erreur de lecture pour le fichier.\n");
Envoyer( soc, &taille, sizeof(taille) );
Envoyer( soc, "Erreur de lecture pour le fichier.\n",
strlen("Erreur de lecture pour le fichier.\n") );
}
}

Ce protocole nous permet de transférer des fichiers non forcément .txt, des tests ont été
effectués sur des pdf, une fois transférés, nous pouvions les lire avec gv.

Echange entre plusieurs clients et un serveur

Classiquement, afin de pouvoir gérer plusieurs connexions simultanée sur un serveur nous
avons utilisé la commande Unix fork. Nous dupliquons ainsi le processus (créons un
processus fils qui attends de nouvelle connexion).
L’extrait du code correspondant à partir du listen :
listen(sd,5);
for (;;)
{
ladcour=sizeof(ladcour);

if ((nsd = accept(sd, (struct sockaddr*) &adclcour, &ladcour))<0)


{
//perror(">> Probleme sur l'accept ");
exit(1);
}
else printf("> Connexion autorisee a %s\n", inet_ntoa(
adclcour.sin_addr )) ;

pid=fork();
if (pid==0) { // processus fils
close(sd);

while ( !fin_connexion )
renvoi(nsd, (&adclcour)->sin_addr );

close(nsd);
}

else close(nsd);
}
close(sd);

La commande reste cependant coûteuse car le système devra gérer autant de processus que de
connexions. A notre échelle, dans le cadre de l’utilisation que nous en faisons (simple
présentation) nous n’avons pas jugé utile d’implémenter une solution avec des threads.

Conclusion
Le TP nous à permis d’intégrer le mécanisme des sockets, base de toute communication entre
processus.
Il nous à également permis d’apprécier d’avantage le travail existant derrière une application
réelle tel que le ftp (nous somme allé consulter les RFC).
Nous nous sommes rendus compte de la complexité des protocoles devant mettre en place des
reprises sur erreur, gérés des pauses ou suivre des normes tout simplement.
Nous avons pu mettre en place une solution permettant de répondre au problème posé :
échanger des informations entre plusieurs client et un serveur en utilisant les sockets.
Cependant, nous n’avons pas eu le temps d’essayer de mettre en œuvre la solution selon
laquelle un client exécute le serveur (utilisation de la commande rexec) lorsque celui-ci n’est
pas en route.

Vous aimerez peut-être aussi