- TCP (Transmission Control Protocol) est le protocole central d’Internet qui permet une transmission de données fiable et ordonnée, même dans un environnement réseau instable
- Alors qu’IP ne s’occupe que de l’acheminement des données entre hôtes, TCP assure la communication entre processus basée sur les ports ainsi que la récupération d’erreurs, la retransmission et le contrôle de l’ordre
- Grâce au contrôle de flux (flow control) et au contrôle de congestion (congestion control), il ajuste les échanges pour ne pas dépasser les limites du buffer de réception ou de la bande passante réseau
- Des exemples simples de serveur TCP et de serveur HTTP implémentés en C expliquent concrètement la création de sockets, le bind, le listen, l’acceptation des connexions et les échanges en émission/réception
- La structure interne de TCP — numéros de séquence et d’ACK, fenêtre, checksum, flags (SYN/ACK/FIN/RST) — constitue la base essentielle qui permet le fonctionnement fiable de l’Internet actuel
Nécessité et rôle de TCP
- IP se charge uniquement de l’acheminement des paquets entre hôtes, et une couche de transport comme TCP/UDP est nécessaire pour la communication entre processus
- L’adresse IP est comparée à un « bâtiment » et le port à un « appartement », chaque application communiquant après s’être liée à un port
- TCP masque l’instabilité du réseau, comme les pertes de paquets, les doublons et l’inversion d’ordre, et garantit la fiabilité via la retransmission et le checksum
- Les routeurs restent simples, et la fiabilité est gérée aux deux extrémités de la communication, ce qui réduit la complexité de l’infrastructure réseau
- Grâce à cette architecture, les grands services Internet comme HTTP, SMTP et SSH fonctionnent de manière stable
Contrôle de flux et contrôle de congestion
- Le récepteur stocke temporairement les données via le buffer de réception (receive buffer) du noyau, dont la taille se configure avec
net.ipv4.tcp_rmem
- L’émetteur ajuste le volume envoyé en recevant, via le champ window, la quantité de données que le récepteur peut accepter
- Pour éviter la congestion due aux différences de bande passante dans l’ensemble du réseau, TCP introduit des algorithmes de contrôle de congestion
- Le mécanisme de back-off a été ajouté à la suite de l’épisode de congestion collapse survenu en 1986
Exemples de serveur TCP et de serveur HTTP
- Un serveur echo TCP de base écrit en C reçoit les entrées du client puis les renvoie en y ajoutant « you sent: »
- Il utilise l’API des sockets Berkeley avec
socket(), bind(), listen(), accept(), send(), recv()
- Quand le serveur est dans
sleep(), les données du client attendent dans le buffer de réception avant d’être traitées séquentiellement
- Dans un exemple simple de serveur HTTP/1.1, le serveur accepte les requêtes via une connexion TCP, puis envoie les en-têtes
HTTP/1.1 200 OK et le corps de la réponse
- Il compte le nombre de requêtes avec
i, et une requête curl localhost:8080 affiche une réponse de la forme « [1] Yo, I am a legit web server »
Structure d’un segment TCP et champs essentiels
- Un segment TCP se compose notamment du port source, du port destination, du numéro de séquence, du numéro d’ACK, de la taille de fenêtre, du checksum et des flags
- Les ports sont alloués sur 16 bits chacun, ce qui permet d’utiliser jusqu’à 64K ports
- Une connexion est identifiée par le 5-tuple
(protocole, IP source, port source, IP destination, port destination)
- Le numéro de séquence indique la plage d’octets transmise, et le numéro d’ACK les octets reçus avec succès
- S’il manque des données, l’ACK s’arrête à cet endroit, puis est mis à jour sous forme d’ACK cumulatif après retransmission
- Les bits de flag contrôlent l’état de la connexion
SYN/ACK établissent la connexion via le 3-way handshake
FIN termine la connexion via le 4-way handshake
RST coupe immédiatement la connexion en cas d’arrêt anormal ou d’erreur
- Le champ window indique la quantité de données pouvant être reçue, et la commande
ss permet de vérifier l’état des buffers (rb131072, tb16384)
- Le checksum détecte les erreurs en additionnant les unités de 16 bits dans le segment
Conclusion
- TCP garantit la fiabilité, l’ordre et l’intégrité, et permet aux applications de fonctionner correctement même sur un Internet instable
- Il y a plusieurs décennies, transmettre quelques Ko relevait déjà du défi, alors qu’aujourd’hui le streaming 4K est devenu banal
- La finesse de conception et d’implémentation de TCP, qui a rendu possible cette communication stable, constitue l’une des bases de la croissance continue d’Internet
1 commentaires
Avis Hacker News
Si l’on essaie de construire un flux de données fiable au-dessus d’une couche de datagrammes non fiable, on finit par obtenir quelque chose de très proche de TCP
Les limites initiales de TCP étaient une petite taille de fenêtre, une mauvaise gestion des paquets perdus, et le fait de ne gérer qu’un seul flux
SCTP et QUIC sont apparus pour résoudre ces problèmes
Les algorithmes de contrôle de congestion ne font pas partie du protocole lui-même, mais du code qui s’exécute aux deux extrémités de chaque connexion
Les premiers algorithmes (Reno, Vegas, etc.) étaient simples mais suffisamment efficaces, et la recherche continue depuis sur les gros buffers, les RTT élevés, l’équité, etc.
J’avais autrefois créé en JavaScript une bibliothèque permettant de contrôler la priorité et l’annulation de plusieurs téléchargements sur un seul flux
Avec un script GreaseMonkey, je faisais précharger en arrière-plan les miniatures d’un site de rencontres, avec un préchargement déclenché selon la position de défilement
Au final, cela réduisait la charge serveur tout en améliorant l’expérience utilisateur
Fait amusant, j’ai partagé ce script avec l’un de mes matchs, et nous sommes toujours ensemble aujourd’hui — c’était en quelque sorte Tinder avant Tinder
TCP fournit une sorte de circuit virtuel sur un réseau à commutation de paquets, et l’idée d’implémenter la fiabilité par réémission vient du réseau français Cylades
Un attaquant peut injecter des données n’importe où dans le réseau ou couper la connexion avec un paquet RST
Bloquer les RST via un pare-feu améliore la stabilité, mais les attaques de désynchronisation via des numéros de séquence falsifiés restent possibles
Toutes les applications doivent donc implémenter une fonctionnalité de reprise sur une connexion séparée, tout en subissant aussi le problème du slow start de TCP
De plus, l’idée même de séparer adresses et ports me paraît inefficace
Par exemple, avec DNS over TLS (DoT), on peut envoyer plusieurs requêtes en parallèle sur une seule connexion TCP et recevoir les réponses dans le désordre
C’est plus efficace et plus poli que d’ouvrir plusieurs connexions
Je ne sais pas si QUIC est plus rapide, mais le support côté serveur reste encore limité
Le pipelining HTTP/1.1 permet quelque chose de similaire, mais les réponses arrivent dans l’ordre
Pourtant, beaucoup de cours universitaires n’insistent pas sur ce point, si bien que beaucoup pensent à tort qu’il n’existe qu’un seul algorithme TCP
Je serais curieux de savoir s’il y a ici des amateurs de SCTP
SCTP est un protocole qui combine l’orientation message d’UDP avec la fiabilité de TCP, et prend en charge le multistreaming ainsi que le multihoming
Il permet de transmettre en parallèle plusieurs flux indépendants, par exemple le texte et les images d’une page web
Voir Wikipedia: Stream Control Transmission Protocol pour plus de détails
Au final, la meilleure réponse reste une couche de fiabilité au-dessus d’UDP, autrement dit QUIC
Je me demandais s’il était possible d’envoyer directement des paquets en n’utilisant qu’IP
J’avais l’impression que les routeurs intermédiaires rejetteraient les paquets qui ne sont ni TCP ni UDP
En IPv4, il suffit d’indiquer l’un des numéros 0 à 255 dans la liste des numéros de protocole IANA
Les routeurs de cœur de réseau n’inspectent pas ce champ, mais les NAT ou les équipements d’ISP peuvent le faire
Entre deux serveurs Linux, on peut même communiquer avec des numéros expérimentaux (253, 254)
Des protocoles comme IPsec, GRE et L2TP ne sont pas non plus du TCP/UDP
Dans les réseaux d’entreprise avec pare-feu ou NAT, les protocoles arbitraires peuvent être bloqués
Le NAT a brisé le principe de bout en bout, et les gens ont fini par tout faire passer au-dessus de TCP ou d’UDP, voire au-dessus de HTTP
Le seul impact est une éventuelle baisse d’entropie pour le hachage ECMP
Au final, tout dépend de la capacité du destinataire à comprendre ce protocole
Les numéros de port ne sont que des identifiants de service internes au nœud
RUDP (Plan9) était un excellent compromis entre TCP et UDP
Voir Reliable User Datagram Protocol
Le fait que TCP soit devenu la valeur par défaut a conduit à l’utiliser systématiquement, même quand on n’avait besoin ni de fiabilité ni de garantie d’ordre
Avec la diffusion de HTTP/3 (basé sur QUIC), la situation pourrait s’améliorer
Cela dit, QUIC est bien plus complexe, et sa puissance n’est utile qu’à certains
Une couche de chiffrement simple de style UDP + WireGuard pourrait être une meilleure alternative
TCP est l’une des grandes inventions de l’humanité, mais il n’avait pas anticipé la domination des réseaux semi-connectés (basés sur le NAT)
les ingénieurs de l’époque auraient sans doute demandé pourquoi complexifier les choses à ce point
Au final, l’architecture de liaisons asymétriques actuelle et la distinction client-serveur découlent de cette logique
Les algorithmes de contrôle de congestion de TCP ont des effets intéressants que les développeurs connaissent mal
Lorsqu’on envoie des données sur une nouvelle connexion, la transmission initiale est lente, et la montée en débit est déterminée par la latence
Dans les datacenters, réduire le RTT de quelques microsecondes peut suffire à obtenir un gain de débit important
La plupart des piles TCP calculent cette montée non pas en octets mais par segment, donc avec des jumbo frames, on peut monter 6 fois plus vite
C’est pour cela qu’AWS investit beaucoup dans une faible latence de commutation et la prise en charge des jumbo frames
Les experts font ce type d’optimisation, mais la plupart des gens se demandent simplement pourquoi une liaison à 10 Gbit/s n’atteint pas 10 Gbit/s
Construire son propre protocole au-dessus d’IP était très simple
Il y a encore 15 ans, on pouvait expérimenter en Python en assemblant soi-même les paquets