Professional Documents
Culture Documents
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 sin_family;
unsigned int sin_port; //avec convention Big Endian
struct in_addr sin_addr;
unsigned char s[8]; // 8 octets inutiles
};
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.
Adresse IPV4, Notation pointée et stockage des entiers...
struct in_addr{
unsigned long int s_addr;
};
s_addr identifie le réseau, les sousreseaux et la machine.
Correspondance entre numero IP x.y.z.t et s_addr donnée par:
s_addr= x+y*256+z*256^2+t*256^3.
Passage adresse IP entier > notation pointée:
Passage adresse IP notation pointée > entier:
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.
Elle renvoie 0 si le premier argument n'a pas la forme d'une IP pointée (peut servir de test pour un if... else...).
Informations sur les machines
ATTENTION: Dans le cas d'une adresse AF_INET, 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.y.z.t, alors
h_addr=ASCII(x)ASCII(y)ASCII(z)ASCII(t).
Passage de l'IP pointée à son codage ASCII:
char * IPpointee;
struct in_addr stockageIP;
char * codageASCII;
if(inet_aton(IPpointee,&stockageIP)!=0){
codageASCII=(char*)(&stockageIP.s_addr);
}
Si Ippointee est une adresse valide, on la stocke sous la forme d'un entier dans une structure in_addr et on caste
ensuite cet entier en char*.
Requêtes pour les informations de la machine
toutes les requêtes cidessous nécessitent d'inclure les mêmes fichiers que pour définir la structure hostent.
Cette requête renvoie les infos complètes de la machine appellée nom.
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)). Le parametre type correspond a type d'adressage utilisé.
Cette requête renvoie le nom de la machine. Le parametre lgmaxnom correspond à la longuer maximale premise pour
les noms (256 en général).
Appel système socket
#include <sys/types.h>
#include <sys/socket.h>
l'entier family parametre le domaine d'adressage: AF_XXX,
l'entier type donne le mode de communication:
● SOCK_STEAM: mode connecté au dessus de TCP,
● SOCK_DGRAM: mode déconnecté avec datagrammes au dessu de TCP,
● SOCK_RAW: mode utilisé au dessu de IP.
L'entier protocole donne le protocole. En général il est mis à zéro, valeur par défaut.
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).
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.
#include <sys/types.h>
#include <sys/socket.h>
Appel système connect
(coté client)
Les socket étant toujours crées en état déconnecté, après avoir créé une socket et initialisé son adresse avec les
paramètres d'un serveur, on établit la connection via la commande connect:
#include <sys/types.h>
#include <sys/socket.h>
renvoie 1 en cas d'echec de connection.
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.h>
#include <sys/socket.h>
L'entier spécifie le nombre maximal de personnes que le serveur peut mettre en attente.
Appel système accept
(coté serveur)
Lorsque le serveur est à l'écoute, et qu'il a suffisamment peu de client pour prendre un client supplémentaire, il peut
accepter une éventuellement requête de connection:
#include <sys/types.h>
#include <sys/socket.h>
enregistre l'adresse du client dans la structure client_addr et la longueur de son adresse dans
lgclient_addr. Renvoie le flux de la socket socketfd sur le flux de la socket du client.
Si les deux derniers paramètres sont inutiles au serveur, on peut les premplacer par NULL.
ATTENTION: ne pas oublier de caster les structures sockaddr_in en structure sockaddr simple dans l'utilisation
de bind, connect et accept.
Emission d'information (Mode connecté SOCK_STREAM
)
#include <sys/types.h>
#include <sys/socket.h>
Envoie dans la socket socketfd le contenu de buffer , 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>
#include <sys/socket.h>
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). Renvoie le nombre de caractères émis ou 1 si échec.
Remarque: Attention aux retours charriots que l'on peut avoir à supprimer, en particulier si on utilise telnet comme
moyen de connection coté client:
#include <sys/types.h>
pid_t pid;
pid=fork(); // En cas de réussite, les 2 processus suivent leur
cours. Renvoie pid=-1 si erreur.
if(pid==-1){
/* Gestion du message d'erreur*/
};
if(pid==0){
/* Processus fils */
exit(0);
};
else{
/*Processus père*/
};
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.
Remarque: Un pid_t est un entier simple.
Gestion des processus fils devenus zombies
Puis qu'un processus fils et son processus père peuvent a priori interagir durant leurs exécutions, même après s'être
terminé, un processus fils doit attendre que son père soit définitivement terminer pour se finir complètement. Il attend
donc sagement, façon zombie, après avoir envoyé un signal (SIGCHLD) à son père qui le prévient qu'il a fini de
mouliner. Pour traiter ce signal afin de terminer proprement le processus fils (pour récupérer la mémoire...) et d'éviter
de bloquer le reste, on fabrique une petite fonction (à mettre avant le main) qui définit la façon de se comporter à
l'entente d'un signal SIGCHLD.
#include <signal.h>
#include <wait.h>
L'appel waitpid est une fonction qui écoute les signaux envoyés par un ou plusieurs processus fils et qui détermine, à
l'entente de ce signal, quel processus a bougé:
#include <wait.h>
/*Appel simple:*/
waitpid(-1, NULL, WNOHANG);
s'il réussit, l'appel renvoie l'identifiant du processus fils pid dont l'état a changé, et en cas d'erreur 1 est renvoyé.
Lorsque pid=-1, l'appel est à l'écoute de tous les processus fils. Si *status n'est pas NULL, waitpid() stocke
l'état du fils dans la variable *status qui indique si le fils s'est terminé et comment. L'entier options détermine la
réaction du processus fils pid, l'option WNOHANG permet de pas bloquer si aucun fils ne s'est terminé.
Si WNOHANG est utilisé et aucun fils n'a changé d'état, la valeur de retour est 0.
Schéma de programme serveur
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h> /*Pour memset() !*/
#include <signal.h> /*Pour gestion des signaux*/
#include <wait.h> /*Pour gestion des signaux*/
/*Petite fonction qui traite les signaux de fins des processus fils...*/
void ZombieKiller(int sig){
while(waitpid(-1,NULL,WNOHANG)>0);
}
printf("Le programme est lancé sur la machine appellée \"%s\", dont l'IP est
%s\n\n",infosmachine->h_name,inet_ntoa(nummachine));
int scomm; // on cree une socket client pour liberer la socket principale .
pid_t pid;
struct sockaddr_in ca;
int lgca=sizeof(ca);
memset(&ca,0,lgca);
while(1){
scomm=accept(sd,(struct sockaddr *)&ca,&lgca);
//ouverture d'un "canal" client avec recup de ses infos.
/*Instructions Fils */
/* Instruction Père*/
ping x.y.t.z
pour savoir si d'un point de vue physique, la machine x.y.z.t et la sienne sont reliées...
route -n
donne la liste des routages d'IP.
ifconfig
donne la liste des interfaces réseau (et l'IP de la machine, en particulier)
netstat -antp
donne la table des processus lancés sur la machine avec les numéros de ports sur lesquels ces processus écoutent.