0% ont trouvé ce document utile (0 vote)
123 vues77 pages

Cours 2 Sockets

Ce document introduit les sockets TCP et UDP et leur mise en œuvre en C. Il présente les sockets, l'adressage réseau, les structures de données C pour la manipulation des adresses et identifiants réseau, et l'accès aux identifiants des machines locales et distantes.

Transféré par

MOHAMED SOLTANI
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
123 vues77 pages

Cours 2 Sockets

Ce document introduit les sockets TCP et UDP et leur mise en œuvre en C. Il présente les sockets, l'adressage réseau, les structures de données C pour la manipulation des adresses et identifiants réseau, et l'accès aux identifiants des machines locales et distantes.

Transféré par

MOHAMED SOLTANI
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Introduction aux

Systèmes distribués

Sockets TCP/UDP et leur


mise en œuvre en C

Eric Cariou

Université de Pau et des Pays de l'Adour


Département Informatique

[Link]@[Link]
1
Plan
1. Présentation générale des sockets
2. Sockets UDP
3. Sockets TCP
4. Multicast IP

2
Rappel sur les réseaux
 TCP ou UDP
 Communication entre systèmes aux extrémités
 Pas de visibilité des systèmes intermédiaires

Application Application
Communication d’extrémité
à extrémité
TCP/UDP TCP/UDP
IP IP IP
Liaison Liaison Liaison
Physique Physique Physique
3
Adressage
 Adressage pour communication entre applications
 Adresse « réseau » application = couple de 2 informations
 Adresse IP : identifiant de la machine sur laquelle tourne l'appli
 Numéro de port : identifiant local réseau de l'application
 Couche réseau : adresse IP
 Ex : [Link]
 Couche transport : numéro de port TCP ou UDP
 Ce numéro est en entier d'une valeur quelconque
 Ports < 1024 : réservés pour les applications ou protocoles systèmes
 Exemple : 80 = HTTP, 21 = FTP, ...
 Sur un port : réception ou envoi de données
 Adresse notée : @IP:port ou nomMachine:port
 [Link]:80 : accès au serveur Web tournant sur la machine
d'adresse IP [Link] 4
Sockets
 Socket : prise
 Associée, liée à un port
 C'est donc un point d'accès aux couches réseaux
 Services d'émission et de réception de données sur la
socket via le port
 En mode connecté (TCP)
 Connexion = tuyau entre 2 applications distantes
 Une socket est un des deux bouts du tuyau
 Chaque application a une socket locale pour gérer la
communication à distance
 Une socket peut-être liée
 Sur un port précis à la demande du programme
 Sur un port quelconque libre déterminé par le système
5
Sockets

 Une socket est


 Un point d'accès aux couches réseau TCP/UDP
 Liée localement à un port
 Adressage de l'application sur le réseau : son couple @IP:port
 Elle permet la communication avec un port distant sur une
6
machine distante : c'est-à-dire avec une application distante
Client/serveur avec sockets
 Il y a toujours différenciation entre une partie client
et une partie serveur
 Deux rôles distincts au niveau de la communication via
TCP/UDP
 Mais possibilité que les éléments communiquant jouent
un autre rôle ou les 2 en même temps
 Différenciation pour plusieurs raisons
 Identification : on doit connaître précisément la localisation
d'un des 2 éléments communiquants
 Le coté serveur communique via une socket liée à un port
précis : port d'écoute
 Dissymétrie de la communication/connexion
 Le client initie la connexion ou la communication
7
Sockets UDP

8
Sockets UDP : principe
 Mode datagramme
 Envois de paquets de données (datagrammes)
 Pas de connexion entre parties client et serveur
 Pas de fiabilité ou de gestion de la communication
 Un paquet peut ne pas arrivé (perdu par le réseau)
 Un paquet P2 envoyé après un paquet P1 peut arriver avant
ce paquet P1 (selon la gestion des routes dans le réseau)
 Principe de communication
 La partie serveur crée une socket et la lie à un port
UDP particulier
 La partie client crée une socket pour accéder à la
couche UDP et la lie sur un port quelconque
9
Sockets UDP : principe
 Principe de communication (suite)
 Le serveur se met en attente de réception de paquet sur
sa socket
 Le client envoie un paquet via sa socket en précisant
l'adresse du destinataire
 Couple @IP/port
 Destinataire = partie serveur
 @IP de la machine sur laquelle tourne la partie serveur et numéro
de port sur lequel est liée la socket de la partie serveur
 Il est reçu par le serveur (sauf pb réseau)
 Si le client envoie un paquet avant que le serveur ne soit
prêt à recevoir : le paquet est perdu
 Emission non bloquante
 Réception bloquante 10
Structures de données C
 Ensemble de structures de données pour
manipulation des adresses des machines, des
identifications réseau ...
 Adresse IP (v4) d'une machine
 Fichier <netinet/in.h>
 typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
 Entier sur 32 bits : 4 octets
 4 octets d'une adresse IP, codés en hexa
 Ex. : [Link] correspond à l'entier 0xC0A80C28
11
Conversions adresse IP
 Fichier <arpa/inet.h>
 Codage in_addr vers chaine de type ''X.X.X.X''
 char *inet_ntoa(struct in_addr adresse)
 Renvoie une chaîne statique
 Codage chaîne ''X.X.X.X'' vers in_addr ou long
 int inet_aton(const char *chaine,
struct in_addr *adresse)
unsigned long inet_addr(char *chaine)
 inet_aton pas disponible sur tous systèmes
 inet_addr fonction standard
 Retourne valeur INADDR_NONE en cas d'erreur
 Attention : INADDR_NONE correspond aussi à une adresse de
broadcast ([Link]) 12
Structures de données C
 Identifiants d'une machine
 Fichier <netdb.h>
 struct hostent {
char *h_name; nom officiel,
char **h_aliases; liste des alias,
int h_addrtype; type d'adresse,
int h_length; longueur de l'adresse,
char **h_addr_list; liste des adresses
#define h_addr h_addr_list[0] première
adresse
};
 Type d'adresse : internet (IP v4) par défaut
 Valeur = AF_INET, longueur = 4 (en octets)
 Une machine peut avoir plusieurs adresses IP et noms 13
Accès identifiants machines
 Accès aux identifiants locaux d'une machine
 Définition dans fichier <unistd.h>
 int gethostname(char *nom, size_t lg)
 Récupére le (premier) nom de la machine locale, placé dans nom
 lg est la taille de l'espace mémoire référencé par nom
 Retourne 0 si appel réussi, -1 sinon
 long gethostid()
 Retourne l'adresse IP de la machine locale sous forme d'un
entier sur 4 octets

14
Accès identifiants machines
 Accès aux identifiants de machines distantes
 Définitions dans fichier <netdb.h>
 struct hostent *gethostbyname(char *nom)
 Retourne l'identifiant de la machine dont le nom est passé en
paramètre ou NULL si aucune machine de ce nom trouvée
 Recherche des infos dans l'ordre suivant
1. Serveur de nom DNS
2. Serveur NIS
3. Fichier local /etc/hosts
 Note
 La structure retournée est placée à une adresse statique en mémoire
 Un nouvel appel de gethostbyname écrase la valeur à cette adresse
 Nécessité de copier la structure avec un memcopy ou un bcopy
si on veut la conserver
15
Accès identifiants machines
 Accès identifiants machines distantes (suite)
 struct hostent gethostbyaddr(
char *adresse, zone mémoire contenant
l'adresse de la machine,
int longueur, longeur de l'adresse,
int type) type de l'adresse
 Retourne les identifiants d'une machine à partir de son adresse
 Retourne NULL si machine non trouvée
 Si adresse IP
 longueur = 4
 type = AF_INET
 adresse pointe vers une entier sur 4 octets codant l'adresse IP
 Note
 Là aussi, retourne une référence sur une zone mémoire statique
16
écrasée à chaque nouvel appel
Identifiants d'une socket
 Identifiants d'une socket
 Couple adresse IP/numéro de port dans le contexte IP
 Identifiant « général »
 struct sockaddr
 On ne l'utilise jamais directement mais une spécialisation selon
le type de réseau ou de communication utilisé
 Exemples de spécialisation
 Fichier <netinet/in.h>
 struct sockaddr_in
 Contexte IP
 Fichier <sys/un.h>
 struct sockaddr_un
 Contexte Unix (communication locale à la même machine via des
sockets) 17
Identifiants d'une socket
 Identifiants socket TCP/UDP – IP
 struct sockaddr_in {
short sin_familly; = AF_INET,
u_short sin_port; port associée à la socket,
struct in_addr sin_adr; adresse de la machine,
char sin_zero[8]; champ de 0 pour compléter
};
 Opérations sur les sockets
 Fichier <sys/socket.h>
 Création de sockets
 Liaison sur un port local
 Envoi/réception de données
 ...
18
Création et gestion de sockets
 Création d'une socket
 int socket(int domaine, int type, int protocole)
 Retourne un descripteur correspondant à la socket créée
 Similaire à un descripteur de fichier
 Paramètres
 Domaine : utilisation de constantes parmi
 AF_UNIX : domaine local Unix
 AF_INET : domaine IP
 Type : type de la socket selon protocoles sous-jacents, constantes
parmi entre autres
 SOCK_DGRAM : socket mode datagramme, non connecté et non fiable
(UDP par défaut)
 SOCK_STREAM : socket mode connecté et fiable (TCP par défaut)
 SOCK_RAM : socket bas niveau (IP ou ICMP)
 Protocole
 On utilise généralement la valeur 0 qui sélectionne le protocole par défaut
associé à chaque type de socket 19
Création et gestion de sockets
 Création d'une socket (suite)
 En cas d'erreur lors de la création : retourne -1
 Pour connaître le détail de l'erreur
 Consulter la valeur de la variable errno ou afficher un
message avec la fonction perror
 Liste des erreurs dans le fichier <ernno.h>
 Si la socket n'est pas liée à un port donné via un bind
 Lors du premier envoi d'un paquet, elle sera liée localement à
un port quelconque disponible
 Fermeture d'une socket
 int close(int socket_desc)
 Ferme la socket dont le descripteur est passé en paramètre
20
Création et gestion de sockets
 Liaison d'une socket sur un port particulier
 int bind(int sock_desc, descripteur de la socket,
struct sockaddr *adresse, adresse sur laquelle
lier la socket,
socklen_t longueur_adresse) taille de l'adresse (int)
 Retourne -1 en cas de problème
 Problèmes les plus courants
 Le port choisi a déjà une socket liée dessus (erreur EADDRINUSE)
 Liaison non autorisée (ex : port < 1024) sur ce port (erreur EACCES)
 Déclaration typique d'une adresse
 De type sockaddr_in pour IP, avec champs positionnés comme suit
 sin_family = AF_INET
 sin_addr.s_addr = INADDR_ANY (ou une adresse IP distante)
 INADDR_ANY : permet d'utiliser n'importe quelle IP de la machine si elle
en a plusieurs et/ou évite de connaître l'adresse IP locale
 sin_port = port sur lequel on veut lier la socket
 Si 0 : n'importe quel port 21
Création et gestion de sockets
 Récupérer les informations sur une socket
 int getsockname(
int descripteur, descripteur de la socket,
struct sockaddr *adresse, contiendra l'adresse
mémoire de l'adresse réseau
int *longueur_adresse)
 Paramètre longueur_adresse
 A l'appel : taille de l'espace réservé pour contenir l'adresse
 Au retour : longueur effective de l'adresse

22
Représentations des nombres
 Selon le système/matériel, le codage binaire des nombres
peut changer
 Big Endian : bit de poid forts à gauche
 Little Endian : bit de poid forts à droite
 Pour éviter une mauvaise interprétation des nombres
 On les code en « mode réseau » lorsqu'on les utilise dans les
structures ou fonctions d'accès au réseau et sockets
 4 opérations assurent la traduction « mode réseau / local »
 u_short htons(u_short) : entier court local vers réseau
 u_long htonl(u_long) : entier long local vers réseau
 u_short ntohs(u_short) : entier court réseau vers local
 u_long ntohl(u_long) : entier long réseau vers local
 Entiers courts : numéros de port
 Entiers longs : adresses IP 23
Envoi/réception données en UDP
 Envoi de données sur socket
 int sendto(
int descripteur, descripteur de la socket qui
émettra les données,
void *message, pointeur vers la zone de
données à émettre,
int longueur, longueur des données,
int option, 0,
struct sockaddr *adresse, adresse de la socket
destinatrice,
int longueur_adresse) longueur de l'adresse
 2 types d'informations à passer en paramètre
 Lien vers les données à émettre
 L'adresse identifiant le destinataire (couple @IP/port)
 Retourne
 Nombre de caractères (réellement) envoyés
24
 -1 en cas de problème (détails avec perror ou errno)
Envoi/réception données en UDP
 Réception de données
 int recvfrom(
int descripteur, descripteur de la socket qui
attend les données,
void *message, pointeur vers la zone de
données,
int longueur, taille max de la zone données,
int option, 0 ou MSG_PEEK,
struct sockaddr *adresse, adresse de la socket
émettrice,
int *longueur_adresse) longueur de l'adresse
 Paramètres à fournir
 Lien et taille de la zone de données qui contiendra les données
reçues
 longueur_adresse : longueur de l'adresse, à initialiser !
25
Envoi/réception données en UDP
 Réception de données (suite)
 recvfrom retourne
 Le nombre de caractères reçus
 -1 en cas de problème
 En retour via les pointeurs
 Zone de données initialisée avec les données
 Adresse : contient l'adresse de la socket émettrice
(longueur_adresse contient la taille de cette adresse)
 Paramètre option = MSG_PEEK
 Le paquet de données reçue n'est pas retiré du tampon
 A la prochaine réception de données, on lira les mêmes
données
 Réception de données est bloquante par défaut
26
Envoi/réception données en UDP
 Notes sur les tailles des données envoyées/reçues
 A priori pas limite en taille pour les données circulant dans
les paquets UDP, mais
 Pour tenir dans un seul datagramme IP, le datagramme UDP ne
doit pas contenir plus de 65467 octets de données
 Un datagramme UDP est rarement envoyé via plusieurs
datagrammes IP
 Mais en pratique : il est conseillé de ne pas dépasser 8176 octets
 Car la plupart des systèmes limitent à 8 Ko la taille des datagrammes
UDP
 Pour être certain de ne pas perdre de données : 512 octets max
 Si datagramme UDP trop grand : les données sont tronquées
 Si la taille de la zone de données en réception est plus petit
que les données envoyées
 Les données reçues sont généralement tronquées 27
Exemple de programme UDP
 Client / serveur basique en UDP
 Client envoie une chaîne « bonjour » au serveur et
attend une réponse
 Serveur se met en attente de réception de données et
renvoie la chaîne « bien recu » à l'émetteur
 Identification du serveur
 Lancé sur la machine scinfe122
 Ecoute sur le port 4000

28
Exemple UDP : coté client
 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

#define TAILLEBUF 20

int main() {

// identifiant de la machine serveur


struct hostent *serveur_host;
// adresse de la socket coté serveur
static struct sockaddr_in addr_serveur;
// taille de l'addresse socket
socklen_t lg; 29
Exemple UDP : coté client (suite)
 // descripteur de la socket locale
int sock;
// chaine à envoyer
char *message = "bonjour";
// buffer de réception
char buffer[TAILLEBUF];
// chaine reçue en réponse
char *reponse;
// nombre d'octets lus ou envoyés
int nb_octets;

// création d'une socket UDP


sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
perror("erreur création socket");
exit(1);
}
30
Exemple UDP : coté client (suite)
 // récupération identifiant du serveur
serveur_host = gethostbyname("sceinfe122");
if (serveur_host==NULL) {
perror("erreur adresse serveur");
exit(1);
}

// création adresse socket destinatrice


bzero(&addr_serveur, sizeof(struct sockaddr_in));
addr_serveur.sin_family = AF_INET;
addr_serveur.sin_port = htons(4000);
memcpy(&addr_serveur.sin_addr.s_addr,
serveur_host -> h_addr, serveur_host -> h_length);

31
Exemple UDP : coté client (fin)
 // on envoie le message "bonjour" au serveur
lg = sizeof(struct sockaddr_in);
nb_octets = sendto(sock, msg, strlen(msg)+1, 0,
(struct sockaddr*)&addr_serveur, lg);
if (nb_octets == -1) {
perror("erreur envoi message");
exit(1); }
printf("paquet envoyé, nb_octets = %d\n",nb_octets);

// on attend la réponse du serveur


nb_octets = recvfrom(sock, buffer, TAILLEBUF, 0,
(struct sockaddr*)&addr_serveur, &lg);
if (nb_octets == -1) {
perror("erreur réponse serveur");
exit(1); }
reponse = (char *)malloc(nb_octets * sizeof(char));
memcpy(reponse, buffer, nb_octets);
printf("reponse recue du serveur : %s\n",reponse);
}

// on ferme la socket 32
close(sock);
Exemple UDP : coté serveur
 // adresse de la socket locale
static struct sockaddr_in addr_local;
// adresse de la socket coté serveur
static struct sockaddr_in addr_client;
// identifiant du client
struct hostent *host_client;
// taille de l'addresse socket
socklen_t lg;
// descripteur de la socket locale
int sock;
// chaine à envoyer en réponse
char *reponse = "bien recu";
// buffer de réception
char buffer[TAILLEBUF];
// chaine reçue
char *chaine;
// nombre d'octets lus ou envoyés
int nb_octets; 33
Exemple UDP : coté serveur (suite)
 // création de la socket
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
perror("erreur création socket");
exit(1);
}

// liaison de la socket sur le port local 4000


bzero(&addr_local, sizeof(struct sockaddr_in));
addr_local.sin_family = AF_INET;
addr_local.sin_port = htons(4000);
addr_local.sin_addr.s_addr=htonl(INADDR_ANY);

if( bind(sock, (struct sockaddr*)&addr_local,


sizeof(addr_local))== -1 ) {
perror("erreur bind");
exit(1); 34
}
Exemple UDP : coté serveur (suite)
 // attente de données venant d'un client
lg = sizeof(struct sockaddr_in);
nb_octets = recvfrom(sock, buffer, TAILLEBUF, 0,
(struct sockaddr *)&addr_client, &lg);
if (nb_octets == -1) {
perror("erreur réception paquet");
exit(1);
}

// récupère nom de la machine émettrice des données


host_client = gethostbyaddr(&(addr_client.sin_addr),
sizeof(long), AF_INET);
if (host_client == NULL) {
perror("erreur gethostbyaddr");
exit(1);
}
35
Exemple UDP : coté serveur (fin)
 // affichage message reçu et coordonnées émetteur
chaine = (char *)malloc(nb_octets * sizeof(char));
memcpy(chaine, buffer, nb_octets);
printf("recu message %s de la part de %s
sur le port %d\n", chaine, host_client->h_name,
ntohs(addr_client.sin_port));

// envoi de la réponse à l'émetteur


nb_octets = sendto(sock, reponse, strlen(reponse)+1,
0,(struct sockaddr*)&addr_client, lg);
if (nb_octets == -1) {
perror("erreur envoi réponse");
exit(1);
}

// fermeture la socket
close(sock);
36
Notes sur ces exemples UDP
 Variables de type sockaddr_in
 A déclarer en static
 Fonctions de copies zones mémoire
 memcpy(void *dest, void *source, size_t lg);
memmove(void *dest, void *source, size_t lg);
 Copie lg octets d'une zone mémoire source vers une zone
mémoire destination
 memmove : à utiliser pour des copies de zones mémoires se
recouvrant partiellement
 Initialisation de zone mémoire
 memset(void *zone_mem, int valeur, size_t lg)
 Initialise chacun des lg premiers octets de la zone avec valeur
 bzero(void *zone_mem, size_t lg)
 Initialise les lg premiers octets de la zone à 0 37
Sockets TCP

38
Sockets TCP : principe
 Fonctionnement en mode connecté
 Phase de connexion explicite entre client et serveur avant comm.
 Données envoyées dans un « tuyau » et non pas par paquet
 Flux (virtuels) de données
 Fiable : la couche TCP assure que
 Les données envoyées sont toutes reçues par la machine destinataire
 Les données sont reçues dans l'ordre où elles ont été envoyées

39
Sockets TCP : principe
 Principe de communication
 Le serveur lie une socket d'écoute sur un certain port bien
précis et appelle un service d'attente de connexion de la
part d'un client
 Le client appelle un service pour ouvrir une connexion
avec le serveur
 Il récupère une socket (associée à un port quelconque par le
système)
 Du coté du serveur, le service d'attente de connexion retourne
une socket de service (associée à un port quelconque)
 C'est la socket qui permet de dialoguer avec ce client
 Comme avec sockets UDP : le client et le serveur
communiquent en envoyant et recevant des données via leur
socket
40
Socket TCP : mise en oeuvre en C
 Utilise les mêmes bases que pour UDP
 Codage différents identifiants
 Structures sockaddr_in, hostent, in_addr et fonctions associées
 Gestion des sockets : socket() et descripteur de fichier
 Besoin supplémentaire par rapport à UDP
 Fonctions pour attendre des connexions côté serveur
 Fonction pour se connecter à la socket d'écoute du serveur coté
client
 Communication entre client/serveur après phase de
connexion
 Pas besoin de préciser identifiants du coté client à chaque envoi de
données car on fonctionne en mode connecté
 Pas d'usage de recvfrom() et sendto()
 Utilise alors les fonctions système read() et write() 41
Sockets TCP : attente connexion
 Coté serveur
 Fonction qui attend la connexion d'un client sur une
socket d'écoute
 Fichier <sys/socket.h>
 int accept(int socket_ecoute,
struct sockaddr *addr_client,
int *lg_addr);
 Paramètres
 socket_ecoute : la socket d'écoute (à lier sur un port précis)
 addr_client : contiendra l'addresse du client qui se connectera
 lg_addr : contiendra la taille de la structure d'addresse
 A initialiser avant d'appeler la fonction comme pour recvfrom
 Retourne un descripteur de socket
 La socket de service qui permet de communiquer avec le client qui
vient de se connecter
 Retourne -1 en cas d'erreur
42
 Fonction bloquante jusqu'à l'arrivée d'une demande de connexion
Sockets TCP : init. connexion
 Coté serveur
 Fonction pour configurer le nombre maximum de
connexions pendantes
 Nombre de connexions en attente à un instant donné que la
partie serveur exécute un accept
 int listen(int descripteur,
int nb_con_pendantes);
 Paramètres
 descripteur : descripteur de la socket d'écoute à configurer
 nb_con_pendantes : nombre max de connexions pendantes autorisées
 Retourne 0 si exécution correcte, -1 en cas de problème
 Le nombre de connexion pendantes précisé doit être inférieur à
la valeur de la constante SOMAXCONN (fichier <sys/socket.h>)
 Cette valeur dépend des systèmes d'exploitation
 SOMAXCONN = 128 sous Linux
43
 Fonction à appeler avant de faire les accept
Sockets TCP : ouverture connexion
 Coté client
 Fonction pour ouvrir une connexion avec la partie serveur
 int connect(int descripteur,
struct sockaddr *addr_serveur,
int lg_addr);
 Paramètres
 descripteur : descripteur de la socket coté client
 addr_serveur : identifiant de la socket d'écoute coté serveur
 lg_adresse : taille de l'adresse utilisée
 Retourne 0 si tout se passe bien, -1 sinon
 Fonction bloquante
 Si le serveur ne fait pas l'accept de son coté

44
Sockets TCP : envoi/réception données
 Si connexion établie
 « tuyau » de communication directe entre socket coté
client et socket de service coté serveur
 Pas besoin de préciser l'adresse du destinataire à chaque
envoi de données ni de vérifier l'émetteur à la réception
 Fonctions de communication
 On peut utiliser les fonctions standards pour communiquer
 write pour émission
 read pour réception
 Si on veut gérer quelques options, fonctions spécialisées
 send pour émission
 revc pour réception 45
Sockets TCP : émission
 2 fonctions pour l'émission de données
 ssize_t write(int descripteur,
void *ptr_mem,
size_t longueur);
 ssize_t send(int descripteur,
void *ptr_mem,
size_t longueur,
int option);
 Paramètres
 descripteur : descripteur de la socket
 ptr_mem : zone mémoire où sont les données à envoyer
 longeur : nombre d'octets à envoyer
 option : 0 si envoi normal ou MSG_OOB si envoi de données
prioritaires
 Retourne le nombre d'octets écrits ou -1 en cas de pb 46
Sockets TCP : réception
 2 fonctions (bloquantes) pour réception de données
 ssize_t read(int descripteur,
void *ptr_mem,
size_t longueur);
 ssize_t recv(int descripteur,
void *ptr_mem,
size_t longueur,
int option);
 Paramètres
 descripteur : descripteur de la socket
 ptr_mem : zone mémoire où seront écrites les données reçues
 longueur : taille de la zone mémoire
 option : 3 valeurs possibles que l'on peut combiner (avec des ||)
 0 : réception normale
 MSG_OOB : réception de données prioritaires
 MSG_PEEK : lecture des données reçues mais sans les retirer du tampon
47
 Retourne le nombre d'octets reçus ou -1 en cas de pb
Sockets TCP : exemple
 Même exemple que pour UDP
 Serveur attend la connexion du client
 Client envoie une chaîne au serveur
 Serveur lui répond en lui renvoyant une chaîne
 Identification du serveur
 Lancé sur la machine scinfe122
 Ecoute sur le port 4000

48
Exemple TCP : coté client
 // identification socket d'écoute du serveur
static struct sockaddr_in addr_serveur;
// identifiants de la machine où tourne le serveur
struct hostent *host_serveur;
// socket locale coté client
int socket;
// message à envoyer au serveur
char *message = "bonjour";
// chaîne où sera écrit le message reçu
char reponse[TAILLEBUF];
// nombre d'octets envoyés/reçus
int nb_octets;

// création socket TCP


socket = socket(AF_INET, SOCK_STREAM, 0);
if (socket == -1) {
perror("creation socket");
exit(1); } 49
Exemple TCP : coté client (suite)
 // récupération identifiants de la machine serveur
host_serveur = gethostbyname("scinfe122");
if (host_serveur==NULL) {
perror("erreur récupération adresse serveur\n");
exit(1);
}

// création de l'identifiant de la socket d'écoute du serveur


bzero((char *) &addr_serveur, sizeof(addr_serveur));
addr_serveur.sin_family = AF_INET;
addr_serveur.sin_port = htons(4000);
memcpy(&addr_serveur.sin_addr.s_addr,
host_serveur->h_addr, host_serveur->h_length);

50
Exemple TCP : coté client (fin)
 // connexion de la socket client locale à la socket coté serveur
if (connect(socket,
(struct sockaddr *)&addr_serveur,
sizeof(struct sockaddr_in)) == -1) {
perror("erreur connexion serveur");
exit(1);
}

// connexion etablie, on envoie le message


nb_octets = write(socket, message,
strlen(message)+1);

// on attend la réponse du serveur


nb_octets = read(socket, reponse, TAILLEBUF);
printf(" reponse recue : %s\n", reponse);

// on ferme la socket
close(socket); 51
Exemple TCP : coté serveur
 // adresse socket coté client
static struct sockaddr_in addr_client;
// adresse socket locale
static struct sockaddr_in addr_serveur;
// longueur adresse
int lg_addr;
// socket d'écoute et de service
int socket_ecoute, socket_service;
// buffer qui contiendra le message reçu
char message[TAILLEBUF];
// chaîne reçue du client
char *chaine_recue;
// chaîne renvoyée au client
char *reponse = "bien recu";
// nombre d'octets reçus ou envoyés
int nb_octets;
52
Exemple TCP : coté serveur (suite)
 // création socket TCP d'écoute
socket = socket(AF_INET, SOCK_STREAM, 0);
if (socket == -1) {
perror("creation socket");
exit(1); }

// liaison de la socket d'écoute sur le port 4000


bzero((char *) &addr_serveur, sizeof(addr_serveur));
add_serveur.sin_family = AF_INET;
addr_serveur.sin_port = htons(4000);
addr_serveur.sin_addr.s_addr=htonl(INADDR_ANY);
if( bind(sock, (struct sockaddr*)&addr_serveur,
sizeof(addr_serveur))== -1 ) {
perror("erreur bind socket écoute");
exit(1);
}

53
Exemple TCP : coté serveur (suite)
 // configuration socket écoute : 5 connexions max en attente
if (listen(socket_ecoute, 5) == -1) {
perror("erreur listen");
exit(1);
}

// on attend la connexion du client


lg_addr = sizeof(struct sockaddr_in);
socket_service = accept(socket_ecoute,
(struct sockaddr *)&addr_client, &lg_addr);
if (socket_service == -1) {
perror("erreur accept");
exit(1);
}

54
Exemple TCP : coté serveur (fin)
 // la connexion est établie, on attend les données envoyées par le client
nb_octets = read(socket_service, message, TAILLEBUF);

// affichage du message reçu


chaine_recue =
(char *)malloc(nb_octets * sizeof(char));
memcpy(chaine_recue, message, nb_octets);
printf("recu message %s\n", chaine_recue);

// on envoie la réponse au client


write(socket_service, reponse, strlen(reponse)+1);

// on ferme les sockets


close(socket_service);
close(socket_ecoute);

55
Sockets TCP : gestion données
 En UDP : mode datagramme
 Un paquet envoyé = un paquet reçu
 Avec paquets tronqués si taille émission ou réception pas
adaptée
 En TCP : mode connecté, communication par flux
 Un bloc de données envoyé n'est pas forcément reçu en
un seul bloc d'un coup
 La couche TCP peut
 Découper un bloc émis et le délivrer en plusieurs blocs
 Plusieurs read renverront les données d'un seul write
 Concaténer plusieurs blocs émis en un seul bloc reçu
 Un read renverra les données de plusieurs write
 Toujours vérifier la taille des données reçues, 56
notamment lorsque l'on envoie des structures de données
Pseudo-connexion en UDP
 Peut utiliser la primitive connect en UDP
 Réalise une pseudo connexion
 La socket locale est configurée alors pour n'envoyer des paquets
qu'à l'adresse de la socket passée en paramètre du connect
 Peut alors utiliser les services send/write et read/recv à la
place de sendto et recvfrom
 Attention
 Pas d'établissement de vraie connexion : juste une mémorisation
de l'adresse destinataire pour simplifier émission
 Si destinataire pas prêt à recevoir les données, elles seront perdues
 N'offre donc pas non plus une communication fiable à la TCP
 Une fois connectée, une socket UDP ne peut plus émettre des
paquets vers une autre adresse que celle de la pseudo connexion
57
Pseudo-connexion en UDP
 Exemple de pseudo-connexion
 La socket locale n'est utilisée que pour envoyer des
données au port 4000 de la machine scinfe122
 ...
sock = socket(AF_INET, SOCK_DGRAM, 0);
host = gethostbyname("scinfe122");
bzero((char *) &adresse, sizeof(adresse));
adresse.sin_family = AF_INET;
adresse.sin_port = htons(4000);
memcpy(&adresse.sin_addr.s_addr, host -> h_addr,
host -> h_length);
// on fait la connexion
connect(sock, (struct sockaddr*)&adresse, lg);
// on peut utiliser alors la fonction write pour émission
write(sock, msg, lg_msg);
...
58
Informations sur la socket distante
 En TCP ou pseudo-connexion pour UDP
 Possibilité de connaitre les identifiants de la socket distante
avec qui la socket locale est connectée
 int getpeername(int sock,
(struct sockaddr *) adresse,
socklen_t lg_adresse);
 Paramètres
 sock : descripteur de la socket locale
 adresse : contiendra l'adresse de la socket distante
 lg_adresse : contiendra la taille de l'adresse
 A initialiser avant d'appeler la fonction

59
Concurrence
 Par principe, les éléments distants
communiquants sont actifs en parallèle
 Plusieurs processus concurrents
 Avec processus en pause lors d'attente de messages
 Exemple de flux d'exécution pour notre exemple
de client/serveur précédent

temps

60
Sockets TCP – gestion plusieurs clients
 Particularité coté serveur en TCP
 Une socket d'écoute sert à attendre les connexions des
clients
 A la connexion d'un client, une socket de service est
initialisée pour communiquer avec ce client
 Communication avec plusieurs clients pour le serveur
 Envoi de données à un client
 UDP : on précise l'adresse du client dans le paquet à envoyer
 TCP : utilise la socket correspondant au client
 Réception de données venant d'un client quelconque
 UDP : se met en attente d'un paquet et regarde de qui il vient
 TCP : doit se mettre en attente de données sur toutes les
sockets actives
61
Sockets TCP – gestion plusieurs clients
 Communication avec plusieurs clients (suite)
 Contrainte
 Lecture sur une socket : opération bloquante
 Tant que des données ne sont pas reçues
 Attente de connexion : opération bloquante
 Jusqu'à la prochaine connexion d'un client distant
 Avec un seul flot d'exécution (processus/thread)
 Si ne sait pas quel est l'ordonnancement des arrivées des données
des clients ou de leur connexion au serveur
 Impossible à gérer
 Donc nécessité de plusieurs processus ou threads
 Un processus en attente de connexion sur le port d'écoute
 Nouvelle connexion : un nouveau processus est créé pour gérer la
communication avec le nouveau client
62
Sockets TCP – gestion plusieurs clients
 Boucle de fonctionnement général d'un serveur
pour gérer plusieurs clients
 while(true)
socketClient = acceptConnection()
newProcessus(socketClient)
 Exemple
avec 2
clients ->

63
Sockets TCP – gestion plusieurs clients
 Création d'un nouveau processus via un fork() à chaque
connexion de client
 Le processus principal fait les accept() en boucle et créé un
nouveau processus fils pour la communication avec un nouveau
client connecté
 while(1) {
socket_service = accept(socket_ecoute,
(struct sockaddr *)&addr_client,&lg_addr);
if (fork() == 0) {
// on est dans le fils
close(socket_ecoute);
// fonction qui gère la communication avec le client
traiter_communication(socket_service);
exit(0);
}
close(socket_service);
} 64
Sockets TCP – gestion processus
 Serveur multi-processus précédent
 Les processus fils deviennent des zombis une fois terminés
 Pour supprimer ce problème, exécuter avant la boucle
 signal(SIGCHLD, SIG_IGN);
 Peut aussi associer une fonction handler à SIGCHLD
 Mais en général on ne fait rien à la terminaison du fils
 Pour que le serveur devienne un démon
 Au départ du lancement du serveur, on crée un fils qui exécute
un setsid() et on termine le processus principal
 Le code du serveur (la boucle principale) est exécutée via ce fils
...
if ( fork() != 0 ) exit(0);
setsid();
...
while(1) {
socket_service = accept(...)
... 65
}
Configuration des options des sockets
&
Broadcast, multicast UDP/IP

66
Multicast
 On a vu comment faire communiquer des
applications 1 à 1 via des sockets UDP ou TCP
 UDP offre un autre mode de communication : multicast
 Plusieurs récepteurs pour une seule émission d'un paquet
 Broadcast, multicast
 Broadcast (diffusion) : envoi de données à tous les éléments
d'un réseau
 Multicast : envoi de données à un sous-groupe de tous les
éléments d'un réseau
 Multicast IP
 Envoi d'un datagramme sur une adresse IP particulière
 Plusieurs éléments lisent à cette adresse IP
67
Multicast
 Adresse IP multicast
 Classe d'adresse IP entre [Link] et [Link]
 Classe D
 Adresses entre [Link] et [Link] sont utilisables par
un programme quelconque
 Les autres sont réservées
 Une adresse IP multicast n'identifie pas une machine sur
un réseau mais un groupe multicast
 Socket UDP multicast
 Avant envoi de paquet : on doit rejoindre un groupe
 Identifié par un couple : @IP multicast/numéro port
 Un paquet envoyé par un membre du groupe est reçu par
tous les membres de ce groupe
68
Multicast
 Utilités du multicast UDP/IP
 Evite d'avoir à créer X connexions et/ou d'envoyer X fois
la même donnée à X machines différentes
 En pratique
 Utilisé pour diffuser des informations
 Diffusion de flux vidéos à plusieurs récepteurs
 Chaine de télévision, diffusion d'une conférence
 Le même flux est envoyé à tous au même moment
 Pour récupérer des informations sur le réseau
 [Link] : pour localiser un serveur DHCP
 Limites
 Non fiable et non connecté comme UDP

69
Configuration des sockets
 Pour utiliser le multicast en C
 Doit passer par la configuration des sockets
 Configuration socket/couche IP
 Accès aux options d'une socket
 Lire l'état d'une option : getsockopt
 int getsockopt(int sock, int niveau, int option,
void *valeur, socklen_t *longueur)
 Modifier l'état d'une option : setsockopt
 int setsockopt(int sock, int niveau, int option,
void *valeur, socklen_t longeur)
 Ces fonctions retournent -1 si problème

70
Configuration des sockets
 Paramètres de [get/set]sockopt
 sock : la socket que l'on veut gérer
 niveau : le niveau du protocole choisi, valeurs entre autres parmi
 SOL_SOCKET : la socket elle même
 IPPROTO_IP : la couche IP
 IPPROTO_TCP : la couche TCP
 IPPROTO_UDP : la couche UDP
 option : option choisie
 Notamment pour le niveau SOL_SOCKET
 SO_BROADCAST : autorisation de diffusion des paquets
 SO_RVCBUF/SO_SNDBUF : taille des buffers de réception et d'émission
 SO_REUSEADDR : autorise de lier plusieurs sockets au même port
 SO_TYPE (avec get) : retourne le type de la socket (SOCK_DGRAM ...)
 Notamment pour le niveau IP_PROTO
 IP_[ADD/DROP]_MEMBERSHIP : inscription ou désincription d'un groupe
multicast
 IP_MULTICAST_LOOP : paquet diffusé est reçu ou pas à son émetteur
71
 IP_MULTICAST_TTL : TTL d'un paquet envoyé en multicast
Configuration des sockets
 Paramètres de [get/set]sockopt (suite)
 valeur : données décrivant l'état de l'option
 En général, on utilise un entier ayant pour valeur 0 ou 1
 0 : l'option n'est pas positionnée
 1 : l'option est positionnée

 longueur : longueur du champ valeur


 Exemple pour faire de la diffusion (broadcast)
 int autorisation, sock;
sock = socket(AF_INET, SOCK_DGRAM, 0);
autorisation = 1;
setsockopt(SOL_SOCKET, SO_BROADCAST, &autorisation,
sizeof(int));
 On diffuse les paquets en utilisant l'adresse de diffusion du réseau
 Adresse IP dont tous les bits codant la machine sont à 1
 Ex pour machine [Link] de classe C : adresse de diffusion =
[Link]
 Toutes les machines connectés au réseau recevront ce paquet 72
 Attention à lire sur le même port que celui utilisé pour l'émission
Réalisation de multicast en C
 Pour décrire le groupe multicast, structure ip_mreq
 struct ip_mreq {
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};
 imr_multiaddr : adresse IP multicast
 imr_interfcae : adresse IP locale ou interface locale
 On utilisera par défaut INADDR_ANY

73
Réalisation de multicast en C
 Pour initialiser une socket UDP en mode multicast,
actions à effectuer
1. Créer la socket UDP de manière normale
2. Créer l'objet ip_mreq
3. Associer cet objet ip_mreq à la socket avec l'option
IP_ADD_MEMBERSHIP
 Abonnement au groupe multicast
4. Eventuellement appliquer l'option SO_REUSEADDR
 Sinon on ne peut pas avoir 2 programmes utilisant le même
groupe multicast sur la même machine à cause du bind réalisé
5. Lier la socket au numéro de port du groupe
 Emission d'un paquet
 On utilise le couple @IP du groupe/port du groupe 74
Multicast : exemple
 Adresse/port du groupe : [Link]:1234
 Note : les erreurs ne sont pas gérées
 int sock;
struct in_addr ip;
static struct sockaddr_in ad_multicast, adresse;
ip_mreq gr_multicast;

// création de la socket UDP


sock = socket(AF_INET, SOCK_DGRAM, 0);

// récupération adresse ip du groupe


inet_aton("[Link]", &ip);

// création identificateur du groupe


gr_multicast.imr_multiaddr.s_addr = ip.s_addr;
gr_multicast.imr_interface.s_addr =
htons(INADDR_ANY); 75
Multicast : exemple
 // abonnement de la socket au groupe multicast
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&gr_multicast, sizeof(struct ip_mreq));

// autorise de lier plusieurs sockets sur le port utilisé par cette


// socket, c'est-à-dire sur le port du groupe multicast
int reuse = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(int *)&reuse, sizeof(reuse));

// liaison de la socket au port du groupe multicast


bzero((char *) &adresse, sizeof(adresse));
ad_multicast.sin_family = AF_INET;
ad_multicast.sin_addr.s_addr = htons(INADDR_ANY);
ad_multicast.sin_port = htons(1234);
bind(sock, &adresse, sizeof(struct sockaddr_in));

76
Multicast : exemple
 // émission d'un paquet :
// on l'envoie au couple @/port du groupe
static struct sockaddr_in adresse;
int longueur_adresse = sizeof(struct sockaddr_in);
bzero((char *) &adresse, sizeof(adresse));
adresse.sin_family = AF_INET;
adresse.sin_addr.s_addr = ip.s_addr;
adresse.sin_port = htons(1234);
sendto(sock, message, tailleMessage , 0,
(struct sockaddr*)&adresse, longueur_adresse);

// réception d'un paquet : avec recvfrom ou peut utiliser


// aussi recv car ne recevra des paquets que venant du groupe
recv(sock, buffer, TAILLEBUFFER, 0);

77

Vous aimerez peut-être aussi