- Jalon 1 - Client-serveur TCP et serveur multi-clients
- Jalon 2 - Les utilisateurs
- Jalon 3 - Les salons de discussion
- Jalon 4 - Les transferts de fichiers
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.
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.
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
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 push
votre code.
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
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.
L'Ă©valuation de votre travail se fera en fonction des critĂšres suivants:
- La bonne soumission des jalons en temps et en heure sur votre dépot github;
- Le bon respect de l'implémentation des fonctionnalités spécifiées;
- 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);
- 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.
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.
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.
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.
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).
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.
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
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.
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.
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
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
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
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
Valgrind path/to/program/serveur
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;
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) { // }
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);
}