Notes TP Programmation Réseau

Adresses de socket #include <sys/socket.h> #include <sys/types.h> struct sockaddr{ unsigned int sa_family; char sa_data[14]; };

sa_family donne le type de l'adressage: ● AF_INUX: protocoles internes à UNIX, ● AF_INET: protocole internet, ● AF_NS: procole Xerox, ● ... d'une manière générique, toujours de la forme AF_XXX. sa_data peut contenir jusqu'à 14 octets d'inforamtions selon le protocole. Pour le type AF_INET, qui sert ici, on a 2 octets de numéro de port, 4 octets d'adresse IP (cas IPV4)ou 6 octets (cas  IPV6). Les autres seront mis a zéro. Adresses de socket internet #include <sys/socket.h> #include <sys/types.h> struct sockaddr_in{ int unsigned int struct in_addr unsigned char }; sin_family =AF_INET, sin_port contient le numero du port, avec la convention Big endian, toujours utilisée sur les réseaux, mais pas  forcement en local sur les machines...  Pour etre sûr que le port et bien dans la forme souhaiter, utliser la fonction htons()qui convertit de l'hote (h) vers  (to) le reseau (n) les int court (s) ou long (l). Toujours penser à mettre à zéro les octets qui terminent l'adresse internet, pour éviter les ennuis. En général, on remet  tout à zéro après la déclaration et avant le remplissage. Pour cela, on utilise la fonction memset().  #include <string.h> memset(&mysockaddr_in,0,sizeof(mysockaddr_in)); Cette structure d'adresse peut être castée sans problème en une structure sockaddr simple si necessaire :(struct sockaddr *)&mysockaddr_in. sin_family; sin_port; //avec convention Big Endian sin_addr; s[8]; // 8 octets inutiles

h> struct hostent{ char * h_name. Passage adresse IP notation pointée ­> entier: int inet_aton(const char* IPpointee. else. h_addr. terminée par NULL // type d'adresse: AF_XXX // taille de l'adresse. // liste des adresses si plusieurs interfaces réseaux // IP principale = h_addr_list[0] ATTENTION: Dans le cas d'une adresse AF_INET.. Notation pointée et stockage des entiers.y.t et s_addr donnée par:   s_addr= x+y*256+z*256^2+t*256^3. Informations sur les machines #include <netdb. // Pour AF_INET // nom officiel de la machine // liste des alias. Cette fonction remplit la structure spécifiée en deuxième argument avec l'entier qui correspont à l'IP en notation pointée  passée en premier argument. s_addr identifie le réseau. Passage de l'IP pointée à son codage ASCII: char * IPpointee.). struct in_addr{ unsigned long int }. char * codageASCII.&stockageIP)!=0){ codageASCII=(char*)(&stockageIP.Adresse IPV4.y. le paramètre h_addr est stocké sous la forme de 4 caractère  ASCII qui correspondent aux entiers de l'IP pointée:  si la machine a pour IP x. les sous­reseaux et la machine. selon type. struct in_addr stockageIP.z..z.  Elle renvoie 0 si le premier argument n'a pas la forme d'une IP pointée (peut servir de test pour un if..s_addr). Correspondance entre numero IP x. s_addr.t. } Si Ippointee est une adresse valide. char * }. int h_length. char ** h_addr_list.. alors  h_addr=ASCII(x)ASCII(y)ASCII(z)ASCII(t). on la stocke sous la forme d'un entier dans une structure in_addr et on caste  ensuite cet entier en char*..h> #include <sys/socket.struct in_addr *myin_addr). if(inet_aton(IPpointee. char ** h_aliases int h_addrtype. Passage adresse IP entier ­> notation pointée: char* inet_ntoa(struct in_addr myin_addr).  ..

 on établit la connection via la commande connect: #include <sys/types. struct hostent * gethostbyname(const char * nom). int lgmaxnom).h> #include <sys/socket.   Appel système    socket   #include <sys/types. Remarque: renvoie un entier que l'on appelle descripteur de fichier de la socket (même rôle que les descripteur de  fichiers in et out). L'entier protocole donne le protocole. renvoie ­1 en cas d'echec de connection. struct hostent * gethostbyaddr(const char * myaddr. Le parametre lgmaxnom correspond à la longuer maximale premise pour  les noms (256 en général). struct sockaddr* serv_addr.  gethostname(char * nom.h> #include <sys/socket. struct sockaddr* myaddr. après avoir créé une socket et initialisé son adresse avec les  paramètres d'un serveur. .int lgmyaddr). valeur par défaut. Cette requête renvoie le nom de la machine. int type.int lgserv_addr).int protocole). Le parametre type correspond a type d'adressage utilisé.     Appel système    connect    (coté client)   Les socket étant toujours crées en état déconnecté. ● SOCK_RAW: mode utilisé au dessu de IP. Cette requête renvoie les infos complètes de la machine adressée  par myaddr  où lgaddr represente la longueur de  l'adresse (lgaddr=sizeof(myaddr)). l'entier  family parametre le domaine d'adressage: AF_XXX.h> int bind(int socketfd. int lgaddr. int type). l'entier  type donne le mode de communication: ● SOCK_STEAM: mode connecté au dessus de TCP.h> int socket(int family. Cette requête renvoie les infos complètes de la machine appellée nom.h> #include <sys/socket.   Appel système    bind   Cette fonction associe le descripteur de fichier de la socket à une adresse de socket (IP+port) sur laquelle se mettre en  écoute.h> int connect(int socketfd. ● SOCK_DGRAM: mode déconnecté avec datagrammes au dessu de TCP.Requêtes pour les informations de la machine  toutes les requêtes ci­dessous nécessitent d'inclure les mêmes fichiers que pour définir la structure hostent. En général il est mis à zéro. #include <sys/types.

Si les deux derniers paramètres sont inutiles au serveur. Envoie dans la socket socketfd le contenu de buffer . struct sockaddr* client_addr. L'entier  spécifie le nombre maximal de personnes que le serveur peut mettre en attente. Remarque: Attention aux retours charriots que l'on peut avoir à supprimer. int nbbytes).h> #include <sys/socket. buffer[nblus-2]='\0'. ATTENTION: ne pas oublier de caster les structures sockaddr_in en structure sockaddr simple dans l'utilisation  de bind. char * buffer. .int * lgclient_addr). en particulier si on utilise telnet comme  moyen de connection coté client: int nblus=read(int socketfd.h> int write(int socketfd. Renvoie le flux de la socket socketfd sur le flux de la socket du client. on peut les premplacer par NULL. connect et accept. l'entier nbbytes est égal à sizeof(buffer) et renvoie  le nombre de caractères émis dans la socket ou ­1 si échec.   Reception d'information (Mode connecté    SOCK_STREAM   ) #include <sys/types.h> int accept(int socketfd.h> #include <sys/socket.  Appel système    listen    (coté serveur)   Après avoir crée une socket et initialisé son adresse. on doit mettre le serveur en écoute pour que d'éventuels clients se  connectent: #include <sys/types.  enregistre l'adresse du client dans la structure  client_addr  et  la longueur de son adresse dans  lgclient_addr.     Appel système    accept   (coté serveur)       Lorsque le serveur est à l'écoute.h> int read(int socketfd. et qu'il a suffisamment peu de client pour prendre un client supplémentaire.h> #include <sys/socket. il peut  accepter une éventuellement requête de connection: #include <sys/types. char * buffer. On lit ce qui arrive de la socket socketfd et on le met en mémoire dans buffer en général de longueur maximale  nbbytes (256).h> int listen(int socketfd. char * buffer.  Renvoie le nombre de caractères émis ou ­1 si échec.   Emission d'information (Mode connecté    SOCK_STREAM   ) #include <sys/types. int nbbytes). int nbbytes).h> #include <sys/socket. int maxlog).

}.WNOHANG)>1).h> pid_t pid. int options). //avant d'utiliser le fork().  Remarque: Un pid_t est un entier simple. } L'appel waitpid est une fonction qui écoute les signaux envoyés par un ou plusieurs processus fils et qui détermine.  Cela permettra de mettre dans le processus fils la gestion d'un client pendant que le processus pere retourne écouter le  réseau  pour d'éventuelles autres connections. Il attend  donc sagement.... if(pid==0){ /* Processus fils */ exit(0). qui vaut 0 pour le  processus fils..ZombieKiller). Renvoie pid=-1 si erreur... On fait donc une fourchette fork(). else{ /*Processus père*/ }. à  l'entente de ce signal. Determine comment traiter le signal .h> void ZombieKiller(int sig){ while(waitpid(-1. pid=fork().  . Rodriguez!   Pour pouvoir connecter simultanément des clients.. #include <signal. après avoir envoyé un signal (SIGCHLD) à son père qui le prévient qu'il a fini de  mouliner. (cf dessous).... Voici la structure d'une dérivation père­fils: #include <sys/types. utilisent le  même programme pas n'ont pas le même numéro d'identité processus pid.h> pid_t waitpid(pid_t pid. Selon la valeur de  pid. char * argv[]){ . /*Appel simple:*/ waitpid(-1. le programme peut avoir des effets différents.  Mise en parallèle de processus:    pid.  s'il réussit..  Pour traiter ce signal afin de terminer proprement le processus fils (pour récupérer la mémoire. façon zombie. même après s'être  terminé. on doit pouvoir dédoubler les processus mis en oeuvre pour 1 client  dans le programme serveur.h> #include <wait. et en cas d'erreur ­1 est renvoyé. le père et le fils.. } int main(int argc. Les deux processus peuvent interagir. NULL. int * status. if(pid==-1){ /* Gestion du message d'erreur*/ }. les 2 processus suivent leur cours. processus père et fils. l'appel renvoie l'identifiant du processus fils pid dont l'état a changé.  Gestion des processus fils devenus zombies Puis qu'un processus fils et son processus père peuvent a priori interagir durant leurs exécutions.. un processus fils doit attendre que son père soit définitivement terminer pour se finir complètement. signal(SIGCHLD. quel processus a bougé: #include <wait. Cela peut  eventuellement poser des problème de programme zombie. fork   . on fabrique une petite fonction (à mettre avant le main) qui définit la façon de se comporter à  l'entente d'un signal SIGCHLD. // En cas de réussite.) et d'éviter  de bloquer le reste. les deux processus.NULL.. WNOHANG).

struct in_addr nummachine. . memset(&sa. Schéma de programme serveur /* Ossature d'un programme créant un serveur en écoute constante.sin_addr=nummachine.*/ void ZombieKiller(int sig){ while(waitpid(-1. dont l'IP est %s\n\n". //Récupère le nom de la machine infosmachine = gethostbyname(nom).h> /*Pour gestion des signaux*/ /* Taille maximale des noms:*/ #define MAXNAME 256 /*Petite fonction qui traite les signaux de fins des processus fils.*/ /* traitement des clients en simultané. sa.WNOHANG)>0).h> <string. struct hostent *infosmachine.sizeof(sa)). /*Déclaration de l'adresse de la socket*/ struct sockaddr_in sa. L'entier options détermine la  réaction du processus fils pid.h> /*Pour gestion des signaux*/ <wait.NULL.s_addr=*temp.h> /*Pour memset() !*/ <signal. unsigned long int *temp.h> <netinet/in.. la valeur de retour est 0. gethostname(nom.sin_family=AF_INET.inet_ntoa(nummachine)). /*Determination du nom et IP de la machine ou le programme est lancé*/ char nom[MAXNAME]. char * argv[]){ /*Récupération du numéro de port pris en argument*/ int numport=atoi(argv[1]). avec gestion des zombies*/ /* Parametre à entrer: numéro du port d'écoute*/ #include #include #include #include #include #include #include #include #include #include <stdio.sin_port=htons(numport). //pour mettre dans le bon sens.h> <sys/socket. printf("Le programme est lancé sur la machine appellée \"%s\".infosmachine->h_name..h> <unistd..SOCK_STREAM.0).. } int main(int argc.MAXNAME). //Récupère les infos de la machine /*Ecriture des propriétés de la machine: Nom+IP*/ temp=(unsigned long int *)infosmachine->h_addr. sa.h> <stdlib.  waitpid() stocke  l'état du fils dans la variable  *status qui indique si le fils s'est terminé et comment.h> <netdb. l'option WNOHANG permet de pas bloquer si aucun fils ne s'est terminé. sa.  Si *status n'est pas NULL.  l'appel est à l'écoute de tous les processus fils. nummachine.h> <arpa/inet.0. /*Déclaration d'une socket*/ int sd=socket(AF_INET.Lorsque pid=-1. Si WNOHANG est utilisé et aucun fils n'a changé d'état.

.ZombieKiller)..sizeof(sa)). while(1){ scomm=accept(sd. // on cree une socket client pour liberer la socket principale . (struct sockaddr *)&sa. struct sockaddr_in ca. /*Nombre de client max en file d'attente pour la socket*/ listen(sd. memset(&ca. } } close(sd). //ouverture d'un "canal" client avec recup de ses infos. exit(0). } if(pid==0){ //PROCESSUS FILS /*Instructions Fils close(scomm).lgca). pid=fork(). //disjoinction Père-Fils if(pid==-1){ //ERREUR DE DISJONCTION fprintf(stdout. } else{ */ // ferme la socket de communication //termine le processus //PROCESSUS PERE /* Instruction Père*/ close(scomm).&lgca).*/ signal(SIGCHLD. /*Et c'est parti pour affronter les clients."Erreur dans la disjonction PERE/FILS").. int lgca=sizeof(ca)./*Assignation de l'adresse à la socket*/ bind(sd. pid_t pid. } // le processus père retourne en attente //ferme la socket principale . //Gestion des signaux de fins de processus.(struct sockaddr *)&ca.10). int scomm.0.

.. L'option -n laisse les adresse et  les ports en chiffres. ping x.y.t et la sienne  sont reliées.. la machine x. on peut enlever simplement  host nommachine  pour ecouter tous les échanges. pour écouter tout ce qui vient d'un domaine particulier. mais aussi le  remplacer par: ● net nomdomaine.y. netstat -antp donne la table des processus lancés sur la machine avec les numéros de ports sur lesquels ces processus écoutent.t.. ifconfig donne la liste des interfaces réseau (et l'IP de la machine. Au lieu d'écouter les échanges entre sa machine et  une autre précisée.ANNEXE: Petites commandes aidant au diagnostique d'une erreur. en particulier) sudo /etc/init.. pour ecouter ce qui transite par les ports compris entre  numportmin et numportMAX. ● port numport. pour écouter ce qui transite par le port  numport. .z pour savoir si d'un point de vue physique. -i any ecoute sur toutes les interfaces réseau. Sudo tcpdump -i any -n host nommachine donne en temps reel les echanges entre notre machine et la machine nommachine..z. route -n donne la liste des routages d'IP.d/networking restart  relance le réseau. ● portrange numportmin-numportMAX.

Master your semester with Scribd & The New York Times

Special offer for students: Only $4.99/month.

Master your semester with Scribd & The New York Times

Cancel anytime.