Qu'est-ce que TCP ?
TCP (Transmission Control Protocol) est le protocole de transport le plus utilisé sur Internet. Défini dans la RFC 793 en 1981, il offre un service de communication fiable, ordonné et full-duplex entre deux processus applicatifs.
Les caractéristiques fondamentales de TCP
| Caractéristique | Description |
|---|---|
| Fiable | Les données perdues sont retransmises automatiquement |
| Ordonné | Les segments sont réordonnés à la réception |
| Contrôle de flux | Le récepteur contrôle le débit de l'émetteur |
| Contrôle de congestion | Adaptation au débit disponible du réseau |
| Full-duplex | Transmission simultanée dans les deux sens |
| Orienté connexion | Établissement explicite avant transfert |
| Basé sur les octets | Flux d'octets, pas de frontières de messages |
TCP est utilisé par HTTP/HTTPS, SSH, SMTP, FTP, et tout protocole nécessitant une livraison garantie des données.
Structure du segment TCP
Un segment TCP est composé d'un en-tête de 20 octets minimum suivi des données applicatives.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
├─────────────────────────┬─────────────────────────────────────────┤
│ Port source (16 bits)│ Port destination (16 bits) │
├─────────────────────────┴─────────────────────────────────────────┤
│ Numéro de séquence (32 bits) │
├───────────────────────────────────────────────────────────────────┤
│ Numéro d'acquittement (32 bits) │
├──────────┬──────────┬─────────────────────────────────────────────┤
│Offset(4) │Réservé(6)│ URG ACK PSH RST SYN FIN │ Fenêtre (16b) │
├──────────┴──────────┴─────────────────────────┴──────────────────┤
│ Checksum (16 bits) │ Pointeur urgent (16 bits) │
├──────────────────────────────────┴──────────────────────────────-─┤
│ Options (0-40 octets) │
├───────────────────────────────────────────────────────────────────┤
│ Données │
└───────────────────────────────────────────────────────────────────┘
Description des champs clés
| Champ | Taille | Description |
|---|---|---|
| Port source | 16 bits | Port du processus émetteur (0-65535) |
| Port destination | 16 bits | Port du processus récepteur |
| Numéro de séquence | 32 bits | Position du premier octet de données dans le flux |
| Numéro d'acquittement | 32 bits | Prochain octet attendu par le récepteur |
| Data Offset | 4 bits | Taille de l'en-tête en mots de 32 bits |
| Flags | 6 bits | SYN, ACK, FIN, RST, PSH, URG |
| Fenêtre (rwnd) | 16 bits | Espace tampon disponible chez le récepteur |
| Checksum | 16 bits | Vérification d'intégrité (en-tête + données) |
Les flags TCP
| Flag | Signification | Utilisé lors de |
|---|---|---|
| SYN | Synchronize — initie une connexion | Handshake (étapes 1 et 2) |
| ACK | Acknowledge — confirme la réception | Handshake étape 3, puis toujours actif |
| FIN | Finish — demande de fermeture propre | Four-way teardown |
| RST | Reset — fermeture immédiate/erreur | Port fermé, connexion invalide |
| PSH | Push — envoyer immédiatement à l'application | Données urgentes |
| URG | Urgent — pointeur urgent actif | Rarement utilisé |
Le Three-Way Handshake
Avant tout transfert de données, TCP établit une connexion via le three-way handshake (poignée de main en 3 étapes). Ce processus synchronise les numéros de séquence initiaux (ISN) des deux parties.
Les 3 étapes avec numéros de séquence
Client Serveur
│ │
│──── [SYN] Seq=X ─────────────────────► │ Étape 1
│ "Je veux une connexion" │ Client choisit ISN=X au hasard
│ │
│ ◄── [SYN-ACK] Seq=Y, Ack=X+1 ─────────│ Étape 2
│ "D'accord, mon ISN=Y, j'attends X+1"│ Serveur choisit ISN=Y
│ │
│──── [ACK] Seq=X+1, Ack=Y+1 ───────────►│ Étape 3
│ "Confirmé, j'attends Y+1" │
│ │
│ [Connexion établie] │
Exemple concret :
Client → Serveur : [SYN] Seq=1000
Serveur → Client : [SYN-ACK] Seq=5000, Ack=1001
Client → Serveur : [ACK] Seq=1001, Ack=5001
Pourquoi 3 étapes et pas 2 ?
Deux étapes suffiraient pour que le client sache que le serveur est prêt. Mais il faut aussi que le serveur sache que le client a reçu son SYN-ACK. Le troisième message (ACK du client) confirme cette bidirectionnalité. C'est le minimum mathématique pour synchroniser les deux ISN de manière fiable.
Observer le handshake avec tcpdump
# Capturer le handshake TCP vers un serveur web
sudo tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' port 80 -n
# Exemple de sortie :
# 14:30:01.001 IP 192.168.1.100.54321 > 93.184.216.34.80: Flags [S], seq 1234567890
# 14:30:01.052 IP 93.184.216.34.80 > 192.168.1.100.54321: Flags [S.], seq 9876543210, ack 1234567891
# 14:30:01.052 IP 192.168.1.100.54321 > 93.184.216.34.80: Flags [.], ack 9876543211
# Flags : [S]=SYN, [.]=ACK, [S.]=SYN-ACK, [F.]=FIN-ACK, [R]=RST
Transfert de données
Numérotation des octets
TCP traite le flux de données comme un flux d'octets numéroté. Chaque octet a un numéro de séquence. Si 500 octets de données sont envoyés dans un segment avec Seq=1001, le prochain segment aura Seq=1501.
Segment 1 : Seq=1001, 500 octets de données → octets 1001-1500
Segment 2 : Seq=1501, 500 octets de données → octets 1501-2000
Segment 3 : Seq=2001, 200 octets de données → octets 2001-2200
Acquittements cumulatifs
L'acquittement TCP est cumulatif : Ack=N signifie "J'ai reçu tous les octets jusqu'à N-1, j'attends l'octet N."
Envoi : Seq=1001, 500 octets
Réception : ACK=1501 ("j'ai reçu jusqu'à 1500, j'attends 1501")
Envoi : Seq=1501, 500 octets
Réception : ACK=2001 ("j'ai reçu jusqu'à 2000, j'attends 2001")
MSS — Maximum Segment Size
Le MSS est la taille maximale des données dans un segment TCP (hors en-tête). Il est négocié lors du handshake via les options TCP.
MSS typique sur Ethernet : 1460 octets
(MTU Ethernet 1500 - En-tête IP 20 - En-tête TCP 20 = 1460)
Contrôle de flux — La fenêtre de réception
Le contrôle de flux protège le récepteur d'une inondation de données. Le récepteur annonce dans chaque ACK sa fenêtre de réception (rwnd) : la quantité de données qu'il peut accepter sans saturer son tampon.
Récepteur → Émetteur : ACK, rwnd=8192
Signification : "Tu peux m'envoyer jusqu'à 8192 octets sans ACK"
L'émetteur NE PEUT PAS avoir plus de rwnd octets "en vol" sans ACK.
Fenêtre glissante
Flux de données (chaque bloc = 1 segment) :
[1][2][3][4][5][6][7][8][9]...
│←──── fenêtre rwnd=4 ────→│
↑
Prochain à envoyer
Après ACK du segment 1 :
[1][2][3][4][5][6][7][8][9]...
│←── fenêtre ──→│
↑
La fenêtre "glisse" vers la droite
Si rwnd=0, l'émetteur s'arrête et envoie des sondes (Zero Window Probe) toutes les quelques secondes jusqu'à ce que la fenêtre s'ouvre à nouveau.
Contrôle de congestion
Le contrôle de congestion protège le réseau (pas seulement le récepteur) d'une surcharge. TCP maintient une fenêtre de congestion (cwnd) côté émetteur, indépendante de rwnd.
La quantité réelle de données en vol est limitée par : min(rwnd, cwnd).
Slow Start
Au démarrage d'une connexion, TCP ne connaît pas la capacité du réseau. Il démarre avec une cwnd petite (typiquement 10 MSS avec IW10) et double cwnd à chaque RTT jusqu'à atteindre un seuil (ssthresh).
RTT 1 : cwnd = 10 MSS → envoie 10 segments
RTT 2 : cwnd = 20 MSS → envoie 20 segments
RTT 3 : cwnd = 40 MSS → envoie 40 segments
...jusqu'au ssthresh
AIMD — Additive Increase Multiplicative Decrease
Une fois le ssthresh atteint, TCP passe en mode évitement de congestion :
- Additive Increase : cwnd augmente de 1 MSS par RTT (progression linéaire)
- Multiplicative Decrease : en cas de perte détectée, cwnd est divisée par 2
Perte détectée (timeout) :
ssthresh = cwnd / 2
cwnd = 1 MSS (redémarre slow start)
Perte détectée (3 ACK dupliqués → fast retransmit) :
ssthresh = cwnd / 2
cwnd = ssthresh (fast recovery, sans repartir à 0)
Retransmission
TCP retransmet les segments perdus grâce à deux mécanismes :
Timer RTO (Retransmission Timeout)
Chaque segment envoyé déclenche un timer RTO. Si aucun ACK n'est reçu avant expiration, le segment est retransmis. Le RTO est calculé dynamiquement en fonction du RTT mesuré.
Fast Retransmit
Si l'émetteur reçoit 3 ACK dupliqués (même numéro d'ACK répété 3 fois), il infère qu'un segment est perdu et le retransmet immédiatement, sans attendre le RTO. C'est plus rapide que d'attendre le timer.
# Observer les retransmissions avec ss
ss -tn | head -20
# Observer avec tcpdump (recherche de segments avec flag RST ou retransmissions)
sudo tcpdump -i eth0 'tcp[tcpflags] & tcp-rst != 0'
# Statistiques TCP globales (retransmissions, erreurs)
netstat -s | grep -i retrans
# ou
ss -s
The Four-Way Teardown (fermeture propre)
La fermeture d'une connexion TCP nécessite 4 échanges car chaque sens de communication doit être fermé indépendamment.
Client Serveur
│ │
│──── [FIN] Seq=X ─────────────────►│ Client ferme son côté émission
│ │
│ ◄── [ACK] Ack=X+1 ───────────────│ Serveur confirme
│ │ (le serveur peut encore envoyer des données)
│ │
│ ◄── [FIN] Seq=Y ─────────────────│ Serveur ferme son côté émission
│ │
│──── [ACK] Ack=Y+1 ───────────────►│ Client confirme
│ │
L'état TIME_WAIT
Après avoir envoyé le dernier ACK, le client entre en état TIME_WAIT pendant 2 × MSL (Maximum Segment Lifetime, typiquement 60 secondes, soit 2 minutes de TIME_WAIT).
Pourquoi ? Pour s'assurer que :
- Le dernier ACK est bien arrivé (sinon le serveur renvoie son FIN)
- Les anciens segments en transit dans le réseau expirent et ne perturbent pas une future connexion sur les mêmes ports
Les états TCP (machine d'états)
TCP est une machine d'états. Voici les états principaux :
CLOSED → [SYN envoyé] → SYN_SENT → [SYN-ACK reçu] → ESTABLISHED
│
CLOSED ← [FIN-ACK reçu] ← TIME_WAIT ← [FIN reçu] ← FIN_WAIT_2
│
FIN_WAIT_1 ← [FIN envoyé]
| État | Description |
|---|---|
LISTEN |
Serveur en attente de connexions entrantes |
SYN_SENT |
Client a envoyé SYN, attend SYN-ACK |
SYN_RECEIVED |
Serveur a reçu SYN, a envoyé SYN-ACK |
ESTABLISHED |
Connexion active, transfert de données |
FIN_WAIT_1 |
FIN envoyé, attend ACK |
FIN_WAIT_2 |
ACK reçu, attend FIN du pair |
CLOSE_WAIT |
FIN reçu, l'application doit fermer |
CLOSING |
FIN simultanés des deux côtés |
LAST_ACK |
Dernier ACK attendu avant fermeture |
TIME_WAIT |
Attente 2×MSL avant fermeture définitive |
CLOSED |
Connexion terminée |
# Voir tous les états TCP en temps réel
ss -tn
# Compter les connexions par état
ss -tn | awk '{print $1}' | sort | uniq -c | sort -rn
# Voir les connexions ESTABLISHED avec le processus propriétaire
ss -tnp state established
# Voir les sockets en TIME_WAIT (indicateur de charge sur un serveur web)
ss -tn state time-wait | wc -l
# Surveiller en temps réel
watch -n 1 'ss -tn | awk "{print \$1}" | sort | uniq -c'
L'algorithme de Nagle
L'algorithme de Nagle (RFC 896) empêche l'envoi de très petits segments ("tinygrams") en regroupant les petites écritures applicatives. TCP attend que :
- Il y ait assez de données pour remplir un segment de taille MSS, ou
- Tous les segments précédemment envoyés ont été acquittés
Cela améliore l'efficacité réseau mais ajoute de la latence. Pour des protocoles interactifs (SSH, gaming), Nagle est souvent désactivé :
# Désactiver Nagle en Python
import socket
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# Désactiver Nagle avec netcat
nc -N ...
Commandes pratiques complètes
# Observer le trafic TCP sur le port 80
sudo tcpdump -i eth0 tcp port 80 -n
# Afficher toutes les connexions TCP avec processus
ss -tnp
# Tester la connectivité TCP sur un port
nc -zv google.com 443
# Connected to google.com port 443 [tcp/https] succeeded!
# Tester avec timeout
nc -zv -w 3 192.168.1.50 22
# Simuler un client HTTP simple avec netcat
echo -e "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n" | nc example.com 80
# Afficher les statistiques TCP du kernel
cat /proc/net/tcp
# Voir les connexions TCP en détail (verbose)
ss -tnip
# Surveiller les connexions d'un port spécifique
watch -n 1 'ss -tnp | grep :443'
Récapitulatif
| Concept | Détail clé |
|---|---|
| Three-way handshake | SYN → SYN-ACK → ACK (synchronise les ISN) |
| Numéro de séquence | Position de l'octet dans le flux, 32 bits |
| Acquittement cumulatif | Ack=N → "j'ai reçu jusqu'à N-1" |
| Fenêtre rwnd | Contrôle de flux côté récepteur |
| Fenêtre cwnd | Contrôle de congestion côté réseau |
| Slow Start | Croissance exponentielle au démarrage |
| AIMD | +1MSS/RTT, /2 sur perte |
| Four-way teardown | FIN → ACK → FIN → ACK |
| TIME_WAIT | 2×MSL après fermeture, évite les anciens segments |
| Nagle | Regroupe les petites écritures (désactiver pour temps réel) |
Passez maintenant au chapitre suivant : UDP : la vitesse avant tout — le protocole sans connexion qui privilégie la rapidité à la fiabilité.