Skip to content

EnseirbTelecom/re216-2023-ilyas

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RE216 - Projet de programmation réseau ---

  1. RE216 - Projet de programmation réseau
  1. Jalons
  1. Tips and Tricks
  2. Rappel de C

Ce projet consiste en la rĂ©alisation d'un grand classique de la programmation rĂ©seau, un cas pratique de discussion instantanĂ©e de type client/serveur. A titre d'exemple, vous pouvez jeter un coup d'Ɠil au protocole IRC (Internet Relay Chat) dĂ©fini originellement par la RFC1459.

Le projet a pour objectif la rĂ©alisation d'une application de chat client/serveur en C permettant d'Ă©changer des messages entre 2 utilisateurs, entre plusieurs utilisateurs, ou Ă  destination de la totalitĂ© des utilisateurs connectĂ©s sur le rĂ©seau, ainsi que de s'envoyer des fichiers. L'objectif sous-jacent de ce projet est la manipulation de l'API socket POSIX en C vue en cours, ainsi que la mise Ɠuvre de communications sur TCP/IP.

DĂ©roulement global

Les enseignements intĂ©grĂ©s que vous avez suivis vous ont donnĂ© les bases solides pour dĂ©buter ce projet. Le travail doit ĂȘtre rĂ©alisĂ© en binome. Vous allez construire votre code petit Ă  petit en suivant des jalons prĂ©-dĂ©finis, dĂ©crits ci-dessous. Un jalon correspond Ă  une Ă©tape de rĂ©alisation, et:

  • chaque jalon doit ĂȘtre intĂ©gralement rĂ©alisĂ© avant de passer au jalon suivant.
  • Une fois le jalon atteint, il faut le soumettre au travers de la procĂ©dure qui vous est donnĂ©e.

La durée de réalisation ce projet ne devrait pas dépasser les 21h20 dont 10h40 pendant les séances encadrées.

Contenu du depot git

Lorsque vous récupérez ce dépot git, il comprend les fichiers/dossiers:

  • ./sample-jalon1 et ./sample-jalon2 qui contiennent des squelettes de codes pour les jalons 1 et 2 ainsi qu'un Makefile.
  • travail/ qui est le dossier dans lequel vous allez coder, c'est celui ce qui sera Ă©valuĂ© pour vos soumissions de jalons
  • rendu_final/ qui est le dossier dans lequel vous allez mettre votre code en fin de projet
  • info.txt que vous devez remplir avec vos noms + prĂ©nom + login github
  • le support du cours

Pusher son code sur le dépot git de GitHub

Pour pouvoir réaliser cette opération, vous allez devoir vous créer un Personal Access Token en suivant ce tutorial.

A la suite de ce tutoriel, vous obtenez un token sous la forme d'une chaine de caractĂšre Ă  fournir Ă  la place de votre mot de passe lorsque vous essayer de git pushvotre code.

Soumission des jalons

La soumission des jalons se fait sur GitHub en créant une release de votre code. Une release de code est une capture de l'état de vos fichiers disponibles sur votre dépot github à un instant t.

Pour faire correctement une release:

  • assurez vous d'avoir bien mis le code du jalon courrant dans le repertoire qui convient (i.e., ./travail pour le code du jalon 1 Ă  4, et ./rendu_final pour le rendu final, etc.).
  • Ne pas mettre de fichiers attachĂ©s Ă  la release, l'Ă©quipe enseignante les ignorera dans l'Ă©valuation.
  • publiez votre release et de ne pas la garder en tant que draft uniquement. Elle sera ignorĂ©e dans le cas Ă©chĂ©ant.

Si vous ne savez pas comment faire une release de votre code sur GitHub, voici la ressource officielle pour apprendre à le faire. Le nom des releases sera jalonx avec x le numéro du jalon. Chaque soumission sera analysée par les detecteurs de plagiat que sont JPLAG et MOSS.

En ce qui concerne les deadlines de rendu des jalons:

  • Jalon 1 : 14 octobre 23h59
  • Jalon 2 : 21 octobre 23h59
  • Jalon 3 : 11 novembre 23h59
  • Jalon 4 et rendu final : 19 novembre 23h59

Soumission finale

POur la osumission finale, copier votre code depuis le repertoire travail/ dans le repertoire rendu_final/ . Cette soumission devra impérativement compiler à l'aide d'un makefile, sans erreurs et sans warning, sur les machines de l'enseirb. Si vous le souhaitez, vous pouvez repartir des makefiles proposés dans les dossiers sample-jalon1 et sample-jalon2.

Pour développer et tester votre code sur les machines de l'enseirb, vous pouvez vous connecter à la machine ssh (i.e., ssh.enseirb.fr) de l'enseirb et ensuite vous connecter à une des machines de TPs de l'enseirb. Pour connaitre les machines disponibles, vous pouvez utiliser la commande netgroup [numéro_de_salle] par exemple netgroup I101.

Evaluation

L'Ă©valuation de votre travail se fera en fonction des critĂšres suivants:

  1. La bonne soumission des jalons en temps et en heure sur votre dépot github;
  2. Le bon respect de l'implémentation des fonctionnalités spécifiées;
  3. Le fonctionnement non erroné en cas de reception et traitement de messages non implémentés et de messages erronés (que ce soit du coté client ou du coté serveur);
  4. La libération de mémoire et la fermeture des sockets (utilisation de valgrind et lsof)

L'Ă©valuation du projet suivra la grille de notation suivante et prendra en compte des points bonus/malus.

  • Non respect des consignes (rendu des jalons aux deadlines indiquĂ©es, fichiers info.txt mal remplis, Makefile manquant/incorrect, compilation avec erreurs/warning importants) : -2 points;
  • Mauvaise utilisation des primitives de lecture et d'Ă©criture pour les sockets : -2 points;
  • Faible qualitĂ© et lisibilitĂ© de code (indentation, main() court -<200 lignes-, factorisation du code, utilisation de fonctions) : -2 points;
  • MĂ©moire allouĂ©e et non libĂ©rĂ©e : -2 points;
  • File descriptors des sockets et des fichiers non fermĂ©s : -2 points;
  • Tout fonctionne en IPv6 et IPV4 : +0.25 point.

Voici la grille de notation Ă  titre indicatif seulement. Pour pouvoir obtenir les points d'un jalon prĂ©cis, toutes les fonctionnalitĂ©s et spĂ©cificitĂ©s des jalons prĂ©cĂ©dents doivent ĂȘtre implĂ©mentĂ©es sans erreurs.

Note RĂ©alisation correcte des exigences du(es) jalon(s)
[0-4[ [0 - Jalon 1[
[4-8[ [Jalon 1 - Jalon 2[
[8-14[ [Jalon 2 - Jalon 3[
[14-18[ [Jalon 3 - Jalon 4[
[18-20] Jalon 4 + Surprise

Surprise : Faites-vous et faites-nous rĂȘver ! Ajoutez des fonctionnalitĂ©s Ă  vos programmes en plus des jalons 1+2+3+4.

Jalons

Jalon 1 - Client-serveur TCP et serveur multi-clients

Top

Description

Dans ce jalon, vous allez vous concentrer sur la rĂ©alisation d'un modĂšle client/serveur IPv4 oĂč le serveur renvoie au client la chaĂźne de caractĂšre prĂ©cĂ©demment envoyĂ©e. Dans ce modĂšle de communication, il y a une partie client et une partie serveur. Pour la premiĂšre partie, le client devra crĂ©er une socket TCP (avec une adresse IPv4 et un numĂ©ro port renseignĂ© en argument du programme), Ă©tablir une connexion sur la socket du serveur. L'envoi et la rĂ©ception de donnĂ©es pourra alors se faire pour le client. ParallĂšlement, du cĂŽtĂ© du serveur, il faudra crĂ©er une socket, la lier Ă  un port d'Ă©coute et repĂ©rer la nouvelle connexion. Une fois la connexion acceptĂ©e, la rĂ©ception et l'envoi de donnĂ©es peut se faire. Dans ce jalon, une fois la connexion Ă©tablie, le client doit envoyer une chaĂźne de caractĂšre tapĂ©e au clavier au serveur. En rĂ©ponse, le serveur rĂ©cupĂšre cette chaĂźne et la renvoie directement Ă  l'utilisateur en question qui doit l'afficher sur son Ă©cran.

Le serveur doit ĂȘtre capable gĂ©rer plusieurs clients en parallĂšle. Pour cela, il faut utiliser un unique processus pour rĂ©aliser toutes les tĂąches en utilisant la fonction poll(). Les informations des clients (descripteur de fichier de la socket client et adresse/port) doivent ĂȘtre stockĂ©s par le serveur dans une liste chaĂźnĂ©e.

Le client doit pouvoir gĂ©rer en mĂȘme temps les messages provenant du serveur et le texte tapĂ© au clavier. Pour cela, il faut Ă©galement utiliser la fonction poll() cĂŽtĂ© client sur l'entrĂ©e standard du programme et la socket pour la communication avec le serveur.

Exigences

Les exigences/requirements pour ce premier jalon sont définis comme suit :

Req1.1 : Création d'un client qui se connecte en TCP à un serveur renseigné par un port et un nom de domaine passés comme arguments du programme comme suit :

./client <server_name> <server_port>

Req1.2 : CrĂ©ation d'un server avec une socket d'Ă©coute qui accepte la connexion et gĂšre les donnĂ©es entrantes. Le port du serveur doit ĂȘtre passĂ© comme argument du programme.

./server <server_port>

Req1.3 : Le serveur doit ĂȘtre capable d’accepter les connexions et de rĂ©pondre Ă  plusieurs clients en mĂȘme temps.

Req1.4 : Le client doit pouvoir prendre une chaĂźne de caractĂšre tapĂ©e au clavier, l'envoyer au serveur et recevoir de ce dernier la mĂȘme chaĂźne de caractĂšre.

Req1.5 : Le client doit pouvoir gĂ©rer les chaĂźnes de caractĂšre tapĂ©es au clavier et les messages provenant du serveur en mĂȘme temps (Ă  l'aide de poll() cotĂ© client).

Req1.6 : Le serveur, en recevant une chaĂźne de caractĂšre depuis un client, doit rĂ©pĂ©ter cette chaĂźne uniquement Ă  ce mĂȘme client.

Req1.7 : La connexion doit se couper lorsque le client envoi '/quit'. Que ce soit du cotĂ© serveur ou du cotĂ© client, les sockets crĂ©Ă©s doivent ĂȘtre fermĂ©es et la mĂ©moire allouĂ©e aux structures de donnĂ©es doit ĂȘtre libĂ©rĂ©e.

Req1.8 : Le serveur doit stocker les informations des clients (descripteur de fichiers et adresse/port remplis par la fonction accept()) dans une liste chaßnée.

Jalon 2 - Les utilisateurs

Top

Description

L'objectif de ce jalon est de permettre au serveur de rĂ©cupĂ©rer, stocker et utiliser des informations relatives aux utilisateurs du service de messagerie instantanĂ©e. Le serveur n'est plus, comme dans le jalon 1, un serveur rĂ©pĂ©titif mais plutĂŽt l'intermĂ©diaire entre les utilisateurs pour l'envoi de messages. GrĂące Ă  cela, les utilisateurs seront capable de choisir un pseudo, de voir les pseudos des autres utilisateurs connectĂ©s, de rĂ©cupĂ©rer des informations sur ces utilisateurs, d’envoyer des messages privĂ©s Ă  un utilisateur et d’envoyer des messages Ă  tous les utilisateurs connectĂ©s. Pour faire cela, les utilisateurs devront taper des commandes avant leur message (/nick, /who, /whois , /msgall , /msg ) qui seront interprĂ©tĂ©es par le client et le serveur.

À partir de ce jalon, il vous est demandĂ© de ne plus envoyer de simple chaĂźnes de caractĂšres mais d’utiliser la structure suivante pour vos messages :

#define NICK_LEN 128
#define INFOS_LEN 128

enum msg_type { 
	NICKNAME_NEW,
	NICKNAME_LIST,
	NICKNAME_INFOS,
	ECHO_SEND,
	UNICAST_SEND, 
	BROADCAST_SEND,
	MULTICAST_CREATE,
	MULTICAST_LIST,
	MULTICAST_JOIN,
	MULTICAST_SEND,
	MULTICAST_QUIT,
	FILE_REQUEST,
	FILE_ACCEPT,
	FILE_REJECT,
	FILE_SEND,
	FILE_ACK
};

struct message {
	int pld_len;
	char nick_sender[NICK_LEN];
	enum msg_type type;
	char infos[INFOS_LEN];
};

static char* msg_type_str[] = {
	"NICKNAME_NEW",
	"NICKNAME_LIST",
	"NICKNAME_INFOS",
	"ECHO_SEND",
	"UNICAST_SEND", 
	"BROADCAST_SEND",
	"MULTICAST_CREATE",
	"MULTICAST_LIST",
	"MULTICAST_JOIN",
	"MULTICAST_SEND",
	"MULTICAST_QUIT",
	"FILE_REQUEST",
	"FILE_ACCEPT",
	"FILE_REJECT",
	"FILE_SEND",
	"FILE_ACK"
};

Cette structure est déclarée dans le fichier msg_struct.h.

La structure s’utilise de la façon suivante:

  • Le champ pld_len contient le taille des donnĂ©es utiles Ă  envoyer (gĂ©nĂ©ralement, la taille du message Ă  envoyer). S’il n’y a pas de donnĂ©es utiles (par exemple la requĂȘte en question n’a pas de donnĂ©es de message ou de fichier), pld_len doit valoir 0.
  • Le champ nick_sender contient le pseudo de l’utilisateur qui envoie le message.
  • Le champ type contient le type de message (cf la liste dans enum msg_type). Ce gens va ĂȘtre utilisĂ© pour diffĂ©rencier les actions demandĂ©es par l’utilisateur.
  • Le champ infos va contenir des informations Ă  envoyer. Ces informations dĂ©pendent du champ type.

Juste aprĂšs ce message, il est possible d’envoyer un ensemble d'octet contenant le payload (i.e. les donnĂ©es utiles) de taille pld_len.

Dans ce jalon, il faut utiliser les types : NICKNAME_NEW, NICKNAME_LIST, NICKNAME_INFOS, ECHO_SEND, UNICAST_SEND, BROADCAST_SEND.

Les champs infos doivent contenir les valeurs suivantes en fonction des cas :

  • Pour NICKNAME_NEW, le champ infos contient le nouveau pseudo. Le champ nick_sender contient une chaĂźne de caractĂšres vide si l’utilisateur n’a pas encore de pseudo, ou le pseudo actuel si l’utilisateur change de pseudo.
  • Pour NICKNAME_LIST, le champ infos contient une chaĂźne de caractĂšres vide.
  • Pour NICKNAME_INFOS, le champ infos contient le pseudo de l’utilisateur dont on veut les informations.
  • Pour ECHO_SEND, le champ infos contient une chaĂźne de caractĂšres vide.
  • Pour UNICAST_SEND, le champ infos contient le pseudo de l’utilisateur Ă  qui on veut envoyer le message.
  • Pour BROADCAST_SEND, le champ infos contient une chaĂźne de caractĂšres vide.

Exigences

Req2.0 : Les messages échangés entre le client et le serveur doivent impérativement respecter la structure donnée dans le sujet. Dans ce sujet, la seule façon de transmettre un message de taille non-déterminée à l'avance est d'utiliser 2 messages : Un premier contenant les octets de la struct message avec le champ pld_len qui indique la taille du second message, un second message de taille pld_len contenant les octets utiles.

Req2.1 : Une fois la connexion Ă©tablie avec le serveur, le client doit s'identifier par son pseudo (avec la commande /nick , type NICKNAME_NEW). Le cas particulier oĂč un utilisateur se connecte avec un pseudo trop long ou un pseudo avec des espaces et autres caractĂšres spĂ©ciaux (autre que chiffres ou lettres de l'alphabet) doivent ĂȘtre gĂ©rĂ©s.

Connecting to server ... done!
[Server] : please login with /nick <your pseudo>
/nick CoolestUserEver
[Serveur] : Welcome on the chat CoolestUserEver

Req2.2 : Un utilisateur doit recevoir une erreur si le pseudo qu’il dĂ©sire est dĂ©jĂ  attribuĂ©.

Req2.3 : Le serveur doit gĂ©rer plusieurs utilisateurs et plusieurs connexions. Les utilisateurs (pseudo) et les informations liĂ©es Ă  leur connexion (numĂ©ro de socket et structure d’adresse) doivent ĂȘtre stockĂ©s par le serveur dans une liste chaĂźnĂ©e.

Req2.4 : Le serveur doit tenir compte du changement de pseudo d’un utilisateur (rĂ©utilisation de la commande /nick ).

Req2.5 : un client doit pouvoir obtenir du serveur la liste des utilisateurs connectés. (commande /who, type NICKNAME_LIST)

/who
[Server] : Online users are
                          - User1
                          - User2
                          - CoolestUserEver

Req2.6 : un client doit pouvoir obtenir du serveur des informations sur un utilisateur en particulier (avec la commande /whois, type NICKNAME_INFOS). Les informations à donner sont, la date de connexion, l'adresse IP et le numéro de port utilisés par le client.

/whois  User1
[Server] : User1 connected since 2014/09/29@19:23 with IP address 192.168.3.165 and port number 52322

Req2.7 : Un utilisateur doit pouvoir envoyer un message Ă  tous les autres utilisateurs Ă  la fois (commande /msgall , type BROADCAST_SEND).

%terminal_user0>  /msgall Hello all
                    %terminal_user1 > [user0] : Hello all
                                    %terminal_user2 > [user0] : Hello all

Req2.8 : Un message envoyĂ© ne doit pas ĂȘtre retransmis Ă  l'expĂ©diteur.

Req2.9 : Un utilisateur doit pouvoir envoyer un message privé à un autre utilisateur (commande /msg , type UNICAST_SEND).

%terminal_user0> /msg user1 Hello
                    %terminal_user1 > [user0] : Hello

Req2.10 : Le serveur doit traiter les requetes de type UNICAST_SEND lorsque l'utilisateur spécifié n'existe pas. Le serveur doit alors renvoyer à l'émetteur une information pertinente.

%terminal_user0> /msg userKJHDQ Hello
                    %terminal_user1 > [Server] : user userKJHDQ does not exist

Req2.11 : Le serveur doit considerer sa fonction “echo” (i.e. renvoyer le message Ă  l’utilisateur) si aucune commande n’est tapĂ©e avant le message (type ECHO_SEND).

Jalon 3 - Les salons de discussion

Top

Description

Ce jalon a pour objectif la rĂ©alisation des messages entre les utilisateurs afin que votre application devienne une application de messagerie instantanĂ©e Ă  part entiĂšre. Jusqu'Ă  prĂ©sent, le serveur ne permettait que d’envoyer des messages privĂ©s et des messages en Ă  tout le monde. DorĂ©navant, vos utilisateurs pourront interagir avec des salons de discussion. Dans ce contexte, un utilisateur peut crĂ©er un salon. Les utilisateurs ont alors la possibilitĂ© de rejoindre ce salon, et une fois inscrits les utilisateurs du salon peuvent s'Ă©changer des messages entre eux. Les utilisateurs peuvent quitter le salon ou changer de salon quand ils le souhaitent.

Dans ce jalon, il faut utiliser les types : MULTICAST_CREATE, MULTICAST_LIST, MULTICAST_JOIN, MULTICAST_SEND et MULTICAST_QUIT.

Les champs infos doivent contenir les valeurs suivantes en fonction des cas :

  • Pour MULTICAST_CREATE, le champ infos contient le nom du salon Ă  crĂ©er.
  • Pour MULTICAST_LIST, le champ infos contient une chaĂźne de caractĂšres vide.
  • Pour MULTICAST_JOIN, le champ infos contient le nom du salon Ă  rejoindre.
  • Pour MULTICAST_SEND, le champ infos contient le nom du salon dans lequel on veut envoyer le message.
  • Pour MULTICAST_QUIT, le champ infos contient le nom du salon Ă  quitter.

Exigences

Req3.1 : Un utilisateur doit pouvoir crĂ©er un salon (commande /create <channel_name>, type MULTICAST_CREATE). Les cas particuliers oĂč un utilisateur dĂ©clare un salon avec des espaces ou des caractĂšres spĂ©ciaux (i.e. autre que les lettres de l'alphabet et des chiffres) doivent ĂȘtre gĂ©rĂ©s.

Req3.2 : L'utilisateur doit rejoindre automatiquement le salon qu’il vient de crĂ©er, en quittant le salon dans lequel il se trouvait le cas Ă©chĂ©ant.

Req3.3 : L'utilisateur doit pouvoir demander la liste des salons (commande /channel_list, type MULTICAST_LIST).

Req3.4 : Le serveur doit retourner un message d'erreur à l'utilisateur qui demande la création d'un salon déjà existant.

Req3.5 : L'utilisateur doit pouvoir rejoindre et quitter un salon (commandes /join et /quit, type MULTICAST_JOIN et MULTICAST_QUIT).

Req3.6 : L'utilisateur inscrit Ă  un salon doit pouvoir changer de salon, ce qui lui fait quitter le salon en cours.

Req3.7 : Le serveur doit dĂ©truire le salon lorsque son dernier occupant le quitte et doit notifier le dernier utilisateurs de la destruction du salon en mĂȘme temps.

Req3.8 : L'utilisateur envoie des messages dans le salon dans lequel il se trouve en tapant directement une chaine de caractĂšre. Un message envoyĂ© dans un salon ne doit pas ĂȘtre transmis Ă  d'autres utilisateurs que ceux prĂ©sents dans le salon (multicast, type MULTICAST_SEND).

Req3.9 : Les utilisateurs qui composent un salon doivent ĂȘtre notifiĂ© de chaque dĂ©part/arrivĂ© d'utilisateurs dans le salon dans lequel ils se trouvent.

Exemple de fonctionnement des salons :

%terminal_user0> /create channel_name
%terminal_user0> You have created channel channel_name
%terminal_user0[channel_name]> You have joined channel_name
                    %terminal_user1> /join channel_name
                    %terminal_user1[channel_name]> INFO> You have joined channel_name
%terminal_user0[channel_name]> INFO> user1 has joined channel_name
%terminal_user0[channel_name]>  I'm downtown
                    %terminal_user1[channel_name]> user0> : I'm downtown
%terminal_user0[channel_name] > /quit channel_name
					%terminal_user1[channel_name] INFO> user0 has quit channel_name
                    %terminal_user1[channel_name] > /quit channel_name
					%terminal_user0> INFO> You were the last user in this channel, channel_name has been destroyed

Jalon 4 - Les transferts de fichiers

Top

Description

L'objectif de ce jalon est de permettre à un utilisateur d'envoyer un fichier à un autre utilisateur. Plusieurs schémas sont possibles pour implémenter cette fonctionnalité. Nous retiendrons le mode P2P (pair à pair) ne nécessitant pas de passer le serveur pour échanger des données. Le serveur n'est utilisé que pour mettre en relation les utilisateurs.

Les diffĂ©rentes Ă©tapes d’un Ă©change de fichiers sont les suivantes :

  • L’émetteur envoie une demande d’envoi de fichier au serveur, en prĂ©cisant le nom du rĂ©cepteur et le nom du fichier (commande /send, type FILE_REQUEST).
  • Le serveur transmet cette demande Ă  l’utilisateur rĂ©cepteur.
  • L’utilisateur rĂ©cepteur peut accepter le tĂ©lĂ©chargement ou non.
  • La rĂ©ponse est renvoyĂ©e Ă  l’émetteur par le biais du serveur (type FILE_ACCEPT ou FILE_REJECT).
  • Si le rĂ©cepteur a refusĂ© l’envoi, l’émetteur en est informĂ© et le fichier n’est pas envoyĂ©.
  • Si le rĂ©cepteur a acceptĂ©, la rĂ©ponse doit contenir l’adresse IP et le port d’écoute du rĂ©cepteur pour la connection pair-Ă -pair.
  • L’émetteur se connecte alors directement au rĂ©cepteur, et lui envoie le fichier en direct, sans passer par le serveur (type FILE_SEND).
  • Une fois le fichier reçu, le rĂ©cepteur confirme la rĂ©ception (type FILE_ACK).

Dans ce jalon, il faut utiliser les types : FILE_REQUEST, FILE_ACCEPT, FILE_REJECT, FILE_SEND et FILE_ACK.

Les champs infos doivent contenir les valeurs suivantes en fonction des cas :

  • Pour FILE_REQUEST, le champ infos contient le pseudo de l’utilisateur Ă  qui envoyer le fichier. Le nom du fichier se trouvera dans le payload.
  • Pour FILE_ACCEPT, le champ infos contient le pseudo de l’utilisateur qui avait envoyĂ© le message FILE_REQUEST. Le payload du message contient l’adresse et le port sur lequel le rĂ©cepteur Ă©coutera la connexion de l’émetteur (de la forme “addr:port”, par exemple : “127.0.0.1:8081”).
  • Pour FILE_REJECT, le champ infos contient le pseudo de l’utilisateur qui avait envoyĂ© le message FILE_REQUEST.
  • Pour FILE_SEND, le champ infos contient le nom du fichier qui est envoyĂ©. Le payload contient les donnĂ©es du fichier lui-mĂȘme. Attention, ce fichier n’est pas obligatoire un fichier texte et peut contenir des caractĂšres ‘\0’. Il ne faut donc pas utiliser de fonctions propres Ă  la lecture des chaĂźnes de caractĂšres sur ce payload (comme strlen(), strcpy(), strcmp(), etc.).
  • Pour FILE_ACK, le champ infos contient le nom du fichier qui a Ă©tĂ© correctement reçu par le rĂ©cepteur.

Exigences

Req 4.1 : Un utilisateur (l'émetteur) doit pouvoir envoyer un fichier à un autre utilisateur (le récepteur).

Req 4.2 : Lors du transfert d'un fichier, le récepteur doit donner son approbation.

Req 4.3 : Si le rĂ©cepteur accepte l’échange de fichier, l’émetteur soit pouvoir se connecter directement au rĂ©cepteur le temps de lui envoyer le fichier.

Req 4.4 : Si le rĂ©cepteur refuse l’échange de fichier, l’émetteur doit en ĂȘtre informĂ©.

Req 4.5 : Lors du transfert d'un fichier, le récepteur et l'émetteur doivent avoir confirmation que l'envoi s'est déroulé correctement.

Exemple de fonctionnement :

%terminal_user1> /send user2 "/home/user/file.txt"
                            %terminal_user2> user1 wants you to accept the
                            transfer of the file named "file.txt". Do you
                            accept? [Y/N]
                            %terminal_user2> Y
%terminal_user1> user2 accepted file transfert.
%terminal_user1> Connecting to user2 and sending the file...
                            %terminal_user2> Receiving the file from 
                            user1...
                            %terminal_user2> file.txt saved in 
                            .re216/inbox/file.txt
%terminal_user1> user2 has received the file.

%terminal_user1> /send user2 "/home/user/correction_du_projet.txt"
                            %terminal_user2> user1 wants you to accept the 
                            transfer of the file named 
                            "correction_du_projet.txt". Do you accept? 
                            [Y/N]
                            %terminal_user2> N
%terminal_user1> user2 cancelled file transfer.

Tips and Tricks

Top

Debugger les segfault sans printf

Si votre programme crash à cause d'un problÚme mémoire ou tout autre problÚme, vous pouvez identifier la ligne exacte en utilisant gdb.

    gdb --args path/to/program/prog localhost 8080

notez le --args qui vous permet de passer des arguments Ă  votre programme.

Vous entrez alors dans l'interface de GDB.

Tapez "run" (ou "r") pour lancer l'application.

    (gdb) r
    Starting program: path/to/program/prog arg1 arg2

    Program received signal SIGSEGV, Segmentation fault.
    0x000000000040085b in main (argc=3, argv=0x7fffffffdda8) at path/to/program/prog.c:24
    24  printf("%d",*d);
    (gdb)  

de lĂ , vous pouvez voir la ligne qui pose problĂšme et mĂȘme voir la pile d'appel en tapant "backtrace" (ou "bt")

(gdb) bt
#0  0x0000000000400849 in do_ () at path/to/program/prog.c:14
#1  0x00000000004008a2 in main (argc=3, argv=0x7fffffffdda8) at path/to/program/prog.c:28
(gdb) 

Vous pouvez mĂȘme utiliser gdb pour afficher la valeur des variables.

(gdb) p ma_variable
$1 = (int *) 0x0
(gdb) 

Pour quitter gdb, faites quit

Se connecter Ă  votre serveur sans passer par le client

Votre serveur est un serveur de socket TCP. Telnet est un client TCP, vous pouvez donc l'utiliser pour vous connecter et envoyer directement des inputs au serveur. Imaginons que votre serveur écoute sur le port 8080, la commande suivante vous permet de vous connecter au serveur et taper directement des commandes. Vous recevrez également ses réponses. Pour quitter telnet, faites CTRL + ALT + ] et taper quit

telnet localhost 8080

Se faire passer pour un serveur pour votre client

la commande nc permet d'émuler un serveur écoutant sur un port donné. Par exemple, pour écouter sur le port 8080, tapez la commande suivante. Vous pourrez envoyer et recevoir des commandes de la part de votre client.

nc -l 8080

Savoir combien de socket sont ouvertes pour le serveur

Utile pour ĂȘtre sĂ»r que vous ne laissez pas trainer vos sockets

lsof -c path/to/program/serveur 2>/dev/null|grep TCP|wc -l

Tester les fuites mémoire de votre programme

Valgrind path/to/program/serveur

Rappel de C

Top

Structures

Syntaxe pour déclarer les structures :

struct module {
    int moduleId;
    double moduleGrade;
    char padding[20];
};

Syntaxe pour déclarer une variable de type structure

struct module re216;

Syntaxe pour accéder aux champs d'une structure

struct module re216;
re216.moduleId = 5
re216.moduleGrade = 12.5;

Les structures peuvent ĂȘtre manipulĂ©es avec des pointeurs aussi

struct module re216;
struct module *pre216 = &re216;
pre216->moduleId = 5;
pre216->moduleGrade = 12.5;

On peut créer des alias pour simplifier le nommage des structures

typedef struct module s_module ;

s_module re216;
re216.moduleId = 1;
re216.moduleGrade = 12.5;

Pointeurs

Les types de base : int, double, float, char

Les pointeurs correspondants : int*, double*, float*, char*

Obtenir le pointeur d'une variable déjà déclarée, utiliser &

int a;
int *pa = &a;

À l'inverse, pour obtenir la valeur pointĂ©e utiliser *

int a = 5;
int *pa = &a; 
if ((*pa) == 5){ // }

Les pointeurs fonctionnent aussi avec les structures, mais avec l'opérateur ->

struct module re216; 
re216.moduleId = 5; 
struct module *p_re216 = &re216;
re216->moduleId = 5; //utilise -> et pas le .

Passer un pointeur en paramĂštre d'une fonction

int func(int* a, int* b){
	return *a+*b;
}
...
int a = 5;
int b = 7;
int res;
res = func(&a,&b);
if (res == 7) { // }

Conversion de type

On peut convertir les types en C avec l'opérateur (.)

Ça marche pour les types de base:

int sum = 17, count = 5;
double mean;
mean = (double) sum / count;

Mais c'est surtout utile pour les pointeurs.

int main(int argc, char** argv) {

//create a pointer to a structure allocated on the heap with malloc
struct information *info = malloc(sizeof(struct module));

//clean the data
memset(info, 0, sizeof(mod));

//fill it up with some data
strcpy(info->base, "firstname"); 
strcpy(info->base+12, "lastname");

//mod->base is firstname___lastname____
printf("who am I: %s %s \n",info->base,info->base+10);

//but we could also cast the structure to the student structure, and access directly fname and lname
struct student * stu = (struct student*) info;

//stu->fname is firstname  stu->lastname is lastname
printf("who am I 2: %s %s\n", stu->fname, stu->lname);
free(mod);

}

Top