19 points par GN⁺ 2025-11-16 | 1 commentaires | Partager sur WhatsApp
  • 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

 
GN⁺ 2025-11-16
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.

    • Je pense aussi que les développeurs web portent une part de responsabilité pour ne pas avoir bien exploité les flux multiples
      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
    • À l’époque où TCP a été conçu, la vision des réseaux était encore dominée par la commutation de circuits (circuit switching) issue du réseau téléphonique
      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
    • L’un des défauts fondamentaux de TCP est qu’il est impossible à sécuriser
      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
    • Certaines applications fonctionnent très bien avec une seule connexion TCP
      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
    • Le fait que les algorithmes de contrôle de congestion de TCP soient extérieurs au protocole était très novateur pour l’époque
      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

    • SCTP ne résout que la moitié du problème et introduit plusieurs nouveaux défauts
      Au final, la meilleure réponse reste une couche de fiabilité au-dessus d’UDP, autrement dit QUIC
    • En tant qu’utilisateur assidu de BSD et développeur en Erlang, j’adore SCTP
  • 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

    • On peut forger et envoyer directement des paquets IP
      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)
    • Il ne faut pas oublier ICMP. C’est encore plus important en IPv6
      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
    • Sans NAT ni répartiteur de charge, il n’y a pas de problème, mais comme ils sont aujourd’hui courants, IPv6 peut être préférable
    • Les routeurs sont des équipements de couche 3, donc ils ne se soucient pas de savoir s’il s’agit de TCP ou d’UDP
      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
    • TCP et UDP ne sont que des charges utiles IP, et les routeurs ne les interprètent pas
      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)

    • Quelqu’un demande : « Tu parles du NAT ? »
    • Si l’on retournait en 1981 pour dire : « Au lieu d’adresses globales, utilisons des adresses dans une plage limitée, et rendons certains nœuds inaccessibles »,
      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