TP réseau - Token ring en C - un paquet - mode non connecté

De Wiki de Romain RUDIGER
Aller à : navigation, rechercher

Retourner sur la page d'index concernant la conception de protocoles de communication Token ring en C++

Exercice 1 : l’anneau

Rappel du cahier des charges

Problématique

Chaque hôte de l’anneau est muni d’une bouche et d’une oreille. Un seul paquet circule sur l’anneau. Quelle est la meilleure façon de concevoir un protocole réseau pour créer un anneau ?

Polytech S2 TP Reseau anneau.JPG

Description du paquet

Voici la description du paquet que nous utiliserons :

Polytech S2 TP Trame.JPG

  • Le type du paquet est « jeton », « accusé de réception » ou « donnée ». Ces types seront représentés par des chiffres, respectivement : 0, 1, 2.
  • L’adresse de l’émetteur est l’adresse de l’hôte qui a émis le paquet (et non juste transmis).
  • L’adresse de destination est l’adresse de l’hôte à qui est destiné le paquet.
  • Données représente les informations à transmettre.

Traitement logique du paquet

Nous avons fait un diagramme logique pour représenter le traitement logique qui est effectué lors de la réception d’un paquet : Polytech S2 TP Diagramme Logique Paquet.JPG

Deux améliorations sont apportées :

  1. Un hôte ne peut pas s’accaparer la parole puisqu’il ne peut envoyer que 3 paquets au maximum lorsqu’il possède le jeton.
  2. Contrôle des échanges : l’émetteur d’un message reçoit un accusé de réception.


Description de l’algorithme

Voici une brève description de l’algorithme :

  • Initialisation du programme :
> création des prises (émission/réception)
> demande a l’utilisateur s’il veut initialiser l’anneau en envoyant un jeton à l’hôte suivant.
  • Début boucle infinie
Attente de la réception d’un paquet
Début du traitement de la trame
Si c’est un jeton
Demande à l’utilisateur s’il veut envoyer des données
Si oui :
saisie de l’adresse destination
saisie des données
type = 2 //numéro du type "données"
envoie de la trame
Si non :
on fait passé le jeton à l’hôte suivant
Sinon //si ce n’est pas un jeton
Si nous somme l’émetteur de la trame : affichage d’une erreur
Sinon
Si nous sommes le destinataire
Si c’est un accusé de réception :
message : « Votre message a bien été reçu »
Si l’utilisateur a envoyé 3 trames
on fait passé le jeton
Sinon on demande à l’utilisateur s’il veut envoyer des données
Si oui :
saisie de l’adresse destination
saisie des données
type = 2 //numéro du type "données"
envoie de la trame
Si non :
on envoi un jeton à l’hôte suivant
Sinon ce sont des données donc :
affichage du message
envoi d’un accusé de réception
  • Fin de la boucle infinie

Code source du programme

Voici le code du fichier d’en tête (primitives.h) :

/*
 * primitives.h
 * déclaration des variables et des fonctions
 *
 * Travaux Pratiques réseau SILR 1
 * ROCHE RUDIGER RAIRAT
 * 2006/07
 */

#include <sys/types.h> /* utilisé pour le type size_t permettant de connaître la taille de la trame avant de l'envoyer */
#include <assert.h>  	/* pour insertion messages d'erreur */
#include <stdio.h>   	/* pour entrees-sorties */
#include <stdlib.h> 	/* librairie */
#include <string.h>  	/* pour chaine de char */
#include <unistd.h> 	/* types et constants symboliques */

/* définition de la structure d'une trame */
struct trame{
    char type[3];
    char adrs_emet[15];
    char adrs_dest[15];
    char donnee[100];
};
typedef struct trame trame;

char adrs_local[15]; //contiendra l'adresse de notre machine

char adrs_suiv[15]; //contiendra l'adresse de la machine suivante

int socketEmission, socketReception; /* variables pour le numéro des sockets */

int nbr_trame; /* variable pour compter le nombre de trame envoyées */

int creePriseEmission (char *, int);	//adresse, numéro de port

int creePriseReception (int);	//numéro de port de réception

int recoit (int, trame *, size_t);	//fonction permettant de recevoir une trame

int envoie (int, trame *, size_t);	//fonction permettant d'envoyer une trame

void envoie_jeton (trame *);	/* fonction permettant d'envoyer un jeton
				* @param : adresse de la trame */

void envoie_donnee (trame *);	/* fonction permettant d'envoyer des données
				* ces données seront demandées à l'utilisateur
				* @param : adresse de la trame */

void envoie_accuse (trame *);	/* fonction permettant d'envoyer un accusé de réception
				* @param : adresse de la trame */

int traitement(trame *);	/* fonction de traitement qui est appelée lors de la réception d'une trame
				* @param : adresse de la trame */

Code du fichier exo1.c avec la fonction main :

/* exo1.c
 * fonction principale du programme
 *
 * Travaux Pratiques réseau SILR 1
 * ROCHE RUDIGER RAIRAT
 * 2006/07
 */

#include "primitives.h" /* fichier d'en tête des fonctions et déclaration des variables */

/* fonction principale du protocole 
   @param : adresse IP de l'hote suivant
   @param : port d'écoute de la machine suivante
   @param : adresse IP locale
   @param : port d'écoute de notre machine
   */
int main (int argc, char **argv)
{
    /*> test du nombre de paramètre */
   assert (argc == 5); /* erreur si pas 4 arguments */

    /* récupération des arguments et affichage */
   int port_emi; //contiendra le port d'émission
   int port_recep;	//contiendra le port de réception
   port_emi=atoi(argv[2]);	//conversion de la chaîne de caractères en entiers
   port_recep=atoi(argv[4]);	//idem
   strcpy(adrs_local,argv[3]);	//copie de l'argument dans la variable
   strcpy(adrs_suiv,argv[1]);		//idem
	 /* affichage des arguments */
   printf("########################################\n");
   printf("#adresse du pc suivant : %s\n",adrs_suiv);
   printf("#port du pc suivant    : %d\n",port_emi);
   printf("#adresse de notre pc   : %s\n",adrs_local);
   printf("#port de reception     : %d\n",port_recep);
   printf("########################################\n");

	 /* initialisation des sockets */
   socketEmission = creePriseEmission (adrs_suiv, port_emi); /* socket pour l'émission vers le port passé en argument sur de l'hote destination */
   socketReception = creePriseReception (port_recep); /* socket pour la réception sur le port passé en argument */
   trame t,*ptrame; /* création de la trame a envoyer */
   ptrame=&t;	/* création d'un pointeur sur cette trame */ 
 
    /*> partie permettant d'envoyer une trame vide au poste suivant pour initialiser l'anneau */
	char reponse[2];	//contiendra la réponse de l'utilisateur
	printf("> Voulez-vous initialiser l'anneau en envoyant un jeton ? o/n ");
	scanf("%s",reponse);
	if(strcmp(reponse,"o")==0){
 		printf("########################################\n");
		printf("#Envoi d'un jeton.\n");
		strcpy(ptrame->type,"0"); //on met le type a jeton
		strcpy(ptrame->adrs_emet,adrs_local); //adrs local
		strcpy(ptrame->adrs_dest,adrs_suiv); //adrs du pc suivant
		envoie (socketEmission, ptrame, sizeof(trame)); //envoie
 		printf("########################################\n");
	}
    /*> début de la boucle infinie */
   while(1){
			printf("########################################\n");
			printf("#Attente de la réception d'une trame...\n");
			recoit(socketReception, ptrame, sizeof(trame));
			traitement(ptrame);	//appel de la fonction traitant la trame
			printf("########################################\n");
   }
	return 0;
}

Voici le fichier traiter.c qui contient plusieurs fonctions utilisées par que la fonction traitement appelé lors de la réception d’une trame.

/* traiter.c
 * traitement d'un paquet
 *
 * Travaux Pratiques réseau SILR 1
 * ROCHE RUDIGER RAIRAT
 * 2006/07
 */

#include "primitives.h" /* fichier d'en-tête des fonctions et déclaration des variables */

/* fonction permettant d'afficher une trame 
 * @param : adresse d'une variable de type trame
 */
void afficher(trame *ptrame){
	//affichage si c'est un jeton
   if(strcmp(ptrame->type,"0")==0)
		printf("#Nous avons reçu un jeton, il vient de : %s \n",ptrame->adrs_emet);
	//affichage si c'est un accusé de réception
	if(strcmp(ptrame->type,"1")==0)
		printf("#La machine %s a bien reçu notre message.\n",ptrame->adrs_emet);
	//affichage si ce sont des données
   if(strcmp(ptrame->type,"2")==0){
		printf("#Affichage des données venant de : %s\n",ptrame->adrs_emet);
		printf("#Voici les données : %s\n",ptrame->donnee);
	}
}

/* fonction permettant d'envoyer un jeton
 * @param : adresse d'une variable de type trame
 */
void envoie_jeton(trame *ptrame){
	printf("#Nous avons passé le jeton.\n");
	strcpy(ptrame->type,"0");	//type jeton
	strcpy(ptrame->adrs_emet,adrs_local);
	strcpy(ptrame->adrs_dest,adrs_suiv);
	envoie(socketEmission, ptrame, sizeof(trame));
}

Voici la fonction permettant d’envoyer un paquet de données :

/* fonction permettant d'envoyer des données
 * ces données seront demandées à l'utilisateur
 * @param : adresse d'une variable de type trame
 */
void envoie_donnee(trame *ptrame){
	strcpy(ptrame->type,"2");	//type « donnée »
   printf("> Saisir l'adresse de destination (mettre d pour l'hote suivant) : ");
   scanf("%s",ptrame->adrs_dest);
	if(strcmp(ptrame->adrs_dest,"d")==0) strcpy(ptrame->adrs_dest,adrs_suiv); 
   printf("> Saisir les données (terminer la saisie par FIN) : ");
	char saisie[30];	//contiendra chaque mot
	strcpy(ptrame->donnee,"");	//vide la variable
	do { //tant que l'utilisateur n'a pas tapé le mot FIN
		scanf("%s",saisie);
		if(strcmp(saisie,"FIN")!=0){ 	//si ce n'est pas le mot FIN on enregistre le mot saisi
		strcat(ptrame->donnee," ");	//on met un espace a la suite
		strcat(ptrame->donnee,saisie);//on ajoute le mot a la suite de l'espace
		}
	} while(strcmp(saisie,"FIN")!=0);
	strcpy(ptrame->adrs_emet,adrs_local);
	envoie(socketEmission, ptrame, sizeof(trame));//envoi de la trame
	nbr_trame++;//on incrémente le nombre de trame envoyé
	printf("#La trame a été envoyé, attente de l'accusé de réception...\n");
}

/* fonction permettant d'envoyer un accusé de réception
 * @param : adresse de la trame reçu
 */
void envoie_accuse(trame *ptrame){
	printf("#Envoi de l'accusé\n");
	strcpy(ptrame->type,"1");	//type accusé
	strcpy(ptrame->adrs_dest,ptrame->adrs_emet);
   strcpy(ptrame->adrs_emet,adrs_local);
	envoie(socketEmission, ptrame, sizeof(trame));
}

Voici la fonction traitement :

/* fonction de traitement qui est appelée lors de la réception d'une trame
 * cette fonction agit en couche, elle fait des choix au fur et à mesure de la lecture du paquet.
 * @param : adresse d'une varaible de type trame
 */
int traitement(trame *ptrame){
	char reponse[2];//variable pour mémoriser la réponse de l'utilisateur
	if(strcmp(ptrame->type,"0")==0){//si c'est un jeton
		afficher(ptrame);//affichage de la trame
		nbr_trame=0; //initialisation du nombre de trame envoyé
		fflush(stdin);
		printf("> Voulez vous envoyer un paquet ? o/n ");
		scanf("%s",reponse);
		if(strcmp(reponse,"n")==0){//on transmet le jeton
			 envoie_jeton(ptrame);
	   }
		else{ // nous envoyons la première trame de donnees
			 envoie_donnee(ptrame);
		}
   }
	// ce n'est pas un jeton alors
	else{
		// si nous en somme l'émetteur on affiche une erreur
		if(strcmp(ptrame->adrs_emet,adrs_local)==0)
	   	printf("#!!! Erreur d'adressage, le paquet a fait le tour");
		// s'il n'y a pas d'erreur on continue
		else {
			// si la trame est pour nous
	   	if(strcmp(ptrame->adrs_dest,adrs_local)==0){
				// si c'est un accusé de réception
				if(strcmp(ptrame->type,"1")==0){
		  			afficher(ptrame);//affichage de la trame
					// on vérifie que l'on a pas déja envoyé 3 trames
					if(nbr_trame==3) {
						printf("#Passage du jeton car vous avez déjà envoyé trois paquets.\n");
						envoie_jeton(ptrame);
					}
					// si on a envoyé moins de 3 paquets
					else {
						fflush(stdin);
						printf("> Voulez vous envoyer un paquet ? o/n ");
    					scanf("%s",reponse);
						if(strcmp(reponse,"o")==0){//on envoi un paquet
							envoie_donnee(ptrame);
						}
						else{
			 				envoie_jeton(ptrame);
						}							 
					}
				}
				else{//si ce sont des donnees
		  		afficher(ptrame);//affichage de la trame
				envoie_accuse(ptrame);//envoi de l'accusé
				}
			}
		}
	}
   return 0;
}

Exemple d’utilisation du programme

Le programme a été testé sur deux machines virtuelles. C’est la distribution Debian qui a été choisi et c’est le logiciel VMware Workstation qui a servi. Voici quelque exemple d’utilisation :

Poste 1 (IP : 192.168.0.191) Poste 2 (IP : 192.168.0.190)
Compilation du programme :
casi@poste1:~$ make
gcc    -c -o exo1.o exo1.c
gcc    -c -o envoie.o envoie.c
gcc    -c -o recoit.o recoit.c
gcc    -c -o creePriseEmission.o creePriseEmission.c
gcc    -c -o creePriseReception.o creePriseReception.c
gcc    -c -o traiter.o traiter.c
ar rv primitives.a exo1.o envoie.o recoit.o creePriseEmission.o creePriseReception.o traiter.o
ar: création de primitives.a
a - exo1.o
a - envoie.o
a - recoit.o
a - creePriseEmission.o
a - creePriseReception.o
a - traiter.o
ranlib primitives.a
gcc -o exo1 primitives.a
casi@poste1:~$
On fait la même chose de ce coté.
Lancement du programme en indiquant l’adresse du pc suivant, le port sur lequel il écoute ainsi que l’adresse de notre pc et son port d’écoute :
casi@poste1:~$ ./exo1 192.168.0.190 3000 192.168.0.191 3000
########################################
#adresse du pc suivant : 192.168.0.190
#port du pc suivant    : 3000
#adresse de notre pc   : 192.168.0.191
#port de reception     : 3000
########################################
Il faut changer les adresses IP :
casi@poste2:~$ ./exo1 192.168.0.191 3000 192.168.0.190 3000
########################################
#adresse du pc suivant : 192.168.0.191
#port du pc suivant    : 3000
#adresse de notre pc   : 192.168.0.190
#port de reception     : 3000
########################################
On n’initialise pas l’anneau sur le poste 1 mais sur le 2 :
> Voulez vous initialiser l'anneau en envoyant un jeton ? o/n n
########################################
#Attente de la réception d'une trame...
Nous initialisons l’anneau en envoyant un jeton :
> Voulez vous initialiser l'anneau en envoyant un jeton ? o/n o
########################################
#Envoi d'un jeton.
########################################
########################################
#Attente de la réception d'une trame...
Nous recevons bien le jeton, mais nous ne voulons pas envoyer de message :
#Nous avons reçu un jeton, il vient de : 192.168.0.190
> Voulez vous envoyer un paquet ? o/n n
#Nous avons passé le jeton.
########################################
########################################
#Attente de la réception d'une trame...
Nous recevons bien le jeton, nous voulons de plus envoyer un message au poste 1 :
#Nous avons reçu un jeton, il vient de : 192.168.0.191
> Voulez vous envoyer un paquet ? o/n o
> Saisir l'adresse de destination (mettre d pour l'hote suivant) : d
> Saisir les données (terminer la saisie par FIN : Bonjour, je t'envoie un petit coucou du poste 2. 
Cordialement romain. FIN
#La trame a été envoyé, attente de l'accusé de réception...
########################################
########################################
#Attente de la réception d'une trame...
Sur le poste un on reçoit bien la message, un accusé de réception est automatiquement envoyé :
#Affichage des données venant de : 192.168.0.190
#Voici les données :  Bonjour, je t'envoie un petit coucou du poste 2. Cordialement romain.
#Envoi de l'accusé
########################################
########################################
#Attente de la réception d'une trame...
Nous recevons bien l’accusé de réception et nous envoyons deux autres messages pour tester la limite de 3 messages par jeton :
#La machine 192.168.0.191 a bien reçu notre message.
> Voulez vous envoyer un paquet ? o/n o
> Saisir l'adresse de destination (mettre d pour l'hote suivant) : d
> Saisir les données (terminer la saisie par FIN : message 2 ! FIN
#La trame a été envoyé, attente de l'accusé de réception...
########################################
########################################
#Attente de la réception d'une trame...
#La machine 192.168.0.191 a bien reçu notre message.
> Voulez vous envoyer un paquet ? o/n o
> Saisir l'adresse de destination (mettre d pour l'hote suivant) : 192.168.0.191
> Saisir les données (terminer la saisie par FIN : message 3 ! FIN
#La trame a été envoyé, attente de l'accusé de réception...
########################################
########################################
#Attente de la réception d'une trame...
#La machine 192.168.0.191 a bien reçu notre message.
#Passage du jeton car vous avez déjà envoyé trois paquets.
#Nous avons passé le jeton.
########################################
########################################
#Attente de la réception d'une trame...
Sur le poste 1 nous recevons bien les 2 messages :
#Affichage des données venant de : 192.168.0.190
#Voici les données :  message 2 !
#Envoi de l'accusé
########################################
########################################
#Attente de la réception d'une trame...
#Affichage des données venant de : 192.168.0.190
#Voici les données :  message 3 !
#Envoi de l'accusé
########################################
########################################
#Attente de la réception d'une trame...
Puis nous recevons le jeton puisque le poste 2 a envoyé 3 messages :
#Nous avons reçu un jeton, il vient de : 192.168.0.190

Nous envoyons un message au poste 2 pour essayer :

> Voulez vous envoyer un paquet ? o/n o
> Saisir l'adresse de destination (mettre d pour l'hote suivant) : d
> Saisir les données (terminer la saisie par FIN : coucou du poste 1 !!! FIN
#La trame a été envoyé, attente de l'accusé de réception...
########################################
########################################
#Attente de la réception d'une trame...
Le poste 2 reçoit bien le message et envoie l’accusé :
#Affichage des données venant de : 192.168.0.191
#Voici les données :  coucou du poste 1 !!!
#Envoi de l'accusé
########################################
########################################
#Attente de la réception d'une trame...
Le poste 1 reçoit l’accusé et demande a l’utilisateur s’il veut envoyer un autre paquet :
#La machine 192.168.0.190 a bien reçu notre message.
> Voulez vous envoyer un paquet ? o/n

Voila des améliorations pourraient être apportés (demande pour quitter le programme, envoie d’un fichier…) mais le programme fonctionne correctement.


Retourner sur la page d'index concernant la conception de protocoles de communication Token ring en C++