TCP — il protocollo che garantisce tutto
HTTP, HTTPS, SSH, FTP, SMTP: tutti usano TCP. Non è un caso. TCP è il protocollo di trasporto che garantisce che i dati arrivino a destinazione integri, ordinati e senza duplicati, indipendentemente da quello che succede nella rete sottostante. È la fondamenta su cui è costruita la maggior parte di Internet.
Ma questa garanzia ha un costo: TCP è complesso, ha un header minimo di 20 byte, richiede una connessione esplicita e mantiene stato su entrambi gli host. Capire come funziona internamente ti permette di capire perché alcune applicazioni sono lente, perché certi firewall si comportano in modo strano, e come i moderni protocolli come QUIC cercano di migliorarlo.
Le sei caratteristiche fondamentali
Prima di trasferire dati, TCP stabilisce una connessione con un handshake a tre vie (three-way handshake). Solo dopo che entrambi gli host hanno concordato parametri e numeri iniziali di sequenza inizia il trasferimento dati.
TCP garantisce che ogni byte trasmesso venga ricevuto correttamente tramite ACK (acknowledgment), ritrasmissione in caso di perdita e riordinamento di segmenti arrivati fuori sequenza.
Dati scorrono in entrambe le direzioni simultaneamente sulla stessa connessione. Ogni lato ha il suo Sequence Number indipendente. ACK e dati possono essere trasportati insieme nello stesso segmento (piggybacking).
TCP non conosce i confini dei messaggi applicativi. L’applicazione invia un flusso continuo di byte; TCP li segmenta come ritiene opportuno. Il ricevitore riassembla il flusso nell’ordine corretto. Contrasto con UDP che è message-oriented.
Previene il buffer overflow del ricevitore. Il ricevitore comunica quanto spazio ha disponibile tramite il campo Window Size nell’header. Il mittente non può inviare più byte di quanti il ricevitore ne possa accettare.
Previene il collasso della rete. TCP adatta la velocità di trasmissione allo stato della rete tramite algoritmi (Slow Start, AIMD, CUBIC, BBR). Nessuna applicazione può inviare a piena velocità se la rete è congestionata.
La struttura dell’header TCP
L’header TCP ha una dimensione minima di 20 byte (5 parole da 32 bit), espandibile fino a 60 byte con le opzioni. Ogni campo ha un ruolo preciso nel meccanismo di consegna affidabile.
Campi dell’header — analisi dettagliata
| Campo | Dimensione | Ruolo |
|---|---|---|
| Source Port | 16 bit | Porta del processo mittente (client: porta effimera; server: porta well-known) |
| Destination Port | 16 bit | Porta del processo destinatario — usata per il demultiplexing |
| Sequence Number | 32 bit | Numero del primo byte del payload in questo segmento. Inizializzato con l’ISN all’apertura della connessione |
| Acknowledgment Number | 32 bit | Numero del prossimo byte atteso dal ricevitore (ACK cumulativo). Valido solo se flag ACK=1 |
| Data Offset | 4 bit | Lunghezza dell’header in parole da 32 bit. Minimo 5 (= 20 byte), massimo 15 (= 60 byte) |
| Reserved | 3 bit | Riservati per usi futuri, devono essere 0 |
| Flags (control bits) | 9 bit | CWR, ECE, URG, ACK, PSH, RST, SYN, FIN — controllano il comportamento della connessione |
| Window Size | 16 bit | Spazio disponibile nel buffer del ricevitore (in byte). Base del controllo del flusso |
| Checksum | 16 bit | Calcolato su pseudo-header IP + header TCP + dati. Obbligatorio (a differenza di UDP) |
| Urgent Pointer | 16 bit | Offset fino ai dati urgenti. Usato raramente; valido solo se flag URG=1 |
| Options | 0–40 byte | Opzioni variabili — estendono le funzionalità base di TCP (vedi sezione dedicata) |
I flag TCP — il linguaggio della connessione
I 9 bit di flag sono lo strumento con cui TCP comunica lo stato della connessione. Ogni flag attivo (=1) ha un significato preciso:
Synchronize — apertura connessione. Presente nel primo e nel secondo messaggio del three-way handshake. SYN occupa 1 numero di sequenza.
Acknowledgment — il campo Ack Number è valido. Presente in quasi tutti i segmenti TCP tranne il primo SYN.
Finish — chiusura graziosa della connessione. Il lato che invia FIN non trasmetterà più dati (può ancora ricevere). FIN occupa 1 numero di sequenza.
Reset — chiusura immediata e anomala. Nessun graceful close. Usato quando la connessione è in uno stato inconsistente o per rifiutare una connessione.
Push — richiede al ricevitore di consegnare i dati all’applicazione immediatamente senza aspettare il riempimento del buffer.
URG = dati urgenti. CWR/ECE = Explicit Congestion Notification (RFC 3168). Raramente usati nelle implementazioni comuni.
Sequence Number e ACK — il cuore dell’affidabilità
I campi Sequence Number e Acknowledgment Number sono il meccanismo centrale di TCP. Vale la pena capirli a fondo.
L’ISN (Initial Sequence Number) è il numero di partenza, scelto pseudocasualmente all’apertura della connessione (non parte da 0). Il motivo è la sicurezza: un ISN prevedibile consentirebbe attacchi di IP spoofing e session hijacking.
Window Size — il controllo del flusso in un campo
Il campo Window Size (16 bit) comunica al mittente quanti byte il ricevitore può ancora accettare nel suo buffer di ricezione. Il mittente non può inviare più dati di quanti indicati dalla window:
Con Window Size a 16 bit, il massimo è 65535 byte. Su una connessione con RTT = 100ms e banda 1 Gbps, il BDP (Bandwidth-Delay Product) è circa 12,5 MB. Con una window di soli 64 KB, il mittente sarebbe costretto ad aspettare gli ACK ogni 64 KB, sprecando la banda disponibile. La soluzione è l’opzione Window Scale (RFC 7323): un moltiplicatore fino a 2¹⁴ = 16384 × che porta la window effettiva fino a ~1 GB.
Le opzioni TCP principali
Le opzioni TCP estendono l’header base aggiungendo funzionalità negoziabili nell’handshake:
| Opzione | RFC | Funzione |
|---|---|---|
| MSS (Maximum Segment Size) | RFC 793 | Dimensione massima del payload TCP che il mittente è disposto a ricevere. Negoziato nell’SYN. Evita la frammentazione IP. Default 536 byte; tipicamente 1460 byte su Ethernet (MTU 1500 – 20 IP – 20 TCP). |
| Window Scale | RFC 7323 | Moltiplicatore per il campo Window Size. Permette window effettive fino a ~1 GB su reti ad alta banda e latenza elevata. |
| SACK (Selective Acknowledgment) | RFC 2018 | Il ricevitore segnala blocchi non contigui ricevuti. Il mittente ritrasmette solo i segmenti effettivamente mancanti invece di tutto dalla posizione del gap. Riduce drasticamente i dati ritrasmessi in caso di perdite multiple. |
| Timestamps | RFC 7323 | Permette una stima più precisa del RTT e protegge da sequence number wrap-around su connessioni molto veloci (PAWS — Protection Against Wrapped Sequences). |
Il checksum TCP viene calcolato su un pseudo-header che include IP sorgente, IP destinazione, protocollo e lunghezza del segmento TCP — campi che tecnicamente appartengono al livello IP. Questo viola strettamente la separazione tra livelli, ma serve a rilevare eventuali errori di consegna a indirizzi errati. È una scelta pragmatica consolidata (stessa tecnica usata da UDP).
Da RFC 793 a RFC 9293 — TCP si evolve
TCP è stato definito nel 1981 con RFC 793. Nel 2022 è stato aggiornato dall’RFC 9293 che consolida decenni di correzioni, chiarimenti e best practice in un unico documento. RFC 793 è ufficialmente obsoleto. Le implementazioni moderne (Linux, Windows, BSD) seguono RFC 9293 più tutta una serie di RFC aggiuntivi per SACK, CUBIC, BBR, TFO (TCP Fast Open) e altri.
- TCP è connection-oriented, affidabile, full-duplex, byte-stream, con controllo del flusso e della congestione
- Header minimo 20 byte: Source/Dest Port (16 bit), Sequence Number (32), Ack Number (32), Data Offset (4), Flag (9), Window Size (16), Checksum (16), Urgent Pointer (16)
- Sequence Number = numero del primo byte del payload; Ack Number = prossimo byte atteso (ACK cumulativo)
- ISN casuale per sicurezza; SYN e FIN consumano 1 numero di sequenza ciascuno
- Flag principali: SYN (apertura), ACK (conferma), FIN (chiusura), RST (reset), PSH (push immediato)
- Opzioni fondamentali: MSS (evita frammentazione), Window Scale (reti veloci), SACK (ritrasmissione selettiva), Timestamps (RTT preciso)