Les sockets · TCP / UDP
Une API qui transforme deux processus distants en deux extrémités d'un même tuyau. Mode connecté (TCP, fiable) ou datagramme (UDP, rapide). Adresses, ports, byte order — tout l'arsenal pour parler à travers le réseau.
📄 Ressources du cours
🎥 Vidéos recommandées
-
🇫🇷
What is difference between Semaphore and Mutex
HowTo · 07:00 · 293k vues — Mutex is a object owned by thread who is executing in critical section whereas semaphore is a signaling mechanism.
-
🇫🇷
Semaphore Vs. Mutex - A Clear Understanding
Shriram Vasudevan · 10:14 · 101k vues — Here you go.. The clear differences between Semaphore and Mutex. All the technical aspects are discussed with examples for each.
-
🇬🇧
What is difference between Semaphore and Mutex
HowTo · 07:00 · 293k vues — Mutex is a object owned by thread who is executing in critical section whereas semaphore is a signaling mechanism.
1. Qu'est-ce qu'une socket ?
Une socket est un point de communication (extrémité) par lequel un processus peut émettre / recevoir des données (messages) à destination / en provenance d'un autre processus.
Étendue — familles de protocoles
AF_INET— InternetAF_UNIX— Fichiers locaux- OSI, SN, DEC, CCITT (X25), Appletalk…
Modèle Client / Serveur
Processus rendant un service spécifique, installé puis mis en attente sur une station bien identifiée (sur un réseau d'ordinateurs).
Processus appelant le serveur afin d'obtenir le service. Lancé à la demande à partir de n'importe quelle station.
2. Adressage TCP/IP
Chaque ordinateur (machine) possède une adresse Internet (IP) unique composée de 32 bits. L'adresse IP contient assez d'information pour identifier de façon unique un réseau donné et un ordinateur spécifique sur le réseau.
Exemples : 132.227.104.15, 193.49.30.38.
Nom de domaine — hiérarchique
- Code d'identification de l'organisation (
comcommercial,eduéducation,govgouvernement…) - Code d'identification du pays (
fr,ca,us…) - Code d'identification d'un sous-domaine (
univ-cergy) - Nom de la machine hôte (
www) - Exemple :
www.formadep.fr
La structure sockaddr_in (AF_INET)
Désignation spécifique permettant d'être identifié de l'extérieur. Structure décrite dans le fichier <netinet/in.h>.
struct sockaddr_in {
short sin_family; // AF_INET
unsigned short sin_port; // numero de port (htons)
struct in_addr sin_addr; // adresse IP
char sin_zero[8]; // padding
};
INADDR_ANY) ou spécifier l'adresse d'une interface IP particulière via gethostname() et gethostbyname().
getservbyname() pour un service existant), ou bien fixer un numéro non réservé (>= 1024), ou encore laisser le système choisir un numéro de port (sin_port = 0).
3. TCP vs UDP
| Critère | UDP — User Datagram Protocol | TCP — Transport Control Protocol |
|---|---|---|
| Entête | minimale | plus lourde (checksum, séquencement…) |
| Livraison | non fiable | fiable |
| Checksum | — | total de contrôle calculé et inclus dans chaque paquet |
| Connexion | aucune connexion à établir | établir la connexion avant la transmission |
| Type de socket | SOCK_DGRAM | SOCK_STREAM |
| Mode | non connecté | connecté |
Types de sockets
| Type | Description |
|---|---|
SOCK_DGRAM | Transport de données en mode non connecté. Dans le domaine AF_INET, le protocole UDP est utilisé. Les frontières de messages sont préservées. |
SOCK_STREAM | Transport de données en mode connecté. Dans le domaine AF_INET, utilisation de TCP. Frontières de messages préservées et possibilité d'envoi de messages urgents. |
SOCK_RAW | Accès au service de niveau 3 (émission/réception de paquets IP). Utilise des paquets bruts (raw packets) permettant d'envoyer des paquets IP directement sans encapsulation dans les protocoles TCP ou UDP. |
SOCK_SEQPACKET | Comme SOCK_STREAM mais ne permet pas l'envoi de messages urgents. |
4. Enchaînement des primitives
Deux scénarios — mode connecté (TCP, SOCK_STREAM) et mode non connecté (UDP, SOCK_DGRAM).
Cycle TCP — serveur (socket → bind → listen → accept) et client (socket → connect → send/recv → close).
socket() → bind() → recvfrom() / sendto() → close(). Client socket() → sendto() / recvfrom() → close(). Pas de listen, pas de accept, pas de connect.
5. Les primitives — prototypes
socket() — créer une socket
Permet de créer une socket (descripteur de socket) pour établir un canal de communication.
int socket(int domain, int type, int protocol);
// domain : AF_INET, AF_UNIX, ...
// type : SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET
// protocol : 0 (auto), IPPROTO_TCP, IPPROTO_UDP
bind() — informations locales
Permet d'associer un nom ou une adresse et un numéro de port au descripteur de socket retourné par socket(). Ses arguments sont :
- le descripteur
sde la socket considérée ; - un pointeur
addrvers une structure qui contient l'adresse ou le nom assigné et le numéro de port utilisé ; - la taille
namelende la structure vers laquelle pointeaddr.
int bind(int s, struct sockaddr *addr, socklen_t namelen);
listen() — file d'attente
Longueur maximum de la file d'attente des communications en cours d'établissement.
int listen(int s, int backlog);
connect() — pseudo-connexion (client)
Établissement de la (pseudo-)connexion en renseignant la socket pour le destinataire.
int connect(int s, struct sockaddr *addr, socklen_t addrlen);
accept() — connexions acceptables (serveur)
Définition de l'ensemble des connexions acceptables.
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
recv() / read() — réception
Réception de données — strictement identique à l'appel système habituel read. Autres primitives similaires à read.
ssize_t read (int s, void *buf, size_t n);
ssize_t recv (int s, void *buf, size_t n, int flags);
ssize_t recvfrom(int s, void *buf, size_t n, int flags,
struct sockaddr *from, socklen_t *fromlen);
send() / write() — émission
Émission de données — identiques ou similaires à l'appel système write.
ssize_t write (int s, const void *buf, size_t n);
ssize_t send (int s, const void *buf, size_t n, int flags);
ssize_t sendto(int s, const void *buf, size_t n, int flags,
const struct sockaddr *to, socklen_t tolen);
close() — libération
• Libère le descripteur de socket
• Libère l'espace de stockage associé à la socket
• Libère la connexion si elle existe
int close(int s);
6. Conversions — noms, adresses, byte order
gethostname() — nom de la machine locale
#include <unistd.h>
#include <stdio.h>
int main() {
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0) {
printf("Nom d'hote : %s\n", hostname);
} else {
perror("gethostname");
}
return 0;
}
gethostbyname() — résoudre un nom en IP
Paramètre d'entrée : le nom de la machine. Valeur de retour : pointeur vers une structure hostent en cas de succès, NULL en cas d'erreur.
struct hostent {
char *h_name; // nom officiel de l'hote
char **h_aliases; // liste d'alias
int h_addrtype; // type d'adresse (AF_INET)
int h_length; // longueur de l'adresse
char **h_addr_list; // liste d'adresses IP
};
#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>
int main() {
struct hostent *host = gethostbyname("www.google.com");
if (host == NULL) {
perror("gethostbyname");
return 1;
}
printf("Nom : %s\n", host->h_name);
char *ip = inet_ntoa(*(struct in_addr *)host->h_addr_list[0]);
// inet_ntoa() sert a convertir une adresse IP IPv4 binaire en chaine
// de caracteres lisible.
printf("IP : %s\n", ip);
return 0;
}
gethostbyaddr() — DNS inverse
Fonction qui permet de résoudre une adresse IP en nom d'hôte (DNS inverse).
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
// addr : pointeur vers l'adresse IP (binaire)
// len : taille de l'adresse
// type : AF_INET (IPv4) ou AF_INET6 (IPv6)
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
int main() {
struct in_addr addr;
addr.s_addr = inet_addr("100.10.20.5");
struct hostent *host = gethostbyaddr(&addr, sizeof(addr), AF_INET);
if (host != NULL) {
printf("Nom d'hote : %s\n", host->h_name);
} else {
perror("gethostbyaddr");
}
return 0;
}
getsockname() — adresse IP d'une socket
Obtenir l'adresse IP d'une socket dont on connaît le descripteur.
int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
Little vs Big Endian
• Windows, Linux et Mac OS X : Little Endian
• Réseau Internet : Big Endian (standard réseau universel)
Il faut donc toujours convertir du Little Endian au Big Endian et vice versa avant et après communication.
| @IP | Big Endian | Little Endian |
|---|---|---|
| 132.227.70.77 | 84.E3.46.4D | 4D.46.E3.84 |
Les quatre fonctions de conversion
| Fonction | Sens | Taille | Usage typique |
|---|---|---|---|
htons(short) | host to network short | 16 bits | numéro de port |
htonl(long) | host to network long | 32 bits | adresse IP, entier 32 bits |
ntohs(short) | network to host short | 16 bits | port reçu du réseau |
ntohl(long) | network to host long | 32 bits | IP/entier reçu du réseau |
h = host (machine) · n = network · s = short (16) · l = long (32).
htons se lit « host-to-network short » — sens, puis taille.
7. Exemple TCP — Serveur
Échange de messages client / serveur. Partie serveur.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8080 // Le port d'ecoute
#define BUFFER_SIZE 1024 // Taille du buffer de reception
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
char buffer[BUFFER_SIZE] = {0};
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Erreur lors de la creation du socket");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // Accepte les connexions sur n'importe quelle adresse
address.sin_port = htons(PORT);
// Lier le socket a l'adresse et au port
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("Erreur de binding");
exit(EXIT_FAILURE);
}
// Ecouter les connexions entrantes
if (listen(server_fd, 3) < 0) {
perror("Erreur lors de l'ecoute");
exit(EXIT_FAILURE);
}
printf("En attente de connexion...\n");
new_socket = accept(server_sd, clien_address, sizeof(client_address));
// Lire le message du client
read(new_socket, buffer, BUFFER_SIZE);
printf("Message du client : %s\n", buffer);
// Repondre au client
char *response = "Message recu!";
send(new_socket, response, strlen(response), 0);
printf("Reponse envoyee au client\n");
close(new_socket);
close(server_fd);
return 0;
}
server_sd, clien_address, client_address dans l'appel accept() — on conserve la version originale du cours sans correction.
8. Exemple TCP — Client
Partie client.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_IP "127.0.0.1" // Adresse IP du serveur
#define PORT 8080 // Le port d'ecoute
#define BUFFER_SIZE 1024 // Taille du buffer de reception
int main() {
int int sock = 0;
struct sockaddr_in server_address;
char buffer[BUFFER_SIZE] = {0};
// Creation du socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Erreur lors de la creation du socket");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_port = htons(PORT);
// Se connecter au serveur
if (connect(sock, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) {
perror("Erreur lors de la connexion");
exit(EXIT_FAILURE);
}
// Envoyer un message au serveur
char *message = "Bonjour, serveur !";
send(sock, message, strlen(message), 0);
printf("Message envoye au serveur\n");
// Lire la reponse du serveur
read(sock, buffer, BUFFER_SIZE);
printf("Reponse du serveur : %s\n", buffer);
// Fermer le socket
close(sock);
return 0;
}
int int sock = 0; (double int) et la référence à address.sin_family alors que la variable est server_address sont reprises telles quelles dans le cours.
9. Connexions concurrentes
Pour qu'un serveur traite plusieurs clients en parallèle, on combine l'API socket avec les techniques de concurrence vues en début de semestre.
Avec fork()
Schéma : Création/attachement (socket/bind) → Ouverture du service (listen) → Attente de la demande de connexion (accept) → Création d'un processus fils (fork) → traitement de la demande du client.
Avec un thread
Variante : à chaque accept() réussi, le programme courant crée un thread qui prend en charge le traitement de la demande du client. Le pattern est identique, on remplace fork() par la création de thread.
•
fork() : isolement total, copie complète du processus, plus coûteux.• thread : mémoire partagée, plus léger, mais synchronisation à gérer.
★ Réviser le chapitre
❓ Q/R — pour vérifier ta compréhension
Qu'est-ce qu'une socket, en une phrase ?
Quelle famille de protocole pour Internet ? Pour les fichiers locaux ?
AF_INET pour Internet ; AF_UNIX pour les fichiers locaux.Différence entre SOCK_STREAM et SOCK_DGRAM ?
SOCK_STREAM = mode connecté, utilise TCP, livraison fiable, frontières préservées + messages urgents possibles. SOCK_DGRAM = mode non connecté, utilise UDP, frontières préservées mais livraison non fiable.Pourquoi a-t-on htons() / ntohs() ?
Quel est l'ordre des primitives côté serveur TCP ?
socket() → bind() → listen() → accept() → recv()/send() → close().Et côté client TCP ?
socket() → connect() → send()/recv() → close(). Pas de bind, pas de listen, pas de accept.Que vaut INADDR_ANY et à quoi sert-il ?
address.sin_addr.s_addr avant bind().Comment laisser le système choisir un port libre ?
sin_port = 0. Pour un port fixe, utiliser getservbyname() (service connu) ou un numéro non réservé ≥ 1024.Que fait close() sur une socket ?
Comment un serveur TCP gère plusieurs clients en parallèle ?
accept() réussi, il crée un processus fils via fork() (ou un thread) qui traite la demande, pendant que le père/programme principal retourne à accept().🃏 Flashcards éclair
Clique pour retourner.
Socket
Définition courte.
tourne →socket() — prototype
Signature.
tourne →int socket(int domain,
int type,
int protocol);AF_INET / SOCK_STREAM
Quel protocole ?
tourne →AF_INET / SOCK_DGRAM
Quel protocole ?
tourne →sockaddr_in
Champs.
tourne →sin_family // AF_INET
sin_port // htons(PORT)
sin_addr // s_addr = IP
sin_zero[8] // paddingCycle serveur TCP
Ordre des primitives.
tourne →socket → bind → listen → accept → recv/send → closeCycle client TCP
Ordre des primitives.
tourne →socket → connect → send/recv → close — pas de bind, listen, accept.htons vs htonl
Différence.
tourne →htons = host to network short (16 bits, port). htonl = host to network long (32 bits, IP).Big vs Little Endian
Côté réseau ?
tourne →INADDR_ANY
Rôle.
tourne →sin_addr.s_addr pour accepter les connexions sur n'importe quelle adresse de la machine.UDP — primitives d'échange
Différentes de TCP ?
tourne →sendto() et recvfrom() à la place de send/recv — on précise struct sockaddr dst/src à chaque appel.gethostbyname()
Renvoie quoi ?
tourne →struct hostent * (avec h_name, h_addr_list…) ou NULL en cas d'erreur. Résout un nom DNS en adresse IP.close() sur une socket
3 effets.
tourne →Concurrence — TCP
Comment ?
tourne →accept() : fork() un fils (ou créer un thread) pour traiter le client, et le père retourne à accept().✎ Quiz éclair
AF_UNIX est pour les fichiers locaux ; AF_INET pour Internet.SOCK_STREAM + AF_INET → TCP, mode connecté, livraison fiable.htons() ?sin_port.📌 À retenir
- Socket = extrémité de communication ; descripteur ≈ descripteur de fichier UNIX
- Familles :
AF_INET(Internet) ·AF_UNIX(fichiers locaux) · OSI, SN, DEC, X25, Appletalk… - Types :
SOCK_STREAM(TCP) ·SOCK_DGRAM(UDP) ·SOCK_RAW·SOCK_SEQPACKET - TCP : mode connecté, checksum, livraison fiable, handshake
- UDP : non connecté, entête minimale, livraison non fiable
struct sockaddr_in:sin_family,sin_port,sin_addr— fichier<netinet/in.h>- Serveur TCP :
socket → bind → listen → accept → recv/send → close - Client TCP :
socket → connect → send/recv → close - UDP : pas de
listen/accept/connect; échange viasendto/recvfrom INADDR_ANY= toutes les interfaces ;sin_port = 0= port choisi par le système ; port ≥ 1024 si non réservé- Réseau = Big Endian ; machine = Little Endian → toujours convertir
htons/ntohs(16 bits, port) ·htonl/ntohl(32 bits, IP)gethostname,gethostbyname,gethostbyaddr,inet_addr,inet_ntoa,getsocknameclose()libère descripteur + stockage + connexion- Concurrence : après
accept()→fork()(processus fils) ou création d'un thread, le père/programme principal repart àaccept()