Parler TCP à la main
Dialoguer avec netcat et voir un protocole brut.
Objectifs
À la fin de cette leçon, vous saurez :
- créer un canal TCP brut entre deux processus avec netcat ;
- comprendre qu'un protocole applicatif n'est « que » du texte convenu sur ce canal ;
- parler HTTP à la main à un vrai serveur web — et lire la réponse brute ;
- préparer la lecture détaillée du protocole HTTP (chapitre suivant).
🔗 Pour vous rafraîchir la mémoire : TCP, UDP et sockets · observer les ports et les connexions
Le dévoilement : un protocole n'est pas magique
Depuis le début du cursus, HTTP est une boîte noire : le navigateur « parle au serveur » et des pages arrivent. Cette leçon ouvre la boîte. L'objectif pédagogique tient en une phrase :
Un protocole applicatif = une CONVENTION DE TEXTE (ou d'octets) que deux programmes s'échangent sur un flux TCP.
Si vous savez produire ce texte à la main, vous comprenez le protocole. Netcat (nc) est l'outil qui rend cela possible : il crée des sockets brutes, sans interprétation.
Étape 1 — Un canal entre deux terminaux
Terminal 1, on se met en écoute :
nc -l 9000
nc -l crée une socket en écoute sur le port 9000 — vérifiez avec ss -lntp | grep 9000, vous reconnaîtrez la ligne LISTEN.
Terminal 2, on s'y connecte :
nc localhost 9000
bonjour
Tapez bonjour + Entrée : le mot apparaît dans le terminal 1. Tapez une réponse côté terminal 1 : elle apparaît dans le terminal 2. Vous venez de construire une conversation TCP complète :
terminal 2 ── flux d'octets bidirectionnel ── terminal 1
(via 127.0.0.1:port_éphémère ↔ 127.0.0.1:9000)
Ce que cet exercice révèle, c'est l'essence même de TCP : un canal d'octets bidirectionnel entre deux processus. Rien de plus. Tout protocole — HTTP, SSH, SMTP — est une convention sur ce que l'on écrit dans ce canal. Ctrl+C ferme la session ; relancez les deux côtés pour rejouer.
Étape 2 — Parler HTTP à la main
Connectons-nous à un vrai serveur web public :
nc example.com 80
Puis tapez EXACTEMENT ces trois lignes (la troisième est vide) :
GET / HTTP/1.1
Host: example.com
Et voici ce qui revient (extrait réel) :
HTTP/1.1 200 OK
Age: 234567
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Mon, 12 Jan 2026 09:14:00 GMT
Content-Length: 1256
<!doctype html>
<html>
<head>
<title>Example Domain</title>
...
Prenez une seconde pour réaliser ce qui vient de se passer : vous avez fait le travail du navigateur, à la main. La réponse contient :
- une ligne de statut :
HTTP/1.1 200 OK; - des en-têtes :
Content-Type,Content-Length,Date... ; - une ligne VIDE (séparateur obligatoire) ;
- le corps HTML.
Ce que cette expérience enseigne, point par point :
- Le port 80 est celui où « parle » HTTP par convention — notre connexion y est allée directement ;
- Le protocole est du texte lisible par un humain — conçu ainsi volontairement, pour être débogable ;
- L'en-tête
Host:est indispensable depuis HTTP/1.1 : il dit AU SERVEUR quel site vous demandez — car plusieurs domaines cohabitent souvent derrière la même adresse IP ; - La ligne vide marque la frontière entre en-têtes et corps : une convention de découpage, comme nos canaux stdin/stdout du chapitre 1.
Étape 3 — Expérimenter : briser les conventions
Le meilleur moyen de comprendre une convention est de la violer. Trois expériences à faire :
Sans l'en-tête Host :
GET / HTTP/1.1
Réponse typique :
HTTP/1.1 400 Bad Request
...
<h1>Bad Request</h1>
<p>An error occurred while parsing your request.</p>
Le serveur refuse poliment : sans Host, il ne sait pas QUEL site servir. Vous venez de provoquer votre première erreur HTTP intentionnellement — chapitre suivant, ces codes deviendront votre vocabulaire.
Avec une méthode inexistante :
FOO / HTTP/1.1
Host: example.com
Réponse : 501 Not Implemented (ou 405 selon le serveur). Même logique : la convention définit les méthodes autorisées ; hors convention, erreur explicite.
Observer la connexion pendant l'échange : dans un troisième terminal, pendant que nc est connecté :
ss -tnp | grep ":80"
ESTAB 0 0 192.168.65.3:45678 93.184.216.34:80 users:(("nc",pid=4321))
Votre quadruplet en direct : port éphémère local ↔ port 80 distant. Et quand vous quittez nc, la connexion disparaît de la liste — le FIN de fermeture propre a circulé.
Pourquoi ça compte pour la suite
Trois raisons concrètes :
- Débogage : quand curl « ne marche pas », pouvoir reproduire l'échange octet par octet isole le fautif — le protocole, le réseau ou le client ;
- Compréhension durable : HTTP cessera d'être une incantation (« le navigateur gère ») pour devenir un dialogue textuel que vous pouvez lire, écrire et prédire ;
- Transition directe : la prochaine partie décortique précisément ce que vous venez de voir — requêtes, réponses, méthodes, statuts, en-têtes, corps. Vous arrivez avec l'expérience en mémoire, elle collera d'autant mieux.
Exercice
- Faites dialoguer deux netcat locaux ; pendant la conversation, retrouvez la connexion avec
ss -tnpet notez son quadruplet complet. - Interrogez
example.comà la main. Notez : code de statut, valeur deContent-Type, taille du corps (Content-Length). - Refaites la requête sans l'en-tête
Host. Notez le code obtenu. - Demandez un chemin inexistant :
GET /nimporte-quoi HTTP/1.1. Quel statut ? Que contient le corps ? - Question de réflexion : pourquoi les concepteurs d'HTTP ont-ils choisi du texte lisible plutôt qu'un format binaire compact ?
Résumé
- Une socket TCP = un flux d'octets bidirectionnel ; tout protocole applicatif est une convention dessus.
nc -l <port>/nc hôte port: fabriquer et joindre un canal brut.- HTTP = lignes de texte : requête (méthode, chemin, version, en-têtes), réponse (statut, en-têtes, ligne vide, corps).
Hostobligatoire depuis HTTP/1.1 : un serveur héberge plusieurs sites derrière une seule adresse.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Question 1.
ESTAB 0 0 127.0.0.1:9000 127.0.0.1:48211 users:(("nc",pid=...))
Quadruplet : destination fixe (9000, l'écoute) ↔ source éphémère (48211, choisie par le noyau au moment du connect). Les deux adresses sont 127.0.0.1 : boucle locale, aucun câble.
Question 2. Valeurs observées type :
Statut : 200 OK
Content-Type : text/html; charset=UTF-8
Content-Length : 1256
Ces trois informations suffisent à prédire l'affichage : succès, document HTML encodé UTF-8, 1256 octets attendus — si le navigateur n'en reçoit que 1000, il sait que quelque chose s'est mal passé. C'est le rôle des en-têtes : annoncer le contrat avant le contenu.
Question 3. 400 Bad Request (ou parfois redirection vers une page d'erreur selon le serveur). Depuis HTTP/1.1, Host est obligatoire : l'hébergement mutualisé (plusieurs domaines sur une adresse) en dépend entièrement. Vous avez vécu la raison : sans nom, le serveur ne peut pas choisir.
Question 4. Statut 404 Not Found, avec un corps HTML d'erreur (page « not found » du site). Remarquez que la requête était PARFAITEMENT valide syntaxiquement : le serveur l'a comprise, cherché, et répond proprement « cette ressource n'existe pas ». Comparez avec le 400 de la question 3 : là, la REQUÊTE était invalide. 4xx = faute du client ; 400 syntaxe, 404 ressource absente — nuance fondamentale du chapitre suivant.
Question 5. Réponse attendue (formulations libres) : « parce que le texte est débogable par un humain : comme nous venons de le faire, n'importe qui peut diagnostiquer un échange avec un simple terminal, sans outil spécialisé. En 1991-1996 (création d'HTTP), la lisibilité primait sur quelques octets d'overhead. Le monde moderne ajoute la compression (gzip) SUR le texte pour récupérer la place, et HTTP/3 passe au binaire — mais uniquement une fois le protocole stabilisé et compris. » C'est un pattern général d'ingénierie : commencer lisible, optimiser ensuite.