Cours Sockets Par6
Cours Sockets Par6
Eric Cariou
[Link]@[Link] 1 2
Sockets
Sockets
Socket : prise
Associée, liée localement à un port
C'est 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 Une socket est
communication à distance
Un point d'accès aux couches réseau TCP/UDP
Une socket peut-être liée
Liée localement à un port
Sur un port précis à la demande du programme
Adressage de l'application sur le réseau : son couple @IP:port
Sur un port quelconque libre déterminé par le système
Elle permet la communication avec un port distant sur une
Par défaut, on ne peut lier qu'une socket par port 5 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 Sockets UDP
Différenciation pour plusieurs raisons
Identification : on doit connaître précisément la
localisation d'un des 2 éléments communicants
Le coté serveur communique via une socket liée à un port
précis : port d'écoute
L'adresse du serveur (@IP et port) est connue du client
Dissymétrie de la communication/connexion
7 8
Le client initie la connexion ou la communication
11 12
Conversions adresse IP Structures de données C
Fichier <arpa/inet.h>
Identifiants d'une machine
Fichier <netdb.h>
Codage in_addr vers chaine de type ''X.X.X.X''
struct hostent {
char *inet_ntoa(struct in_addr adresse) char *h_name; nom officiel,
Renvoie une chaîne statique char **h_aliases; liste des alias,
Codage chaîne ''X.X.X.X'' vers in_addr ou long int h_addrtype; type d'adresse,
int h_length; longueur de l'adresse,
int inet_aton(const char *chaine, char **h_addr_list; liste des adresses
struct in_addr *adresse) #define h_addr h_addr_list[0] première
unsigned long inet_addr(char *chaine) adresse
inet_aton pas disponible sur tous systèmes };
inet_addr fonction standard Type d'adresse : internet (IP v4) par défaut
Retourne valeur INADDR_NONE en cas d'erreur Valeur = AF_INET, longueur = 4 (en octets)
Attention : INADDR_NONE correspond aussi à une adresse de
13 Une machine peut avoir plusieurs adresses IP et noms14
broadcast ([Link])
Paramètre longueur_adresse
structures ou fonctions d'accès au réseau et sockets
A l'appel : taille de l'espace réservé pour contenir l'adresse 4 opérations assurent la traduction « mode réseau / local »
Au retour : longueur effective de l'adresse 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
23 Entiers longs : adresses IP 24
Envoi/réception données en UDP Envoi/réception données en UDP
Envoi de données sur socket Réception de données
int sendto( int recvfrom(
int descripteur, descripteur de la socket qui int descripteur, descripteur de la socket qui
émettra les données, attend les données,
void *message, pointeur vers la zone de void *message, pointeur vers la zone de
données à émettre, données,
int longueur, longueur des données, int longueur, taille max de la zone données,
int option, 0, int option, 0 ou MSG_PEEK,
struct sockaddr *adresse, adresse de la socket struct sockaddr *adresse, adresse de la socket
destinatrice, émettrice,
int longueur_adresse) longueur de l'adresse int *longueur_adresse) longueur de l'adresse
Globalement, deux types d'informations à passer en paramètre
Paramètres à fournir
Lien vers les données à émettre
L'adresse identifiant le destinataire (couple @IP/port)
Lien et taille de la zone de données qui contiendra les données
reçues
Retourne
longueur_adresse : longueur de l'adresse, à initialiser à
Nombre de caractères (réellement) envoyés
25 l'appel, sera modifiée (avec la même valeur …) au retour 26
-1 en cas de problème (détails avec perror ou errno)
Exemple UDP : coté serveur (suite) Exemple UDP : coté serveur (suite)
// création de la socket // attente de données venant d'un client
sock = socket(AF_INET, SOCK_DGRAM, 0); lg = sizeof(struct sockaddr_in);
if (sock == -1) { nb_octets = recvfrom(sock, buffer, TAILLEBUF, 0,
perror("erreur création socket"); (struct sockaddr *)&addr_client, &lg);
exit(1); if (nb_octets == -1) {
} perror("erreur réception paquet");
exit(1);
// liaison de la socket sur le port local 4000 }
bzero(&addr_local, sizeof(struct sockaddr_in));
addr_local.sin_family = AF_INET; // récupère nom de la machine émettrice des données
addr_local.sin_port = htons(4000); host_client = gethostbyaddr(&(addr_client.sin_addr),
addr_local.sin_addr.s_addr=htonl(INADDR_ANY); sizeof(long), AF_INET);
if (host_client == NULL) {
if( bind(sock, (struct sockaddr*)&addr_local, perror("erreur gethostbyaddr");
sizeof(addr_local))== -1 ) { exit(1);
perror("erreur bind"); }
exit(1);
} 35 36
Exemple UDP : coté serveur (fin) Notes sur ces exemples UDP
Variables de type sockaddr_in
// affichage message reçu et coordonnées émetteur A déclarer en static
chaine = (char *)malloc(nb_octets * sizeof(char));
memcpy(chaine, buffer, nb_octets); Fonctions de copies zones mémoire
printf("recu message %s de la part de %s
memcpy(void *dest, void *source, size_t lg);
sur le port %d\n", chaine, host_client->h_name,
ntohs(addr_client.sin_port));
memmove(void *dest, void *source, size_t lg);
Copie lg octets d'une zone mémoire source vers une zone
// envoi de la réponse à l'émetteur mémoire destination
nb_octets = sendto(sock, reponse, strlen(reponse)+1, memmove : à utiliser pour des copies de zones mémoires se
0,(struct sockaddr*)&addr_client, lg);
if (nb_octets == -1) { recouvrant partiellement
perror("erreur envoi réponse"); Initialisation de zone mémoire
exit(1);
} memset(void *zone_mem, int valeur, size_t lg)
Initialise chacun des lg premiers octets de la zone avec valeur
// fermeture la socket bzero(void *zone_mem, size_t lg)
close(sock);
} 37 Initialise les lg premiers octets de la zone à 0 38
39 40
43 44
Datagramme Datagramme
Classe DatagramPacket Classe DatagramPacket
Constructeurs (suite) Méthodes « get »
public DatagramPacket(byte[] buf, int length, InetAddress getAddress()
InetAddress address, int port) Si paquet à envoyer : adresse de la machine destinataire
Création d'un paquet pour envoyer des données (sous forme d'un Si paquet reçu : adresse de la machine qui a envoyé le paquet
tableau d'octets) int getPort()
buf : contient les données à envoyer Si paquet à envoyer : port destinataire sur la machine distante
length : longueur des données à envoyer Si paquet reçu : port utilisé par le programme distant pour envoyer
Ne pas préciser une taille supérieure à celle de buf le paquet
address : adresse IP de la machine destinataire des données byte[] getData
port : numéro de port distant (sur la machine destinataire) où Données contenues dans le paquet
envoyer les données int getLength()
Si paquet à envoyer : longueur des données à envoyer
Si paquet reçu : longueur des données reçues
45 46
47 48
Sockets mode datagramme (UDP) Sockets mode datagramme (UDP)
Classe DatagramSocket Classe DatagramSocket
Méthodes d'émission/réception de paquet Autres méthodes
public void send(DatagramPacket p) public void close()
throws IOException
Ferme la socket et libère le port à laquelle elle était liée
Envoie le paquet passé en paramètre. Le destinataire est identifié par le
couple @IP/port précisé dans le paquet
public int getLocalPort()
Exception levée en cas de problème d'entrée/sortie Retourne le port local sur lequel est liée la socket
public void receive(DatagramPacket p) Possibilité de créer un canal (mais toujours en mode
throws IOException non connecté)
Reçoit un paquet de données
Pour restreindre la communication avec un seul destinataire
Bloquant tant qu'un paquet n'est pas reçu
distant
Quand paquet arrive, les attributs de p sont modifiés
Les données reçues sont copiées dans le tableau passé en paramètre
Car par défaut peut recevoir sur la socket des paquets venant
lors de la création de p et sa longueur est positionnée avec la taille des de n'importe où
données reçues
Les attributs d'@IP et de port de p contiennent l'@IP et le port de la
socket distante qui a émis le paquet 49 50
Sockets mode datagramme (UDP) Sockets UDP Java – exemple coté client
Classe DatagramSocket InetAddress adr;
DatagramPacket packet;
Réception de données : via méthode receive DatagramSocket socket;
Méthode bloquante sans contrainte de temps : peut rester en // adr contient l'@IP de la partie serveur
attente indéfiniment si aucun paquet n'est jamais reçu adr = [Link]("scinfr222");
Possibilité de préciser un délai maximum d'attente // données à envoyer : chaîne de caractères
public void setSoTimeout(int timeout) byte[] data = (new String("youpi")).getBytes();
throws SocketException
// création du paquet avec les données et en précisant l'adresse du serveur
L'appel de la méthode receive sera bloquante pendant au // (@IP et port sur lequel il écoute : 7777)
plus timeout millisecondes packet = new DatagramPacket(data, [Link], adr, 7777);
Une méthode receive se terminera alors de 2 façons
// création d'une socket, sans la lier à un port particulier
Elle retourne normalement si un paquet est reçu en moins du temps socket = new DatagramSocket();
positionné par l'appel de setSoTimeout
L'exception SocketTimeoutException est levée pour indiquer que le // envoi du paquet via la socket
délai s'est écoulé avant qu'un paquet ne soit reçu [Link](packet);
SocketTimeoutException est une sous-classe de 51 52
IOException
Sockets UDP Java – exemple coté serveur Sockets UDP en Java – exemple suite
DatagramSocket socket;
DatagramPacket packet;
La communication se fait souvent dans les 2 sens
// création d'une socket liée au port 7777
Le serveur doit donc connaître la localisation du client
DatagramSocket socket = new DatagramSocket(7777); Elle est précisée dans le paquet qu'il reçoit du client
// tableau de 15 octets qui contiendra les données reçues Réponse au client, coté serveur
byte[] data = new byte[15];
[Link](" ca vient de : "+
// création d'un paquet en utilisant le tableau d'octets [Link]()+":"+ [Link]());
packet = new DatagramPacket(data, [Link]);
// on met une nouvelle donnée dans le paquet
// attente de la réception d'un paquet. Le paquet reçu est placé dans // (qui contient donc le couple @IP/port de la socket coté client)
// packet et ses données dans data. String reponse = "bien recu";
[Link](packet); [Link]([Link]());
[Link]([Link]());
// récupération et affichage des données (une chaîne de caractères)
String chaine = new String([Link](), 0,
[Link]());
// on envoie le paquet au client
[Link](" recu : "+chaine); [Link](packet);
53 54
Sockets UDP en Java – exemple suite Critique sockets UDP
Réception réponse du serveur, coté client Avantages
// attente paquet envoyé sur la socket du client Simple à programmer (et à appréhender)
[Link](packet);
Inconvénients
// récupération et affichage de la donnée contenue dans le paquet Pas fiable
String chaine = new String([Link](), 0,
[Link]()); Ne permet d'envoyer que des tableaux de byte
[Link](" recu du serveur : "+chaine); Si en C cela convient parfaitement au niveau de la manipulation
de données, en Java, c'est de l'information de bas niveau non
naturelle
En Java, il faut pouvoir envoyer des objets quelconques via des
sockets
55 56
59 60
Sockets TCP : principe Sockets TCP : résumé communication
Principe de communication
Le serveur lie une socket dite 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 crée une socket liée à un port quelconque puis
appelle un service pour ouvrir une connexion avec le
serveur sur sa socket d'écoute
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
Il y a une socket de service par client connecté
Comme avec sockets UDP : le client et le serveur
communiquent en envoyant et recevant des données
via leur socket 61 62
// on ferme la socket
close(sock);
73 74
Exemple TCP : coté serveur (suite) Exemple TCP : coté serveur (fin)
// configuration socket écoute : 5 connexions max en attente // la connexion est établie, on attend les données envoyées par le client
if (listen(socket_ecoute, 5) == -1) { nb_octets = read(socket_service, message,
perror("erreur listen"); TAILLEBUF);
exit(1); // affichage du message reçu
} chaine_recue =
(char *)malloc(nb_octets * sizeof(char));
// on attend la connexion du client memcpy(chaine_recue, message, nb_octets);
lg_addr = sizeof(struct sockaddr_in); printf("recu message %s\n", chaine_recue);
socket_service = accept(socket_ecoute,
(struct sockaddr *)&addr_client, // on envoie la réponse au client
&lg_addr); write(socket_service, reponse, strlen(reponse)+1);
if (socket_service == -1) {
perror("erreur accept"); // on ferme les sockets
exit(1); close(socket_service);
} close(socket_ecoute);
77 78
Sockets TCP : gestion données Pseudo-connexion en UDP
En UDP : mode datagramme
Peut utiliser la primitive connect en UDP
Un paquet envoyé = un paquet reçu
Avec paquets tronqués si taille émission ou réception pas Réalise une pseudo connexion
adaptée 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
En TCP : mode connecté, communication par flux Peut alors utiliser les services send/write et read/recv à la
Un bloc de données envoyé n'est pas forcément reçu en place de sendto et recvfrom
un seul bloc d'un coup Attention
La couche TCP peut Pas d'établissement de vraie connexion : juste une mémorisation
Découper un bloc émis et le délivrer en plusieurs blocs de l'adresse destinataire pour simplifier émission
Si destinataire pas prêt à recevoir les données, elles seront perdues
Plusieurs read renverront les données d'un seul write
N'offre donc pas non plus une communication fiable à la TCP
Concaténer plusieurs blocs émis en un seul bloc reçu
Une fois connectée, une socket UDP ne peut plus émettre des
Un read renverra les données de plusieurs write paquets vers une autre adresse que celle de la pseudo
Toujours vérifier la taille des données reçues, connexion
79 80
notamment lorsque l'on envoie des structures de données
// construction de flux objets à partir des flux de la socket // se met en attente de connexion de la part d'un client distant
ObjectOutputStream output = Socket socket = [Link]();
new ObjectOutputStream([Link]());
ObjectInputStream input = // connexion acceptée : récupère les flux objets pour communiquer
new ObjectInputStream([Link]()); // avec le client qui vient de se connecter
ObjectOutputStream output =
// écriture d'une chaîne dans le flux de sortie : c'est-à-dire envoi de new ObjectOutputStream([Link]());
// données au serveur ObjectInputStream input =
[Link](new String("youpi")); new ObjectInputStream([Link]());
// attente de réception de données venant du serveur (avec le readObject) // attente les données venant du client
// on sait qu'on attend une chaîne, on peut donc faire un cast directement String chaine = (String)[Link]();
String chaine = (String)[Link]();
[Link](" recu : "+chaine);
[Link](" recu du serveur : "+chaine);
91 92
97 98
Sockets TCP – gestion plusieurs clients Sockets TCP – gestion plusieurs clients
Fonctionnement de TCP impose des contraintes Boucle de fonctionnement général d'un serveur
Lecture sur une socket : opération bloquante pour gérer plusieurs clients
Tant que des données ne sont pas reçues while(true) {
Attente de connexion : opération bloquante socketClient = acceptConnection();
Jusqu'à la prochaine connexion d'un client distant newProcessus(socketClient); }
Avec un seul flot d'exécution (processus/thread)
Exemple
Si ne sait pas quel est l'ordonnancement des arrivées des avec 2
données des clients ou de leur connexion au serveur clients →
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 101 102
C – gestion plusieurs clients TCP C – gestion des sockets
Création d'un nouveau processus via un fork() à chaque Le fork() duplique toutes les données du père au fils créé
connexion de client Y compris sa table des descripteurs de fichier
Le processus principal fait les accept() en boucle et crée un Une socket est un descripteur de fichier
nouveau processus fils pour la communication avec un nouveau
client connecté Le fils ferme la socket d'écoute
while(1) { Par principe, il n'en a pas besoin, ce n'est pas son rôle de gérer
socket_service = accept(socket_ecoute, les demandes de connexion
(struct sockaddr *)&addr_client,&lg_addr);
if (fork() == 0) { Ne ferme pas physiquement la socket d'écoute car le père a
// on est dans le fils toujours son descripteur ouvert
close(socket_ecoute); Le père ferme la socket de service qui vient d'être créée
// fonction qui gère la communication avec le client
traiter_communication(socket_service); Là aussi, par principe, le père n'en a pas besoin
exit(0); Mais c'est surtout indispensable parce qu'au fil du temps sa table
} des descripteurs de fichier se remplit avec les connexions et une
close(socket_service); fois pleine, plus aucune connexion ne peut être acceptée
} 103 104
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
Multicast UDP/IP 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
107 108
Multicast Multicast
Adresse IP multicast Utilités du multicast UDP/IP
Classe d'adresse IP entre [Link] et [Link]
Évite d'avoir à créer X connexions et/ou d'envoyer X fois
Classe D la même donnée à X machines différentes
Adresses entre [Link] et [Link] sont utilisables par
un programme quelconque En pratique
Les autres sont réservées Utilisé pour diffuser des informations
Une adresse IP multicast n'identifie pas une machine sur Diffusion de flux vidéos à plusieurs récepteurs
un réseau mais un groupe multicast Chaîne de télévision, diffusion d'une conférence
Le même flux est envoyé à tous au même moment
Socket UDP multicast Pour récupérer des informations sur le réseau
Avant envoi de paquet : on doit rejoindre un groupe [Link] : pour localiser un serveur DHCP
Identifié par un couple : @IP multicast/numéro port Limites
Un paquet envoyé par un membre du groupe est reçu Non fiable et non connecté comme UDP
par tous les membres de ce groupe
109 110
111 112
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; Multicast en Java
adresse.sin_port = htons(1234);
sendto(sock, message, tailleMessage , 0,
(struct sockaddr*)&adresse, longueur_adresse);
119 120
Multicast UDP en Java Multicast UDP en Java
Classe [Link] Classe [Link] (suite)
Spécialisation de DatagramSocket Gestion des groupes
Constructeurs : identiques à ceux de DatagramSocket public void joinGroup(InetAddress mcastaddr)
throws IOException
public MulticastSocket() throws SocketException Rejoint le groupe dont l'adresse IP multicast est passée en
Crée une nouvelle socket en la liant à un port quelconque libre paramètre
Exception levée en cas de problème (a priori il doit pas y en avoir) L'exception est levée en cas de problèmes, notamment si l'adresse
public MulticastSocket(int port) IP n'est pas une adresse IP multicast valide
throws SocketException public void leaveGroup(InetAddress mcastaddr)
Crée une nouvelle socket en la liant au port précisé par le throws IOException
paramètre port : c'est le port qui identifie le groupe de multicast Quitte un groupe de multicast
Exception levée en cas de problème L'exception est levée si l'adresse IP n'est pas une adresse IP
multicast valide
Pas d'exception levée ou de problème quand on quitte un groupe
auquel on appartient pas
121 122
123 124
// traite le résultat
...
// quitte le groupe
[Link](group);
Notes
Il est possible que le receive récupère le paquet que le send vient
juste d'envoyer
Besoin d'un autre receive pour réponse venant d'un autre élément
125