|
|
La guida qui riportata è tratta dalla rivista Internet News ed è stata scritta da Dario De Judicibus.
Miliardi di bit viaggiano ogni
giorno sulla Rete. Vi siete mai chiesti come fanno ad arrivare al corretto destinatario?
In questo articolo, primo di una serie vi presentiamo "la suite di protocolli
TCP/IP" cioè le regole utilizzate per la trasmissione su Internet. Non è certo
fondamentale sapere come funziona uno spinterogeno o un albero di trasmissione per guidare
un'automobile. Analogamente, al giorno d'oggi, non serve sapere come funziona un computer
per poterlo utilizzare. La tecnologia ci scherma sempre di più dal come una cosa funziona
spostando l'attenzione sul cosa fare per utilizzarla. Così, quella che era una volta
tecnologia per un'élite abbastanza ristretta di studiosi, ricercatori e studenti, è oggi
una realtà a disposizione di tutti. Non solo è diventato semplice navigare nella
Ragnatela, ma oggi chiunque può facilmente costruirsi le sue pagine e agganciarle a uno
dei tanti siti Web che ospitano pagine private. Non c'è più neanche bisogno di conoscere
l'HTML, grazie alla proliferazione di editor HTML commerciali e di pubblico dominio. In
quanto ai risultati estetici, beh, lì non c'è programma che tenga.
Ma cosa
c'è sotto a tutto ciò? Per chi è ancora e nonostante tutto interessato a capire come
funzionano le cose, e se vogliamo anche per coloro ai quali la cosa non interessa per
niente, ma hanno qualche minuto per leggere un paio di paginette e poi, chissà, potrebbe
sempre tornare utile... insomma, per chi vuole, ecco a voi il "TCP/IP, questo
sconosciuto".
Il nome
Il nome completo è TCP/IP Internet
Protocol Suite, ed è un insieme di protocolli di trasmissione di cui i due principali
sono appunto il TCP (Transmission Control Protocol) e l'IP (Internet Protocol). Ma che
cosa è esattamente un protocollo? Essenzialmente è una serie di regole per comporre dei
messaggi e per far sì che essi possano essere scambiati tra due macchine. Non stiamo
parlando solo di computer. Anche una centrale telefonica meccanica può ricadere in questa
definizione. Un protocollo può contenere regole estremamente dettagliate, come quelle che
identificano il significato di ogni singolo bit nella costruzione di un messaggio, oppure
fornire uno scenario di alto livello, come per esempio definire come avviene il
trasferimento di un file da un computer a un altro. Fondamentalmente un protocollo sta
alla trasmissione dati come un linguaggio di alto livello quale il C++ sta alla
programmazione. Infatti, un linguaggio di programmazione comprende sia regole estremamente
dettagliate che devono essere seguite alla lettera - guai a dimenticare anche un solo
punto e virgola alla fine di un'istruzione C++ - sia strutture di alto livello che vanno
costruite nel modo corretto, pena errori nella struttura logica del programma.
Una
generica architettura di trasmissione è formata da una torre a più piani, dove ogni
piano rappresenta una precisa responsabilità nella trasmissione dei messaggi. Alla base
della torre sta la porta di accesso alla rete fisica, che potremmo pensare come una rete
di strade. Ogni piano prende il messaggio che arriva dal piano superiore, lo mette in una
busta con alcune informazioni aggiuntive, e lo passa come messaggio al piano inferiore.
Le regole
di comunicazione tra i vari piani sono dette interfacce. Il messaggio risultante, formato
da tante buste una dentro l'altra, viene immesso nella rete dalla porta che si trova alla
base della torre. Una volta arrivato al piano terreno infatti, esso viene trasportato alla
torre di destinazione e da qui risale un piano dopo l'altro fino all'ultimo piano, detto
anche livello applicativo. Ogni piano della torre di destinazione apre solo la busta che
gli compete e usa le informazioni aggiuntive per recapitare la busta successiva al piano
superiore. Le informazioni aggiuntive rappresentano il protocollo di comunicazione. Ogni
piano comunica quindi solo con il piano corrispondente.
Un esempio
Il direttore della Pippo e Figli
manda una lettera riservata al direttore della Pluto e Consorte. Il modo con cui i due
comunicano, per esempio i riferimenti a lettere precedenti, lo stile della lettera, il
modo di salutare alla fine della lettera, e così via, rappresenta il protocollo ad alto
livello, cioè quello applicativo. Per spedire la lettera il direttore lo passa alla sua
segretaria. Ciò avviene secondo le regole interne della
Pippo e
figli, ed è perciò un'interfaccia. La segretaria prende la lettera, la mette in una
busta aggiungendo il nome del destinatario e la scritta RISERVATO. Queste informazioni
sono per la sua controparte nella Pluto e consorte, ed è quindi anch'esso un protocollo.
La busta viene quindi passata all'Ufficio Posta dell'edificio secondo la procedura
ordinaria (altra interfaccia), il quale aggiunge l'indirizzo completo, il CAP e altre
informazioni necessarie alla spedizione, e la passa quindi al corriere, che rappresenta il
meccanismo fisico di trasferimento del messaggio. Terzo protocollo. Quando la lettera
arriva, questa viene gestita dall'Ufficio Posta della Pluto e consorte che, dopo aver
buttato la busta esterna con l'indirizzo dell'edificio, la passa alla segreteria del
direttore. Questa registra l'arrivo della missiva, la toglie dalla busta più interna, e
poi consegna la lettera vera e propria al direttore della Pluto e consorte.
È
evidente che perché il sistema funzioni bisogna che i vari protocolli siano rispettati da
entrambe le aziende. Mentre infatti le interfacce possono essere diverse (ogni azienda ha
le sue procedure interne), i protocolli devono essere stati concordati prima, altrimenti
alcune informazioni potrebbero andare perse, o addirittura la lettera potrebbe non essere
recapitata in tempo. Pensate a una segretaria italiana che riceve una busta da una
consociata asiatica con sopra scritto URGENTE in cinese! Ne nasce una considerazione
importante. La base di ogni protocollo è il concetto di standardizzazione. Più vasta è
l'accettazione dello standard, più forte e diffuso è il protocollo. Gli standard
internazionali sono in genere i più importanti, ma non sempre. Un esempio è proprio il
TCP/IP, nato per volontà dell'agenzia americana DARPA (Defense Advanced Research Projects
Agency) e poi diventato di fatto il maggior sistema di protocolli per l'interconnessione
di reti a livello mondiale.
Internet
è fatta a strati Internet è basato su tre livelli concettuali: il livello applicativo
(Application Services), quello del trasporto (Reliable Stream Transport Service) e quello
della spedizione dei pacchetti (Connectionless Packet Delivery Service). Per capire il
TCP/IP, è necessario a questo punto capire bene che cosa è Internet. Tanto per
cominciare Internet non è una rete di comunicazione. Una rete di comunicazione è in
genere legata alle necessità specifiche di chi l'ha disegnata e dell'hardware utilizzato
per implementarla. Costruire una rete ideale che vada bene per qualsiasi esigenza, o
pensare di poter limitare a un solo tipo di hardware l'implementazione di una qualunque
rete non solo non è fattibile, ma neanche auspicabile, date le limitazioni delle
tecnologie attuali. A volte è necessario far correre i dati molto velocemente in un
ambito molto ristretto, come per esempio all'interno di un edificio. Altre volte si ha
l'esigenza di trasmettere dati a migliaia di chilometri di distanza in modo molto
affidabile, anche se questo può significare un rallentamento nella velocità di
trasmissione. Se si cercasse di utilizzare lo stesso hardware in entrambi i casi, i costi
sarebbero assolutamente inaccettabili.
La
soluzione è l'interconnessione delle reti, o internetworking. Grazie a ponti di
collegamento (detti gateway) e la definizione di opportuni protocolli, si possono
collegare fra di loro reti anche molto diverse, fornendone agli utenti una visione comune.
Questa è la forza di Internet rispetto alle varie reti proprietarie, e di conseguenza del
TCP/IP sui vari protocolli proprietari. Il TCP/IP è un insieme di regole pubbliche,
aperte a tutti, o come si dice nell'ambiente, un sistema aperto (open system), che
permette l'interconnessione di reti anche molto differenti, indipendentemente dalla
tecnologia usata da ogni rete. I suoi principali vantaggi sono appunto l'indipendenza
dalle tecnologie delle singole reti interconnesse, la possibilità di far comunicare fra
di loro ogni computer connesso al sistema, la possibilità di trasmettere conferme di
ricezione (acknowledgement) direttamente dal destinatario al mittente, e soprattutto una
notevole quantità di protocolli applicativi per qualunque possibile bisogno, come vedremo
più avanti. Il TCP/IP definisce quindi una unità di trasmissione dati chiamata datagram,
e le regole da seguire per trasmettere un datagram in una particolare rete.
Il
principio che sta alla base dell'interconnessione è quello di schermare le applicazioni
dalle caratteristiche fisiche delle reti in modo semplice e flessibile. Questo avviene
attraverso un livello intermedio che si occupa di spedire e ricevere piccoli pacchetti di
dati fra due punti qualsiasi del sistema di reti. Questo meccanismo si chiama
packet-switching. Esso consiste nella divisione di ogni messaggio in un certo numero di
pacchetti di dati. Ogni pacchetto è formato da poche centinaia di byte, e contiene una
intestazione che fornisce informazioni sul destinatario e su come raggiungerlo. Questo
meccanismo ha il vantaggio di ottimizzare l'utilizzo della rete, parallelizzando la
trasmissione di più messaggi contemporaneamente. Lo svantaggio è che ogni nuovo sistema
che si aggancia alla rete per trasferire dati riduce la disponibilità della rete per
tutti gli altri sistemi già connessi. Una rete infatti ha una certa capacità ben
definita, che dipende sostanzialmente dalla tecnologia hardware e software che utilizza.
Tale capacità viene misurata in bit per second (bps). Questa grandezza non rappresenta la
velocità dei dati in rete, come si potrebbe pensare in prima istanza, bensì dà una
misura del numero massimo di bit che possono essere trasmessi nella rete in un secondo. La
velocità reale di un singolo messaggio dipende da tanti fattori, come il numero di
sistemi che stanno utilizzando la rete, la qualità delle connessioni e di conseguenza il
numero di tentativi necessari per trasferire correttamente i dati, le modalità di
trasmissione e i dati aggiuntivi necessari al trasferimento degli stessi.
Ci sono
altri modi per trasferire dati in una rete: per esempio, quando fate una telefonata, la
rete stabilisce un collegamento diretto fra il vostro telefono e quello della persona
chiamata. A questo punto il telefono incomincia a campionare il microfono della vostra
cornetta in modo continuo, trasferendo il segnare al ricevitore all'altro capo. Il tutto a
64.000 bit per secondo, che è la velocità di campionamento necessaria a digitalizzare la
voce. Questo avviene comunque, indipendentemente dal fatto che stiate parlando o meno.
Anche se state in silenzio la linea è saturata al massimo della sua capacità. Questo
meccanismo è detto circuit-switching. Al contrario del meccanismo usato dal TCP/IP,
quello cioè a pacchetti, la linea è completamente assegnata alla comunicazione in atto,
per cui il fatto che altri stiano telefonando non riduce la capacità della connessione.
D'altra parte la linea è utilizzata completamente indipendentemente dal fatto che ci
siano o meno dati da trasferire. Di qui gli elevati costi di tale meccanismo. La
telefonata, infatti, la pagate lo stesso sia che parliate molto velocemente, sia che
stiate completamente in silenzio. Questo meccanismo è troppo costoso per una rete
informatica, specialmente se si tiene conto che la disponibilità di tecnologie hardware
sempre più raffinate e veloci per il trasferimento dei dati bilanciano in buona parte
quello che è uno dei punti deboli del sistema a pacchetti, e cioè l'impossibilità di
garantire a ogni utente e in ogni momento una certa capacità di trasferimento ben
definita.
Torniamo al sistema a pacchetti.
Per trasferire dati da un sistema a un altro ogni sistema ha un nome unico ben definito.
Non esistono cioè due sistemi con lo stesso nome, anche se in reti diverse,
indipendentemente da quale è il nome locale di un sistema nella sua rete di appartenenza.
All'interno di ciascuna rete, i vari computer usano la tecnologia hardware e software
specifica di quella rete. Tuttavia, grazie a questo strato intermedio di software, le
varie applicazioni hanno una visione unica e globale del sistema interconnesso di reti,
detto appunto internet. Notate la "i" minuscola. Il concetto di internet è
infatti quello appena descritto. Viceversa Internet con la "I" maiuscola,
identifica quel sistema di reti, basato sull'architettura internet, che viene detto anche
Connected Internet.
La
connessione tra due reti avviene attraverso macchine opportune che sono collegate
fisicamente a entrambe le reti, e hanno la responsabilità di far passare i vari pacchetti
da una rete all'altra e viceversa. Tali macchine sono dette internet gateway, o anche IP
router. Sono loro il vero elemento portante di una internet. Ogni router non solo deve
sapere che determinati pacchetti vanno passati da una rete a un'altra, ma deve passare
dall'altra parte anche pacchetti destinati a ulteriori reti connesse attraverso altri
router. Essi però ragionano solo in termini di reti, non di destinazione finale. A un
router non interessa chi è effettivamente il destinatario di un pacchetto, ma solo a
quale rete appartiene. Questo semplifica molto l'implementazione di un router. Alla base
del meccanismo dei router c'è l'indirizzo IP, o IP address.
Ogni cosa
che conosciamo ha un nome. Cane, casa, auto, e via dicendo. Se ci interessa specificare
meglio ciò di cui stiamo parlando, possiamo assegnare un nome anche a un sottogruppo di
cose. Così abbiamo che i cani bassotti sono alquanto diversi dai San Bernardo, una
catapecchia non è certo una villa, e una Ferrari costa un po' più di una Cinquecento. Se
poi dobbiamo identificare una cosa in modo chiaro e univoco, è necessario assegnarle un
nome che solo quella cosa ha. Già un nome come Mario Rossi non va bene, perché non è
unico, e comunque, anche se scegliessimo oggi un nome veramente strano e originale, non
avremmo la garanzia in futuro di non ritrovarci con un caso di omonimia. Ecco allora le
targhe per le automobili, i codici fiscali per le persone, i numeri di telefono, e via
dicendo. Ognuno di questi nomi ha tre caratteristiche. La prima è che esiste un organo
competente centrale che li assegna, proprio per garantirne l'univocità. La seconda, è
che hanno una struttura a sottogruppi. Esistono cioè degli elementi che garantiscono
l'univocità a un certo livello, all'interno del quale esiste una certa libertà di
scelta, e così via, livello dopo livello. Per esempio, il codice fiscale viene costruito
in modo che un uomo e una donna non possano mai avere lo stesso codice, anche se fossero
nati lo stesso giorno, nella stessa città e si chiamassero nello stesso modo. Similmente,
i numeri di telefono di due città diverse si distinguono per il prefisso e se queste si
trovano anche in stati diversi, per il prefisso internazionale.
Affinché
internet possa rappresentare un sistema universale di comunicazione, permetta cioè di far
comunicare qualunque macchina connessa a una delle sue reti con una qualsivoglia altra
macchina connessa alla stessa o a un'altra rete, è necessario fornire ogni macchina di un
nome unico a livello globale. Internet fornisce ogni sistema di un nome, che identifica il
sistema stesso, di un indirizzo, che mi dice dove si trova il sistema, e di un cammino,
che mi dice come raggiungere il sistema. Ogni macchina connessa a una rete è detta host,
nella terminologia internet. Lo stesso termine ha significati differenti in altri contesti
informatici, come per esempio in quello client/server, o nel caso di mainframe. Attenzione
a non fare confusione quindi. In internet un host può essere anche un vecchio 8088 con
640K di RAM e 10M di disco fisso.
Gli indirizzi IP
L'indirizzo, o IP address, è un
campo composto da 32 bit. I primi bit permettono di distinguere 5 forme standard
identificate da una lettera del alfabeto, e dette classi. Le prime tre classi dell'IP
address contengono sia l'indirizzo di una rete (netid), sia quello di una macchina nella
stessa (hostid). In realtà l'indirizzo non identifica necessariamente una macchina, ma
una connessione alla rete. Per esempio, un router ha almeno due indirizzi, avendo
connessioni ad almeno due reti. Questo in quanto un router appartiene a entrambe le reti,
e quindi sono necessari due indirizzi dato che un IP address ha posto per un solo
indirizzo di rete. Se l'indirizzo dell'host è 0, allora l'IP address si riferisce alla
rete stessa. Se viceversa tutti i bit riservati all'indirizzo dell'host sono 1, allora
l'indirizzo viene utilizzato per identificare tutti gli host della rete (broadcasting).
Uno speciale indirizzo formato da 32 bit posti a uno è chiamato local network broadcast
address e serve solo in casi molto particolari. Il concetto di broadcasting è quello
della diffusione a tutto raggio, un po' come fa un'emittente radiofonica. In generale
internet interpreta i campi formati da tutti uno come all, cioè "tutti", mentre
quelli formati da tutti zero come this, cioè "questo", "qui". Questo
per quanto riguarda le classi A, B e C. La classe D è usata per un particolare tipo di
distribuzione dei dati detto multicasting. La classe E è riservata a usi futuri. Dato che
specificare ogni singolo bit di un indirizzo IP sarebbe alquanto poco pratico e di scarsa
leggibilità, la convenzione è quella di leggere ogni ottetto, cioè ogni gruppo di 8
bit, come un intero, e di separare i quattro ottetti con un punto. Oltre a i casi speciali
già descritti, l'indirizzo di classe A 127.0.0.0 è riservato per un particolare processo
di test che rimanda indietro i dati al mittente senza propagarli nella rete.
Uno dei
vantaggi di questo schema è la possibilità da parte dell'organismo centrale che assegna
gli indirizzi (Network Information Center) di delegare ai responsabili delle singole reti
l'assegnazione di una parte dell'indirizzo all'interno della rete stessa. La cosa avviene
un poco come con i numeri di telefono. A livello internazionale ogni stato ha il suo
prefisso internazionale. Per esempio, per l'Italia, è 39. All'interno ogni stato divide
il paese in aree geografiche a cui assegna un ulteriore codice. Per esempio, Roma è
identificata dal 6, Milano dal 2, Firenze da 55, e così via. All'interno poi della
provincia o della città possono essere definite ulteriormente sottoaree a cui si
assegnano due, tre o quattro cifre. Per esempio 529 oppure 7054. Infine ogni telefono in
tali aree avrà il suo numero. Così, se Mr. Smith deve chiamare dagli Stati Uniti il
signor Mario Rossi abitante all'EUR, a Roma, comporrà per esempio il numero
011.39.6.529.4467. In questo caso lo 011 serve per uscire dagli USA, un po' come il nostro
00.
Analogamente
in internet i numeri di classe C sono assegnati alla piccole reti, quelle cioè con meno
di 256 host, quelli di classe B alle reti con al massimo 65536 host, e quelli di classe A
alle reti con oltre 16 milioni di host. Ogni rete decide poi come suddividere gli
indirizzi che gli sono stati riservati al suo interno come meglio crede. Ovviamente, una
internet privata non ha la necessità di seguire queste regole, né a utilizzare indirizzi
assegnati dal NIC, ma il non farlo potrebbe impedire in futuro la connessione alla TCP/IP
Internet.
Dato che
l'indirizzo può essere a volte abbastanza ostico da ricordare, è possibili associare a
ogni host anche un nome, che può essere utilizzato come mnemonico per un IP address, e la
cui risoluzione è responsabilità di particolari macchine chiamate name server. In
realtà il name server è un programma software che può girare in qualunque macchina
connessa alla rete, e che mantiene l'associazione tra nomi e indirizzi IP, fornendo tali
corrispondenze quando richiesto da un altro programma chiamato name resolver. Di fatto, si
preferisce far girare il name server su una macchina dedicata, che prende anch'essa, a
questo punto, il nome di name server. Potete pensare al name server come a una agenda
telefonica elettronica, che contiene una lista parziale di nomi e numeri telefonici. In
internet infatti, non esiste un singolo elenco telefonico, ma tanti name server che
cooperano per fornire quello che è un vero e proprio elenco distribuito. In realtà il
sistema funziona in modo gerarchico, un po' come se una certa agenda contenesse solo i
prefissi internazionali e il puntatore alle agende di ogni singolo stato, le quali a loro
volta contengono i prefissi regionali e i puntatori agli elenchi regionali, e così via,
fino ad arrivare all'agenda che contiene solo le estensioni telefoniche di un singolo
edificio.
Il Domain Name System
I nomi Internet sono basati su una
serie di regole dette Domain Name System (DNS), che si basa appunto su uno schema
gerarchico in cui il nome è suddiviso in varie parti separate fra loro da punti. Per
esempio, vnet.ibm.com. Ogni suffisso è a sua volta un dominio. Quindi, nel nostro
esempio, ibm.com è un dominio di secondo livello, mentre com è un dominio di terzo
livello. I domini ufficiali riconosciuti dal NIC al livello più elevato sono riportati in
tabella 1. Una rete può richiedere di essere registrata come categoria, oppure usando il
dominio geografico. Per esempio, l'Italia ha come dominio base it. Supponiamo che il
governo decida di costruire un insieme di reti cittadine interconesse fra loro e connesse
a Internet. Si può pensare di assegnare a ogni provincia un dominio xxxxxx.it. Per
esempio, Firenze avrebbe come dominio firenze.it. L'università di Firenze potrebbe
registrare la sue rete come unifi.edu, e in tal caso sarebbe direttamente il NIC a dover
dare l'autorizzazione per tale nome, essendo il dominio edu sotto responsabilità
dell'organismo centrale di controllo, oppure potrebbe decidere di far parte del dominio
cittadino, come unifi.firenze.it, e quindi potrebbe richiedere il permesso di registrare
tale nome direttamente all'amministratore del dominio di Firenze. A questo punto, se il
dipartimento di Fisica di Arcetri vuole registrare un proprio dominio, deve chiederlo solo
all'Università stessa, ricevendo così, per esempio, arcetri.usf.fi.it oppure
fisica.usf.fi.it.
Esiste una
piccola complicazione. Ogni oggetto connesso alla rete ha un tipo. Oggetti di tipo diverso
possono avere lo stesso nome. Per cui, per poter risolvere un nome e ottenere indietro
l'indirizzo IP, è necessario anche specificare il tipo di oggetto: macchina, utente,
casella postale, e via dicendo. Dal solo nome non è possibile evincere il tipo di
oggetto.
Il DNS
definisce anche come associare i nomi agli indirizzi IP, e come ottenere quest'ultimi dal
nome. In realtà lo schema è ancora più generale di quanto può sembrare, in quanto
permette di estendere la sintassi del nome per usi specifici, sfruttando anche il concetto
di tipo. Per esempio, nel caso di una casella postale (tipo MX), il nome sarà del tipo
utente@dominio.
Per
esempio ddejudicibus@tecnet.it
Innanzi tutto una internet è un
sistema di interconnessione fra reti differenti che utilizza sia sistemi dedicati per la
connessione, detti gateway, sia uno strato (layer) di protocolli che mostrano alle
applicazioni una visione omogenea di una rete virtuale e che sono basati sulla
trasmissione di piccoli pacchetti di dati. Ogni pacchetto porta con sé l'indirizzo del
destinatario il quale identifica univocamente sia la rete di destinazione che la
connessione alla quale deve essere recapitato il pacchetto. Un sistema connesso a più
reti della stessa internet avrà quindi più indirizzi IP. Un opportuno software, spesso
installato su macchine dedicate, permette di associare a ogni indirizzo un nome di più
facile utilizzo da parte degli utenti del sistema. Il formato di questo nome si basa su un
insieme di regole dette DNS. Quella che è universalmente conosciuta come Internet è di
fatto la principale rete interconnessa esistente, che si estende praticamente su tutto il
pianeta.
Data
questa premessa, vediamo di approfondire la trattazione dei protocolli TCP/IP. Innanzi
tutto qualunque trasferimento di dati implica la trasmissione di bit da un sistema a un
altro. Tali dati devono essere correttamente interpretati dai vari sistemi connessi alla
rete. Data l'enorme varietà di hardware e di sistemi operativi questo è tutt'altro che
banale. Nei protocolli di trasmissione i bit vengono convenzionalmente raggruppati per
multipli di otto, detti ottetti. Una volta questo corrispondeva al bus da 8 bit, cioè un
byte, tipico dei computer. Oggi la maggior parte dei computer usa parole di almeno 32 bit.
Tuttavia non tutte le macchine memorizzano tali parole nello stesso modo. Esistono vari
modi per memorizzare un intero rappresentato da 32 bit. In quello detto Little Endian, la
posizione più bassa in memoria contiene il byte di ordine più basso dell'intero. Nei
sistemi Big Endian avviene esattamente il contrario, cioè la posizione più bassa in
memoria contiene il byte di ordine più elevato. In altri sistemi ancora il raggruppamento
viene fatto con parole da 16 bit, in cui la parola meno significativa viene appunto prima.
Il risultato è lo stesso del Little Endian ma con i byte invertiti all'interno di ogni
singola parola. È evidente che non è pensabile che sia la rete a gestire tutti questi
modi diversi di interpretare i dati, anche perché di solito i protocolli di trasmissione
non entrano nel merito di come ragionano i singoli sistemi, ma si occupano solamente di
trasferire in modo più o meno affidabile i dati a loro affidati. Ne consegue la
necessità di definire un formato standard valido per tutti i dati che corrono lungo i
collegamenti, lasciando a i vari sistemi il compito di effettuare le opportune conversioni
locali. Lo standard internet prevede che gli interi vengano trasmessi a partire dal byte
più significativo, secondo lo stile del Big Endian.
Così in
un pacchetto, un intero ha il byte più significativo verso la testa del pacchetto e
quello meno significativo verso la coda dello stesso.
A questo
punto i sistemi sono in grado di scambiarsi i dati in modo non equivoco. Ma come fa a
sapere la rete internet che un sistema è collegato, e soprattutto, come avviene
l'associazione tra l'IP address e l'indirizzo fisico di rete? Ogni rete fisica infatti ha
un suo formato per gli indirizzi fisici assegnati alle connessioni di rete. In generale
esistono due modi di assegnare indirizzi fisici alle macchine connesse in rete. In una
rete piccola, come può essere una Token Ring, cioè un anello di un paio di centinaia di
macchine al massimo, a ogni connessione può essere assegnato un intero basso, per esempio
compreso tra 1 e 254. Questo sistema ha il vantaggio di associare l'indirizzo fisico alla
connessione piuttosto che alla scheda che permette la stessa. Per cui, se la scheda si
rompe, l'utente può cambiarla senza dover tuttavia modificare l'indirizzo fisico di rete,
purché imposti sulla nuova scheda lo stesso indirizzo di quella vecchia. Lo svantaggio è
che non esiste alcun controllo che impedisca a due utenti sulla stessa rete di impostare
lo stesso indirizzo fisico, creando così una collisione. In altri tipi di reti, come per
esempio Ethernet, ogni scheda ha già preimpostato da parte del costruttore un indirizzo
fisico fisso, per cui non c'è alcun rischio di collisione, ma cambiare la scheda vuol
dire dover necessariamente cambiare indirizzo fisico. Inoltre, dato che questo indirizzo
è unico non solo fra le schede installate su una certa rete, ma in assoluto fra tutte le
schede costruite, esso è generalmente molto lungo. Nel caso di Ethernet è di ben 48 bit.
Associare
un IP address a un sistema con indirizzi formati da piccoli numeri e per giunta tali che a
parità di connessione l'indirizzo non cambia mai, come nel caso di una rete proNET-10, è
molto semplice. Per esempio, per un IP address di classe C, si può usare l'indirizzo
fisico come host identifier. Così, se la rete ha IP address del tipo 10.214.32.x, l'host
con indirizzo fisico 16 avrà IP address 10.214.32.16. Un altro paio di maniche è gestire
indirizzi molto più lunghi dei 32 bit utilizzati per gli indirizzi internet, e per giunta
che possono cambiare nel tempo a parità di connessione. Ovviamente si potrebbe tenere da
qualche parte una tabella per gli accoppiamenti, e di fatto si fa così, ma non è certo
molto pratico pensare che qualcuno la tenga aggiornata a mano. Il problema è stato
risolto efficacemente utilizzando un meccanismo di risoluzione dinamica implementato dal
protocollo ARP, o Address Resolution Protocol.
ARP
funziona più o meno così. Quando un host deve spedire un pacchetto a un certo
destinatario, spedisce a tutti gli host nella stessa rete fisica un messaggio in cui
chiede chi è l'host con quel ben preciso IP address. Nello stesso messaggio mette anche i
propri indirizzi, sia quello fisico che quello IP. Si adopera cioè una tecnica di
broadcasting. L'host il cui IP è quello cercato, rimanda indietro al richiedente il
proprio indirizzo fisico, permettendo così l'associazione tra i due. Ciò è possibile in
quanto esso ha comunque ricevuto anche l'indirizzo fisico del mittente. Ma allora per ogni
pacchetto che va spedito a un certo IP address è necessario prima mandare un pacchetto a
tutti gli host nella rete? E perché allora non mandare direttamente il pacchetto da
trasmettere a tutti, invece di chiedere prima chi è che ha un certo indirizzo IP?
Ovviamente la cosa non funziona così, anche perché si rischierebbe di appesantire
inutilmente la rete con pacchetti che vengono recapitati ai sistemi sbagliati. Quello che
si fa è di mantenere presso ogni host una tabella con tutti gli accoppiamenti già
trovati, e di aggiornarla periodicamente per evitare che diventi obsoleta. A questo punto
i meccanismi di broadcasting servono ad aggiornare tali tabelle. Per esempio, se un host
deve spedire un pacchetto a un certo indirizzo IP, prima controlla nella sua tabella se
non ha già l'indirizzo fisico del destinatario. Solo nel caso l'informazioni manchi,
l'host spedisce a tutti gli altri host il messaggio di richiesta. Quando questo arriva a
un qualunque host, sia esso il vero destinatario o no, ogni host aggiorna la sua tabella
con l'indirizzo fisico e quello IP del mittente, tanto per guadagnare tempo. Il
destinatario, in più, spedisce indietro anche il suo indirizzo fisico al mittente, così
da potergli permettere di aggiornare la sua tabella di indirizzi. Un'ulteriore tecnica che
si usa per assicurarsi che tali tabelle siano sempre aggiornate, è quella di far
distribuire la propria
coppia di
indirizzi, fisico ed IP, ogni qual volta un sistema si connette alla rete, per esempio al
reboot.
ARP non
viene considerato propriamente un protocollo internet, quanto un meccanismo della rete
fisica. Su ARP si basa il protocollo IP per far comunicare fra loro le varie macchine
quando non è possibile risolvere in altro modo gli indirizzi IP in indirizzi fisici. Un
protocollo analogo è il RARP, o Reverse Address Resolution Protocol, con il quale una
macchina senza disco fisso (diskless) è in grado di conoscere il proprio indirizzo IP a
partire da quello fisico. Per far ciò la rete deve avere uno o più RARP Server, i quali
contengono una tabella di associazione fra gli indirizzi IP e quelli fisici di tutte le
macchine diskless. Anche questo protocollo si basa su un messaggio mandato in
broadcasting. L'esistenza di questo protocollo è legata al fatto che una macchina
diskless non può memorizzare il proprio indirizzo IP in alcun posto, non avendo memoria
secondaria.
E veniamo
ora al TCP/IP vero e proprio. Come detto prima l'architettura internet è basata su tre
livelli. L'Application Services è il livello più alto, cioè quello delle applicazioni.
I programmi che utilizzate quando usate internet ricadono in questo livello. Il Reliable
Stream Transport Service è il livello intermedio. Esso si occupa dell'affidabilità della
comunicazione, gestendo gli errori di trasmissione e la perdita di eventuali dati. Esso
inoltre fornisce una visione della comunicazione ad alto livello, in cui esiste una
connessione tra i due host che si trasmettono grandi volumi di dati. Il livello più
basso, chiamato Connectionless Packet Delivery Service è quello che effettua la
spedizione vera e propria dei singoli pacchetti, senza garantire l'affidabilità sulla
singola trasmissione, nella modalità detta connectionless.
Il
protocollo su cui si basa il livello più basso della torre internet è appunto l'Internet
Protocol, o IP. Tale protocollo si basa su alcuni concetti fondamentali. Innanzi tutto il
servizio che fornisce è detto unreliable, cioè inaffidabile, in quanto non dà alcun
garanzia che il singolo pacchetto arrivi effettivamente a destinazione. In secondo luogo
è detto connectionless, cioè senza connessione diretta, in quanto la trasmissione non
avviene direttamente verso il destinatario, ma il messaggio è lanciato nella rete
lasciando poi a questa il compito di portarlo a destinazione utilizzando l'indirizzo IP
dell'host destinatario. Infine si parla di best-effort delivery, cioè spedizione al
meglio delle possibilità, in quanto la rete fa tutto il possibile per portare comunque a
destinazione il pacchetto. In pratica l'IP si comporta come un naufrago su un'isola
deserta che lancia nella corrente un messaggio in una bottiglia per un tizio che si trova
su di un'altra isola dello stesso arcipelago, contando sul fatto che se la bottiglia
arriva sull'isola sbagliata qualcuno ributterà a mare il messaggio fintanto che non
arriverà a destinazione. Detta così c'è quasi da stupirsi che internet funzioni così
bene. Anzi, che funzioni del tutto! In realtà non dimentichiamoci che sopra al livello
più basso ce n'è un altro che garantisce appunto l'affidabilità della comunicazione.
Torniamo comunque all'IP. Esso è formato da tre regole base: come è fatto il pacchetto
da trasmettere, detto IP datagram, come avviene la scelta del cammino che il pacchetto
segue per raggiungere il destinatario, come gli host e i gateway devono trattare i
pacchetti e in particolare le modalità per l'emissione dei messaggi di errore e quelle
per la soppressione dei pacchetti. Prima però di entrare nel
dettaglio dei singoli campi, vediamo come si comporta l'IP nella gestione dei pacchetti di
dati. Questo ci permetterà più avanti di comprendere meglio il significato di alcuni
campi dell'IP datagram.
Innanzi
tutto va ricordato che l'IP è un protocollo unreliable, non dà cioè alcuna garanzia che
il singolo pacchetto arrivi effettivamente a destinazione, ed è connectionless, ovverosia
il messaggio non viene spedito direttamente al destinatario ma viene immesso nella rete
lasciando poi a questa il compito di portarlo a destinazione. Esso inoltre è di tipo
best-effort delivery, in quanto la rete fa tutto il possibile per portare comunque a
destinazione il pacchetto.
Detto
questo, vediamo come avviene la trasmissione vera e propria dei dati. L'unità fisica di
trasferimento dei dati in una rete è la frame. Questa è composta di due parti:
l'intestazione (header) e l'area dati (data area). L'unità di misura è invece l'ottetto,
formato da otto bit, cioè un byte. Ogni rete fisica ha un limite massimo di capacità di
trasferimento per un singolo frame, detto Maximum Transfer Unit (MTU). L'MTU è cioè il
massimo numero di ottetti di dati che può essere trasferito in un singolo frame. Per
esempio, Ethernet ha generalmente una MTU di 1.500 ottetti (1492 secondo lo standard IEEE
802.3). Questo vuol dire che se si devono spedire 2.000 byte di dati via Ethernet, è
necessario spezzarli in due blocchi tali che ogni blocco sia minore o uguale a 1.500. A
ogni blocco si aggiunge poi l'intestazione del frame. Dal punto di vista della rete fisica
l'IP datagram è un blocco di dati. La rete fisica ignora cioè come tali dati vengano
utilizzati dall'IP. Quindi, il primo compito di IP è quello di decidere come costruire il
datagram affinché possa essere trasmesso in un frame fisico. L'ideale sarebbe di poter
mettere un singolo datagram in ogni frame, ottimizzando così la trasmissione e
semplificando la logica. Ma quale frame? Quello della rete di partenza? Quello della rete
di arrivo? E se durante la trasmissione il datagram deve passare attraverso più reti con
MTU differenti? Il punto è che non c'è modo di fare una scelta che assicuri di avere un
datagram per frame. D'altra parte internet ha come obiettivo quello di svincolarsi dalle
caratteristiche fisiche delle varie reti interconnesse fra loro. E allora? La soluzione
adottata è molto semplice. Le dimensioni del datagram sono scelte convenzionalmente
secondo una logica del tutto indipendente dalle MTU delle singole reti fisiche,
dopodiché, a seconda della rete in
cui il
datagram deve passare, questo è spezzato in più pezzi di dimensioni inferiori alla MTU
della rete fisica, detti frammenti (fragment).
Il
datagram è anch'esso un frame, che potremmo chiamare logico per distinguerla da quello
usata da una specifica rete fisica per trasmettere i dati. Come tale anch'esso è formato
da una intestazione e da un'area dati. All'atto della frammentazione, ogni frammento viene
costruito replicando l'header del datagram, modificandone alcuni campi che vedremo in
seguito, e aggiungendo a questo un pezzo dell'area dati originaria. L'aspetto più
importante di questo meccanismo è che il riassemblaggio dei frammenti non viene
effettuato quando i vari frammenti rientrano in una rete fisica ad alto MTU, ma sempre e
comunque presso l'host di destinazione. Così, se due reti con MTU uguale a 1.500 ottetti
sono separate da una rete con MTU più bassa, per esempio 500 ottetti, i frammenti che
arriveranno a destinazione saranno di soli 500 ottetti. In questo caso la frammentazione
avviene nel primo gateway mentre il riassemblaggio avviene solo nell'host di destinazione.
Il protocollo IP richiede che sia gli host che i gateway siano capaci di gestire datagram
di almeno 576 ottetti. In aggiunta, questi ultimi devono essere capaci anche di gestire
datagram grandi quanto l'MTU più grande tra quelle delle reti a cui sono connessi.
Ricordiamo che un gateway, per definizione, ha almeno due connessioni e quindi almeno due
indirizzi IP.
Il punto
debole di questo meccanismo è che la perdita di anche un solo frammento comporta la
perdita dell'intero datagram. Dato che ogni frammento è trasmesso indipendentemente,
passare attraverso reti a bassa MTU comporta un'elevata frammentazione anche nelle reti a
maggiore MTU e comunque aumenta i rischi di perdita dei dati. Quando un frammento arriva a
destinazione, e non è detto che il primo arrivi per primo, l'host fa partire un timer. Se
questo scade prima che tutti i frammenti siano arrivati, il sistema cancella tutti i
frammenti e considera perduto il datagram. Il concetto di timer e di tempi è estremamente
importante per l'IP ed è spesso usato per ottimizzare la rete. Per esempio, ogni datagram
ha una scadenza. Se il datagram è ancora all'interno della rete quando il suo tempo è
scaduto, esso viene cancellato definitivamente. Lo scopo è quello di evitare che un
pacchetto possa restare all'infinito in internet a causa di un errore in una routing
table. Queste tabelle infatti servono a gestire il processo di instradamento del pacchetto
nella rete. Se una o più tabelle sono sbagliate, si potrebbero creare cammini chiusi in
cui i datagram potrebbero rimanere intrappolati. Veniamo finalmente al formato del
datagram. Come si è già detto esso è composto di un'intestazione e di un'area dati.
L'area dati contiene semplicemente una parte dei dati da trasmettere. Questo in quanto il
datagram è piccolo mentre l'oggetto da trasmettere può essere anche molte centinaia di
Kilobyte, se non addirittura migliaia, come per esempio un'immagine o un file compresso.
L'intestazione è invece alquanto più complessa. Vediamola in dettaglio.
I primi 4
bit contengono la versione del protocollo IP che è stato utilizzato per creare il
datagram. Infatti, come spiegato nella prima parte di questo corso, il tutto funziona se e
solo se tutti seguono le stesse regole alla lettera. D'altra parte le convenzioni, e di
conseguenza i protocolli, seguono un processo di evoluzione, per cui un datagram creato
con una versione più recente potrebbe creare problemi a un protocollo più vecchio se
questi non avesse modo di accorgersene in tempo. I 4 bit successivi danno la lunghezza
dell'intestazione misurata in parole da 32 bit. Questa è necessaria agli algoritmi usati
per leggere il datagram (parsing algorithms). Dato che i campi dell'intestazione
potrebbere non risultare un multiplo intero di 32, è necessario porre alla fine
dell'intestazione un campo di riempimento. Inoltre il programma di ricezione ha bisogno di
conoscere anche la lunghezza totale del datagram, cioè la lunghezza dell'intestazione
più quella dell'area dati. Questa è memorizzata nei bit dal 16 al 31 inclusi, e il suo
valore è espresso in ottetti, al contrario del precedente. Poichè il campo è lungo 16
bit, il datagram non può essere più grande di 216 ottetti, cioè 65.535 byte.
Il campo
tra la lunghezza dell'intestazione e quella totale del datagram identifica il tipo di
servizio che va offerto al pacchetto, ed è formato da un campo di 3 bit che specifica
l'importanza che va data al datagram, e da tre campi da 1 bit ciascuno che identificano il
tipo di trasporto desiderato per questo pacchetto. Purtroppo questo campo non può essere
sempre preso in considerazione da tutte le reti, in quanto non sempre la rete fisica è in
grado di soddisfare le richieste di priorità e trasporto memorizzate in questo campo. Per
cui esso viene considerato una sorta di raccomandazione alla rete, piuttosto che un vero
obbligo. In ogni caso il campo di priorità può contenere valori da 0 a 7. Lo zero è il
valore di base di un normale pacchetto, mentre il 7 rappresenta la richiesta di precedenza
più elevata, e va usato per i datagram che contengono dati per il controllo della rete
stessa. I tre bit relativi al tipo di trasporto servono a definire il livello di qualità
relativo al trasferimento del pacchetto. Se impostati a uno, essi richiedono
rispettivamente: di evitare al massimo ritardi nel recapitare il pacchetto al
destinatario, di fornire la massima capacità di trasferimento, e di garantire un'elevata
affidabilità durante il trasporto. Ovviamente è estremamente difficile poter fornire
tutti e tre questi servizi contemporaneamente. Spesso la rete non riesce a garantirne
neanche uno solo.
I tre
campi successivi vengono utilizzati nel meccanismo di frammentazione spiegato poco fa, e
in particolare sono quelli che permettono all'host che riceve i vari frammenti di
riassemblare il tutto per ottenere il datagram originario. Essi sono assolutamente
necessari in quanto non è prevista alcuna comunicazione tra il mittente e il destinatario
su come ricomporre il datagram, tanto più che la frammentazione finale può essere il
risultato di più frammentazioni successive. Inoltre i vari frammenti possono arrivare in
qualunque ordine, dato che possono avere seguito cammini differenti. Dulcis in fundo,
anche se l'intestazione di ogni frammento è ottenuta da quella del datagram originale, il
quarto campo dell'intestazione di un frammento contiene la sua lunghezza totale, e non
quella di tutto il datagram. Quest'ultima informazione deve essere calcolata dal
destinatario in qualche modo. Ed ecco il perché di questi tre campi.
Il primo
campo serve a identificare univocamente il datagram ed è lungo 16 bit. Tutti i frammenti
che appartengono a uno stesso datagram hanno lo stesso identificativo. Il secondo campo è
una maschera di 2 bit che controlla il meccanismo di frammentazione. Il primo bit
specifica se il datagram può essere frammentato: se impostato a uno, la frammentazione
non è permessa. Il secondo bit serve a marcare l'ultimo frammento. Vedremo tra un attimo
a cosa serve. Il terzo campo contiene la posizione dei dati del frammento nel blocco
originale di dati misurato in parole da 64 bit. Questo campo si chiama fragment offset.
Per esempio, se un frammento ha un offset 7, vuol dire che il primo bit dei suoi dati
corrisponde al quattrocentoquarantanovesimo bit dei dati del frammento originale, dato che
7 * 64 + 1 fa appunto 449. A questo punto è chiaro come si può ottenere la lunghezza
totale del datagram originario. Basta sommare l'offset e la lunghezza totale dell'ultimo
frammento, riconoscibile grazie al secondo bit del campo di controllo.
Il campo
successivo, posto a partire dal 64° bit dell'ntestazione, è lungo un byte e serve a
stabilire quanto a lungo un datagram può rimanere nella rete. È cioè il campo che
specifica la scadenza di un datagram di cui avevamo accennato in precedenza. L'idea
originaria era che tale campo contenesse il numero massimo di secondi che il pacchetto
potesse restare nella rete. Tuttavia, data l'evidente difficoltà di sincronizzare gli
orologi di tutti gli hosts e i gateway della rete, si è deciso di semplificare il
meccanismo come segue: ogni gateway che processa il pacchetto decrementa il campo di uno
quando questo arriva e memorizza il tempo di arrivo. Se il pacchetto non riparte subito ma
rimane in attesa nel gateway, il valore di questo campo viene ulteriormente decrementato
di una unità per ogni secondo di attesa. Come il campo arriva a zero, il datagram viene
cancellato dalla rete e un messaggio di errore viene rispedito al mittente.
Il campo
seguente, lungo 8 bit, identifica il protocollo di alto livello utilizzato che ha generato
i dati contenuti nel datagram, e definisce di fatto il loro formato. Ne riparleremo in
seguito, quando vedremo i protocolli applicativi.
Abbiamo
quindi un campo di controllo di 16 bit che serve a verificare l'integrità
dell'intestazione, e che utilizza il meccanismo di checksum ben conosciuto nel mondo del
software. In suo valore è la somma complementata a uno delle parole da 16 bit che
compongono l'intestazione, addizionate con il metodo del complemento a uno.
Quindi
vengono gli indirizzi IP del mittente e del destinatario, ognuno lungo 32 bit. Di tali
indirizzi e del loro scopo abbiamo parlato esaustivamente nella seconda e terza parte di
questo corso.
Per finire
abbiamo un campo di lunghezza variabile che può contenere varie opzioni, e quindi il
campo di riempimento di cui abbiamo già parlato. Queste opzioni non sono presenti in
tutti i datagram e vengono usate prevalentemente nelle verifiche e nella identificazione
dei problemi della rete. Se l’IP rappresenta il braccio
del TCP/IP, il TCP ne rappresenta la mente. Il primo si limita a spedire rapidamente i
dati che gli arrivano senza preoccuparsi troppo se qualcosa va male. Il secondo si occupa
invece di controllare che l’informazione passatagli dai livelli superiori arrivi
correttamente a destinazione. Insieme sono sicuramente una coppia molto affiatata.
In questo
articolo useremo il termine applicazioni per indicare tanto i protocolli applicativi come
FTP o SMTP, quanto i programmi applicativi veri e propri, salvo indicazione contraria.
Indicheremo inoltre con il termine utente di un servizio colui che utilizza tale servizio,
sia esso direttamente una persona, un’applicazione, o un protocollo. Per esempio, il
TCP è un utente dell’IP.
C’è
subito da dire due cose importanti sul TCP. La prima è che lo standard del TCP non
definisce né l’implementazione dello stesso, né le modalità con cui
un’applicazione accede a i servizi di questo protocollo. Esso definisce solamente le
caratteristiche di tali servizi, per cui si possono trovare molte differenti
implementazioni del TCP, ognuna con la propria interfaccia applicativa. Per chi non
programma ricordo che un’interfaccia applicativa o API (Application Programming
Interface) non è altro che l’insieme delle funzioni, delle istruzioni, dei parametri
e dei blocchi di controllo che vengono utilizzati dai programmatori per accedere ai
servizi di un sistema. Per esempio, se ho un sistema di posta elettronica potrei definire
un’API basata su due funzioni, una chiamata spedisci, e una chiamata ricevi . Per
ogni funzione sarebbero poi da definire quali informazioni sono da passare al momento
dell’utilizzo (parametri in ingresso), quali si ottengono una volta espletato il
servizio (parametri di ritorno), eventuali codici di errore, e le regole di utilizzo delle
singole funzioni. Il motivo che sta alla base della scelta di non standardizzare
l’interfaccia con il TCP è che in molti casi questo protocollo è direttamente
definito nel sistema operativo, o comunque fa parte del cosiddetto corredo di base di un
sistema, per cui si è voluto evitare di forzare una sintassi che potesse essere in
contrasto con quella nativa del sistema ospite. Il secondo punto fondamentale è che il
TCP è stato definito per funzionare su un qualsiasi sistema di trasmissione dati a
pacchetto, e non necessariamente solo sull’IP. Di fatto esso può essere poggiato,
per esempio, direttamente sopra una rete Ethernet senza bisogno di un livello Internet
intermedio.
Ma qual è
lo scopo del TCP nell’architettura internet? Il protocollo non fornisce le garanzie
di affidabilità e robustezza necessarie per implementare un sistema di trasmissione dati
sicuro e di facile gestione. L’IP è inaffidabile e benché schermi lo sviluppatore
dalla conoscenza della rete fisica, fornisce ancora una visione di livello troppo basso
del sistema di reti interconnesse. Questo vuol dire che l’IP è troppo complesso per
essere utilizzato direttamente dalle applicazioni. Per avere un protocollo di trasmissione
affidabile abbiamo bisogno di gestire tutte le possibili situazioni di errore, la
duplicazione o la perdita dei pacchetti, la caduta delle connessioni o di un router, e via
dicendo. Se le
applicazioni
utilizzassero direttamente i servizi dell’IP, ognuna di esse dovrebbe implementare
una serie alquanto complessa di algoritmi e servizi per tenere conto di tutto ciò. A
parte il fatto che esistono relativamente pochi programmatori in grado di far questo fra
gli svariati milioni di sviluppatori di applicazioni, nella maggior parte dei casi si
tratterebbe di reinventare ogni volta la ruota. In generale questi problemi, seppure
complessi, sono abbastanza standard, per cui si è pensato di poggiare sui sistemi di
trasmissione a pacchetti un protocollo affidabile che potesse essere implementano da
sviluppatori altamente specializzati, lasciando così agli altri la possibilità di
concentrarsi sulla logica applicativa piuttosto che sugli aspetti specifici della
trasmissione dei dati a basso livello.
Vediamo
allora quali sono le caratteristiche principali del TCP, eventualmente comparate a quelle
dell’IP.
Innanzi
tutto il TCP fornisce una visione dei dati di tipo a flusso (data stream), cioè i
dati sono ricevuti in sequenza e nello stesso ordine con il quale sono stati trasmessi. A
questo livello cioè, l’utente del TCP spedisce i dati come un singolo flusso di byte
e nello stesso modo li riceve. Nell’IP avevamo invece la divisione dei dati in
pacchetti che potevano subire un’ulteriore frammentazione se si trovavano a passare
attraverso reti caratterizzate da una soglia molto bassa sulle dimensioni dei frame
fisici. I pacchetti potevano inoltre arrivare in ordine sparso rispetto a quello di
trasmissione.
Secondo
punto: nell’IP non si sa mai a priori il cammino che effettua un pacchetto. Il
TCP fornisce al suo utente una visione del collegamento come se esso fosse una linea
dedicata. Ovviamente sotto sotto il meccanismo è ancora quello a pacchetti, ma la cosa è
schermata agli utilizzatori del TCP. Tale caratteristica è detta vitual circuit
connection, cioè circuito di connessione virtuale. Il TCP si basi sul concetto di
connessione, piuttosto che su quello di indirizzo come fa invece l’IP. Una
connessione, per definizione, richiede la definizione di due punti piuttosto che di uno
solo, detti punti terminali o estremi della connessione (endpoint). Parleremo anche di
interlocutori per indicare gli utenti posti agli estremi della connessione.
Terzo
punto: abbiamo visto che l’IP divide i dati in pacchetti che vengono costruiti
sulla base di esigenze di trasmissione legate alle varie reti fisiche su cui si poggia il
sistema. D’altra parte le applicazioni dividono i dati in funzione delle esigenze
applicative. Per esempio, un’applicazione di posta elettronica può considerare una
lettera da 8.000 caratteri una singola unità dati, mentre un protocollo per la gestione
della rete può avere l’esigenza di spedire tanti piccoli messaggi di non più di 16
byte l’uno. Il TCP permette di disaccoppiare il modo di dividere i dati delle
applicazioni da quello dell’IP. Così la lettera di cui sopra viene prima spezzata in
tante parti, spedita via IP e poi ricomposta dal livello TCP del destinatario, mentre per
i messaggi di controllo avviene il contrario: prima vengono accumulati in un singolo
pacchetto, e poi rispezzettati presso il destinatario. Questo meccanismo è detto buffered
transfer. Naturalmente può sorgere l’esigenza di forzare la trasmissione dei dati
anche se il buffer non è pieno. Per esempio, se serve sapere se un certo sistema è
attivo o meno manderò prima un messaggio di interrogazione, e solo una volta ricevuta la
conferma incomincerò a spedire gli altri dati. Dato che il messaggio di interrogazione è
più piccolo del buffer, esso non verrebbe realmente spedito dal TCP fintanto che questi
non è stato riempito. È quindi necessario forzare la trasmissione del primo messaggio
(push) se si vuole evitare di attendere inutilmente la risposta a un messaggio che in
realtà non è mai partito.
Quarto
punto: per quanto intelligente, il TCP si preoccupa di trasferire i dati che gli
vengono passati senza entrare in merito a il loro significato dal punto di vista
applicativo. In che modo il flusso di dati vada interpretato semanticamente è
responsabilità delle due applicazioni che utilizzano la connessione TCP per cooperare.
Questo vuol dire che se un’applicazione manda alla sua controparte una serie di
indirizzi, questi arriveranno uno di seguito all’altro nel giusto ordine, ma senza
alcuna garanzia che ogni buffer contenga un numero intero di indirizzi. Sta
all’applicazione ricomporre un indirizzo capitato a cavallo di due buffer
consecutivi. Si parla quindi di flusso senza struttura (Unstructured Stream).
Quinto
e ultimo punto: le connessioni TCP permettono il trasferimento contemporaneo dei dati
in entrambe le direzioni, quello che nel gergo delle comunicazioni si chiama una
connessione full-duplex. Si hanno cioè due flussi che scorrono indipendentemente in
direzioni opposte, senza interagire fra loro. Le applicazioni hanno comunque la
possibilità di passare alla modalità half duplex semplicemente bloccando uno dei due
flussi di dati.
Ma in che
modo il TCP garantisce quella affidabilità che manca all’IP? Il meccanismo di base
utilizzato sia dal TCP che da molti altri protocolli cosiddetti "affidabili" è
quello della ritrasmissione in caso di mancata conferma (positive acknowledgement with
retrasmission). Si tratta di un meccanismo concettualmente semplice: ogni qual volta uno
dei due interlocutori di una connessione spedisce dei dati, questi attende una conferma
dell’avvenuta ricezione. Se questa arriva entro un tempo stabilito viene spedito il
pacchetto successivo, altrimenti l’applicazione rispedisce quello precedente. Tale
tempo viene misurato con un timer che viene fatto partire ogni volta che un pacchetto è
spedito. Questo meccanismo risolve il problema dei pacchetti persi o danneggiati, ma può
crearne un altro. Supponiamo che a causa di problemi di saturazione della rete un
pacchetto ci metta molto più tempo del previsto ad arrivare. A questo punto il mittente,
non vedendosi arrivare indietro la conferma ne rispedisce una copia. Succede così che il
destinatario riceve a una certa distanza l’uno dall’altro due copie dello stesso
pacchetto. Il problema della duplicazione dei pacchetti viene risolto facendo numerare
sequenzialmente al mittente tutti i pacchetti da spedire e facendo verificare al
destinatario la sequenza ricevuta. Naturalmente questo non vale solo per i messaggi ma
anche per le conferme agli stessi. Infatti anche una conferma potrebbe venire erroneamente
duplicata. Per evitare questo ogni conferma riporta il numero di sequenza del messaggio a
cui si riferisce, permettendo così al mittente di verificare che a ogni messaggio spedito
corrisponda una e solo una conferma di ricezione. È un po' lo stesso meccanismo di una
raccomandata con ricevuta di ritorno.
In realtà
gli algoritmi utilizzati dal TCP sono un po' più complicati, e tengono conto di tutta una
serie di situazioni che si possono verificare. Senza contare che il tempo di attesa prima
della ritrasmissione è un punto chiave di tutto il discorso. Se si attende troppo poco si
rischia di generare un sacco di duplicati inutili, saturando per giunta la rete, mentre se
si attende troppo si rischia di abbassare notevolmente e inutilmente le prestazioni della
trasmissione dei dati, rallentando le applicazioni alle estremità della connessione.
Il
meccanismo della conferma di ricezione con ritrasmissione ha inoltre un grosso svantaggio.
Anche se i tempi di attesa sono scelti in modo ottimale, esso causa un notevole
sottoutilizzo della rete. Infatti, indipendentemente dalla capacità della rete, i due
interlocutori passano la maggior parte del tempo attendendo le varie conferme. È un po'
come avere un tubo nel quale vengono fatte cadere una a una delle palline numerate in
sequenza. All’altra estremità del tubo c’è una cesta poggiata su un prato, un
po' distante dal foro di uscita. Se la pallina cade nella cesta fa rumore, altrimenti cade
nel prato e non si sente niente. Se ogni volta che metto una pallina nel tubo aspetto di
sentire il rumore che mi conferma che la pallina è caduta nel cesto, il tubo resta per la
maggior parte del tempo vuoto. Una tecnica di ottimizzazione usata dal TCP per rendere
più efficiente il meccanismo appena descritto è quella delle finestre di scorrimento
(sliding window). Funziona più o meno in questo modo. Immaginate di immettere nel tubo
una sequenza di dieci palline senza attendere che la prima sia arrivata. Come si sente il
primo flop si aggiunge un’undicesima pallina, e poi una dodicesima e così via. Se si
salta un flop si reinserisce una pallina con lo stesso numero di quella che non è
arrivata, tanto il destinatario può comunque riordinare le palline utilizzando i numeri
scritti sopra. Il numero di palline che compongono il trenino da spedire indipendentemente
dalla ricezione del flop si chiama dimensione della finestra di scorrimento (sliding
window size). Se si sceglie una dimensione tale da riempire tutto il tubo nella sua
lunghezza si sfrutta al massimo la capacità dello stesso.
In pratica
questo sistema divide la sequenza di pacchetti in tre fasce. La prima è rappresentata dai
pacchetti spediti e di cui si è avuta la conferma di ricezione. La seconda è formata dai
pacchetti spediti ma dei quali non si sa ancora niente, e la terza è formata dai
pacchetti ancora da spedire. Con questa tecnica il TCP mantiene un timer per ogni singolo
pacchetto che appartiene alla seconda fascia. Il nome "Finestra di scorrimento"
deriva dal fatto che è come se ci fosse una finestra ampia quanto il trenino di pacchetti
che possono essere spediti senza attendere la conferma dell’avvenuta ricezione che
scorre in avanti un pacchetto alla volta ogni qual volta arriva una conferma. Anche in
questo caso, come in quello del tempo di attesa prima di ritrasmettere un pacchetto, le
dimensioni della finestra di scorrimento rappresentano un fattore critico per determinare
l’efficenza del sistema. In generale, se le dimensioni della finestra sono maggiori
del tempo di attesa per il singolo pacchetto, allora la finestra continua a scorrere
regolarmente senza interruzioni, salvo nel caso di ritrasmissioni, e la capacità di
carico della rete viene sfruttata al massimo. Abbiamo detto che il TCP utilizza
il metodo della finestra di scorrimento per tenere la rete sempre impegnata al massimo
della sua capacità e che esiste un'importante differenza tra il meccanismo generale e
quello più sofisticato utilizzato effettivamente dal TCP. Tale differenza consiste in
un'asimmetria rispetto al meccanismo base dove l'unità dati utilizzata nella finestra di
scorrimento era la stessa utilizzata nella trasmissione. Il TCP utilizza il segmento come
unità dati da trasmettere, mentre ragiona in termini di ottetti per quello che riguarda
il meccanismo di ritrasmissione. Questo comporta una complicazione nella gestione delle
conferme di ricezione (acknowledgement).
A causa
dell'asimmetria suddetta, la ritrasmissione in caso di mancata ricezione non avviene per
segmenti, ma a livello di ottetti. Questo vuol dire che un segmento può contenere
contemporaneamente sia nuovi dati sia una parte dei dati persi in precedenza. Ovviamente a
queste condizioni ha poco senso numerare semplicemente i segmenti e usare questo
identificativo nelle conferme di ricezione. Né è pensabile di usare i datagrammi IP a
tale scopo, dato che questi sono generalmente di lunghezza fissa mentre i vari segmenti
TCP sono di lunghezza variabile. Ne consegue che l'unico modo per gestire le conferme è
quello di ragionare in termini di cursore all'interno del flusso di dati. Come dire
"ho ricevuto i primi 300 caratteri della lettera che mi hai spedito".
Ecco che
cosa accade: ogni segmento contiene la posizione dell'area dati del segmento TCP
all'interno del flusso di dati. Tale posizione si chiama numero di sequenza (sequence
number) ed è calcolata in byte. Il destinatario estrae i vari ottetti dai segmenti
ricevuti e li ricompone per ricostruire il flusso dei dati, utilizzando i numeri di
sequenza per riordinare i vari segmenti.
Questi
possono infatti arrivare in qualunque ordine, o essere andati persi. A questo punto, chi
sta ricevendo i dati, avrà ricostruito in modo completo una parte del messaggio
originario e si ritroverà alcuni dati in eccesso che non sono contigui alla parte di
flusso ricostruito. Ogni volta che il destinatario riceve un segmento, manda indietro
nella conferma di ricezione il numero di sequenza dell'ottetto che si aspetta di ricevere
per continuare la ricostruzione, cioè il valore dell'ultimo ottetto della parte contigua
ricostruita più uno.
Immaginate
di dover spedire una lettera a un vostro amico. Il TCP negozia con la controparte la
lunghezza massima del segmento, come vedremo più avanti. Quindi inizia a riempire il
primo segmento un carattere alla volta. Quando il segmento è pieno viene spedito e viene
fatto partire il contatore a tempo per quel segmento. Quindi il TCP inizia a riempire il
secondo segmento, che parte regolarmente, e così dicendo. Man mano che i segmenti partono
arrivano dalla controparte le conferme di ricezione. Supponiamo che
a un certo
punto, dopo aver spedito 450 ottetti, arrivi per due volte al mittente la conferma che il
destinatario è riuscito a ricostruire il flusso fino al 300° carattere e che si aspetta
il 301°. È evidente che qualcosa è andato storto e che si sono persi dei dati. Il TCP
allora spedisce un segmento che contiene di nuovo dal 301° carattere in poi, diciamo fino
al 400°. Dato che i caratteri dal 370° al 450° erano comunque arrivati regolarmente, il
successivo messaggio di conferma richiederà direttamente il 451° carattere, e non il
401°.
Vantaggi e svantaggi
Un vantaggio è che il valore di
conferma è estremamente semplice da calcolare e di immediata comprensione. Inoltre, se
una conferma di ricezione va persa, non è detto che questo causi automaticamente la
ritrasmissione dei dati. Ci sarà comunque la conferma successiva che fornirà
l'indicazione esatta del punto a cui è arrivato il destinatario nel ricostruire il
flusso.
Lo
svantaggio più grosso è che il mittente non ha modo di sapere quanti dati siano
effettivamente arrivati con successo al destinatario, dato che basta un buco nel flusso
per far segnalare come validi un numero di byte molto inferiore a quelli effettivamente
ricevuti. Questo crea seri problemi al mittente, che non sa se ritrasmettere tutti i dati
successivi, e quindi sprecare tempo a ritrasmettere dati già arrivati, o trasmettere solo
una piccola parte e aspettare la conferma che il potenziale buco si è chiuso.
Entrambi
gli schemi sono alquanto inefficienti. Sta allo sviluppatore dello stack TCP/IP decidere
quali algoritmi utilizzare, tenendo presente che un algoritmo troppo complesso ha comunque
lo svantaggio di avere potenzialmente basse prestazioni.
Un altro
punto importante è il calcolo della lunghezza ottimale del segmento. Abbiamo detto più
sopra che ogni conferma di ricezione contiene una soglia di capacità (window
advertisement) la quale specifica il numero di ulteriori ottetti che il destinatario è in
grado di ricevere. Questo meccanismo permette di adattare la finestra di spedizione alle
dimensioni del buffer di ricezione. Tuttavia è anche necessario definire la lunghezza del
segmento oltre che in funzione delle capacità di trasmissione del mittente e di ricezione
del destinatario, anche e soprattutto in funzione delle caratteristiche della rete, come
per esempio la grandezza massima del frame fisico, o Maximum Transfer Unit (MTU). La
lunghezza massima di un segmento, o Maximum Segment Size (MSS), viene calcolata appunto
sulla base dell'MTU se entrambi gli estremi della connessione si trovano nella stessa rete
fisica, altrimenti lo standard raccomanda di utilizzare un valore di 536 byte, equivalente
alla dimensione normale di un datagramma IP meno le dimensioni standard delle intestazioni
IP e TCP sommate insieme, 40 byte appunto.
Tale
calcolo è ovviamente solo un primo tentativo di ottimizzare l'utilizzo della rete da
parte del TCP. Durante la trasmissione il TCP può modificare tale valore in funzione
della situazione contingente. Non esiste tuttora un algoritmo standard per definire il
giusto valore per l'MSS, data la complessità del problema. Una cattiva definizione
dell'MSS può seriamente penalizzare la comunicazione. Se il segmento è troppo piccolo,
il rapporto tra i dati trasmessi e quelli utilizzati nella trasmissione stessa è
sfavorevole. Per esempio, un segmento di cinque byte utilizza solo un ottavo della
larghezza di banda (bandwidth) disponibile, dato che per ogni cinque byte di dati ce ne
sono ben quaranta di intestazione. Viceversa, se il segmento è molto grande, altrettanto
è il datagramma IP. Se il datagramma è più grande dell'MTU, verrà spezzato in più
frammenti non indipendenti fra loro, per cui basta che si perda un solo frammento per
perdere tutto il datagramma e di conseguenza il segmento TCP.
Il controllo di flusso
Il controllo del flusso dei dati è
un aspetto estremamente importante in un sistema in cui sono collegate macchine anche
molto differenti fra loro per dimensioni e capacità di trasmissione . Per controllo del
flusso si intende la possibilità di regolare dinamicamente la quantità di dati che
vengono immessi nella rete. Non solo è importante che il destinatario possa regolare la
velocità di spedizione in funzione della sua capacità di ricezione, ma è fondamentale
che ogni gateway intermedio possa frenare il flusso dei dati che riceve per evitare di
entrare in saturazione. Il meccanismo descritto della soglia di capacità permette di
risolvere il primo problema ma non il secondo. Quest'ultimo è detto congestione, ed è
estremamente importante perché non tenerne conto vuol dire mandare in tilt la rete.
Lo
standard TCP non prevede alcun meccanismo di controllo della congestione, lasciando agli
implementatori di tale protocollo il non banale compito di sviluppare una logica capace di
evitare questo tipo di problemi.
Per quello
che riguarda i segmenti, il fatto che il TCP sia libero di dividere il flusso in segmenti
può a volte causare problemi dal punto di vista applicativo. Per esempio, supponiamo di
implementare via TCP/IP un terminale remoto. Questo vuol dire che tutte le operazioni
effettuate con la tastiera e il mouse su di una macchina (chiamiamola locale) saranno
visibili su di un'altra macchina (remota) come se esse fossero state effettuate dalla
tastiera e dal mouse della macchina remota. Non solo: sarà possibile vedere lo schermo
della macchina remota all'interno di una finestra della macchina locale . Questo tipo di
applicazioni è molto utile per esempio se per un qualche motivo la macchina da
controllare non ha una sua tastiera oppure si trova in un locale non generalmente
accessibile all'operatore. È evidente che affinché l'applicazione funzioni essa debba
lavorare in tempo reale. Se cioè si preme il tasto T sulla tastiera locale, la lettera T
deve apparire immediatamente sullo schermo della macchina remota, e quindi apparire anche
all'interno
della finestra locale che riproduce tale schermo. Lo stesso se si fa click sul pulsante di
chiusura di una finestra. Ovviamente se il TCP fosse libero di accumulare questi comandi
per poi spedirli tutti in una volta l'applicazione sarebbe di difficile utilizzo. Infatti,
se l'operatore decidesse di chiudere una finestra dello schermo remoto per accedere
un'icona sottostante e il TCP non spedisse il comando fintanto che il buffer di partenza
non fosse pieno, non sarebbe possibile eseguire l'operazione successiva, cioè aprire
l'icona sulla scrivania del sistema. Per questo motivo il TCP prevede la possibilità di
forzare la spedizione del buffer (push). Questo tuttavia non è sufficiente. Se infatti il
TCP che riceve i dati accumulasse gli stessi nel buffer di ricezione prima di passarli
all'applicazione destinataria saremmo punto e da capo. Per questo motivo, quando un
segmento è forzato in uscita, il TCP imposta a uno un certo bit nell'intestazione del
segmento in modo che questi possa venire riconosciuto e immediatamente passato
all'applicazione remota. Esiste poi la possibilità che il TCP debba spedire dei dati che
non fanno parte del flusso normale e che vanno immediatamente gestiti dalla controparte
indipendentemente dallo stato in cui si trova la ricostruzione del messaggio originario.
Tali dati sono detti urgenti, e anche in questo caso esiste un bit nell'intestazione del
segmento che informa il destinatario del fatto che il segmento va gestito immediatamente.
Il concetto è analogo a quello del BREAK da tastiera. Se avete lanciato un
programma
che va in loop è necessario poterlo interrompere senza dover far ripartire il sistema. Su
molti sistemi operativi basta premere una sequenza di tasti, come per esempio Control-C
(^C) per bloccare l'esecuzione del programma. Similarmente, se un estremo della
connessione deve bloccare (o sbloccare) l'elaborazione del flusso di dati dall'altra
parte, dovrà poter mandare un messaggio urgente che abbia la precedenza rispetto ai
normali segmenti di dati. Si dice che tale messaggio è fuori banda (out of band).
Benché il TCP presenti
all’utente una visione continua dei dati, detta flusso, l’unità di
trasferimento dei dati del TCP è il segmento. Un segmento è formato come al solito da
una intestazione e da un’area dati. Al contrario del datagramma IP, il segmento ha
dimensioni variabili nel tempo, cioè i vari segmenti spediti a fronte di uno stesso
flusso possono avere lunghezze differenti. I segmenti sono utilizzati dal TCP per aprire e
chiudere una connessione, trasferire dati, spedire conferme di ricezione e modificare la
finestra di spedizione, quel meccanismo che garantisce un utilizzo ottimale della rete,
come spiegato in precedenza. Due caratteristiche peculiari del TCP sono che lo stesso
segmento può portare contemporaneamente sia dati veri e propri sia dati di controllo, e
che le informazioni di controllo possono riferirsi sia allo stesso flusso dell’area
dati, sia al flusso opposto (piggybacking).
L’intestazione
Innanzitutto abbiamo i numeri di
porta del mittente e del destinatario, esattamente come nell’UDP. Come già
nell’UDP, infatti, gli indirizzi IP delle due controparti sono contenuti
nell’intestazione del datagramma IP. Al contrario di quanto avveniva nell’UDP,
tuttavia, la conoscenza da parte del TCP degli indirizzi IP non rompe il paradigma che
vuole un certo isolamento fra le responsabilità dei vari livelli dello stack. Il TCP
infatti, architetturalmente, ragiona in termini di connessioni, e queste comprendono sia
l’informazione relativa alle porte, sia quella relativa agli indirizzi IP. Anzi, ogni
qual volta l’IP consegna un segmento al TCP, gli passa anche gli indirizzi IP
contenuti nell’intestazione del datagramma.
Anche nel
caso del segmento TCP la verifica della correttezza dell’intestazione da parte del
destinatario viene effettuata utilizzando un meccanismo di somma di controllo con
pseudointestazione. All’interno dell’intestazione TCP, infatti, esiste un campo
chiamato somma di controllo (checksum). Il TCP imposta inizialmente tale campo di 16 bit a
zero. Costruisce quindi una psedointestazione che contiene gli indirizzi IP del mittente e
del destinatario, il numero di protocollo del sottosistema di trasmissione (nel caso del
TCP basato su IP è sei) e la lunghezza del segmento TCP compresa l’intestazione. A
questo punto appende alla pseudo intestazione il segmento IP e aggiunge alla fine dello
stesso tanti zeri quanti ne servono per far sì che il blocco risultante sia un multiplo
di parole da 16 bit (padding). Divide quindi il blocco in parole da 16 bit e ne calcola la
somma a complemento uno. Il risultato viene quindi salvato nel campo apposito
dell’intestazione e sia la pseudointestazione sia i bit aggiunti in fondo vengono
rimossi prima di spedire il segmento. Il destinatario ovviamente effettuerà un calcolo
analogo per verificare che il valore di controllo così ottenuto corrisponda con quello
arrivato nell’intestazione del segmento.
Nell’intestazione
ci sono tre campi calcolati in ottetti. Il primo è il numero di sequenza (sequence
number), che rappresenta la posizione dell’area dati del segmento TCP
all’interno del flusso di dati. Il secondo è il numero di conferma (acknowledgement
number), ovverosia il numero di sequenza dell’ottetto che il mittente si aspetta di
ricevere per continuare la ricostruzione. Da notare che tale valore corrisponde al flusso
opposto rispetto a quello in cui viaggia il segmento che lo contiene. Il terzo campo è il
puntatore ai dati "urgenti". Come detto prima, è possibile che il TCP debba
spedire dei dati urgenti che vanno elaborati indipendentemente dal flusso normale di dati,
e con priorità rispetto a quest’ultimo. In questo caso il segmento contiene un
segnalatore (flag) che informa il destinatario della presenza d’informazioni urgenti
nell’area dati. I dati urgenti sono posizionati all’inizio dell’area dati,
e il puntatore in questione indica dove tali dati finiscono e dove ricominciano i dati
normali, se ce ne sono.
I segnalatori
Separati da un’area riservata
per usi futuri c’è il campo che contiene la posizione dell’area dati nel
segmento e un blocco di sei segnalatori. Il primo, misurato in parole da 32 bit, indica di
fatto la lunghezza dell’intestazione del segmento in tale unità di misura. questo
campo è necessario in quanto in fondo all’intestazione esiste una zona riservata a
eventuali opzioni che rende la lunghezza dell’intestazione non fissata a priori. Il
secondo campo contiene invece sei indicatori. Data infatti nel segmento la presenza
contemporanea, almeno in potenza, sia di dati di controllo sia di dati applicativi normali
e urgenti, è necessario utilizzare dei segnalatori per informare il destinatario su cosa
effettivamente contiene il segmento. Tutti i segnalatori sono attivi se impostati a uno,
inattivi altrimenti. Il primo segnalatore indica se l’area dati contiene dati
urgenti. Il secondo indica la presenza nel segmento di una conferma di ricezione valida.
Dato infatti che il campo corrispondente esiste sempre e comunque nell’intestazione,
se il segmento non trasporta alcuna conferma di ricezione è necessario informare in
qualche modo il destinatario che tale campo va ignorato. Il terzo bit è posto a uno
quando si vuole forzare la trasmissione dei dati all’utente finale indipendentemente
dal fatto che il buffer di ricezione sia o meno completamente riempito. Il quarto
segnalatore serve per interrompere immediatamente la connessione (reset). Tale evento
avviene solo in situazioni eccezionali e causa l’interruzione immediata delle
trasmissioni da ambo le parti e il rilascio del contenuto dei buffer di ricezione. Il
quinto bit è detto di sincronizzazione, ed è utilizzato durante la fase iniziale di
negoziazione della connessione, detta in gergo handshake. In pratica, i segmenti scambiati
quando questo bit è impostato a uno servono a sincronizzare i numeri di sequenza delle
due controparti prima d’iniziare la trasmissione vera e propria dei dati.
L’ultimo bit serve a informare il destinatario che il mittente intende terminare in
modo pulito la connessione e che non ha più dati da spedire. All’apertura e alla
chiusura della connessione il TCP utilizza un algoritmo chiamato saluto a tre vie
(three-way handshake) che garantisce la corretta sincronizzazione delle due operazioni.
L’ultimo
campo fisso è quello relativo alla soglia di capacità del mittente (window
advertisement) che contiene il numero di ulteriori ottetti che esso è in grado di
ricevere. A questo punto è di nuovo importante ricordare il concetto di piggybacking, a
cui già si è accennato. Ovverosia, ogni segmento può portare contemporaneamente
informazioni in cui una controparte è vista sia come chi spedisce i dati contenuti nel
segmento, cioè come mittente, sia come chi ha ricevuto o deve ricevere dati
dall’altro capo della connessione, cioè come destinatario. Quando noi parliamo di
mittente, per evitare confusione, ci riferiamo sempre al mittente del segmento di cui
stiamo parlando. È importante comunque tenere sempre presente che alcuni dati del
segmento hanno senso solo se si considera il mittente quale destinatario di dati
precedenti o ancora da venire.
In fondo
all’intestazione c’è un’area opzionale che può essere utilizzata a vari
scopi. In genere essa contiene opzioni che permettono alle due controparti di negoziare
alcuni aspetti della comunicazione. Un esempio è il calcolo della lunghezza ottimale del
segmento.
L’implementazione del
protocollo TCP
Abbiamo visto che tutto il
meccanismo funziona ed è affidabile grazie alle conferme di ricezione e alla
ritrasmissione dei pacchetti probabilmente andati perduti. Ma come fa a sapere il mittente
che un pacchetto è andato effettivamente perduto? Ovviamente perché non è arrivata la
conferma di ricezione, direte voi. Va bene, ma quanto devo aspettare tale conferma prima
di assumere che sia necessaria una ritrasmissione? E qui son dolori. Se aspetto troppo
rischio di rallentare la comunicazione in modo inaccettabile. Se aspetto troppo poco
rischio di ritrasmettere inutilmente troppi segmenti, magari semplicemente un po’ in
ritardo. Tutto il sistema si basa sul calcolo del tempo di attesa massimo, o timeout. Il
TCP calcola il timeout sulla base del tempo intercorso fra la spedizione di un segmento e
l’arrivo della conferma corrispondente. Sembra facile, ma non è così. Vediamo
rapidamente i punti chiave del discorso.
- Il TCP calcola
continuamente il timeout, ogni volta che arriva una conferma di ricezione. In questo modo
il sistema è sempre aggiornato in funzione dello stato effettivo della connessione e
della rete.
- Il timeout è calcolato
come media pesata dei tempi intercorsi fra la spedizione del segmento e la ricezione della
conferma. Chiamiamo quest’ultimo tempo rilevato di andata e ritorno (Round Trip
Sample) o RTS. Il tempo stimato di andata e ritorno (Round Trip Time) è calcolato
utilizzando la formula riportata nel riquadro sotto. In pratica ogni
nuovo RTS pesa più o meno sul calcolo dell’RTT in base al valore di alfa. Se alfa è
molto vicina a zero, l’RTT varia rapidamente a ogni cambiamento dell’RTS, per
cui il sistema risponde rapidamente alle variazioni. Se viceversa alfa è vicina a uno, è
necessario che la nuova RTS rimanga stabile più a lungo per avere effetto sull’RTT.
A questo
punto il timeout viene calcolato moltiplicando l’RTT per un valore maggiore di uno.
Se il valore di beta è troppo vicino a uno la perdita di un pacchetto viene
immediatamente rilevata, ma si rischia di ritrasmettere più pacchetti del necessario. Se
viceversa beta è troppo alto si rischia di aspettare troppo a lungo prima di
ritrasmettere un pacchetto perso, abbassando così le prestazioni della connessione. In
genere si raccomanda per beta un valore di due.
Una scelta difficile
La scelta di alfa e di beta sembra
dunque essere critica, ma i problemi non sono ancora finiti. Infatti, se un segmento è
trasmesso due volte, quando arriva la conferma di ricezione, a chi si riferisce? Al
pacchetto originale o a quello ritrasmesso? Se usiamo il primo pacchetto per il calcolo
dell’RTS rischiamo di far crescere esponenzialmente il valore di timeout. Infatti un
pacchetto è ritrasmesso quando scade il timeout precedente. Di conseguenza il nuovo RTS
è ovviamente più grande del vecchio timeout. Se viene perso un nuovo pacchetto
l’RTS cresce ancora, e così via. Se usiamo il pacchetto ritrasmesso abbiamo il
problema opposto, cioè il timeout rischia di ridursi sempre di più, o almeno si è
dimostrato sperimentalmente che si stabilizza su valori alquanto bassi. Supponiamo infatti
di avere un ritardo in rete: la conferma di ricezione arriva conseguentemente in ritardo.
Nel frattempo il mittente ha rispedito il pacchetto che credeva perso. Appena arriva la
conferma questa è associata al segmento ritrasmesso generando così un RTS molto piccolo.
Il timeout si riduce, aumentando il rischio di considerare persi pacchetti la cui conferma
di ricezione arriva in ritardo, e così via.
P. Karn
propose nel 1987 d’ignorare i pacchetti ritrasmessi nel calcolo del timeout. Questo
evitava il problema suddetto, ma ne creava un altro. Se un pacchetto è ritrasmesso
perché si è avuto un repentino calo di prestazioni della rete, il timeout rimarrà
sempre troppo basso, in quanto il mittente continuerà a ritrasmettere pacchetti le cui
conferme arrivano in ritardo rispetto al timeout calcolato prima del calo di prestazioni.
Dato che tali conferme vengono regolarmente ignorate per il calcolo dell’RTT, il
timeout non viene più aggiornato almeno fintanto che la rete non torna normale, cosa per
giunta complicata dal sovraccarico dovuto all’inutile ritrasmissione dei pacchetti.
La soluzione consiste nell’aumentare il timeout precedente a una ritrasmissione di un
fattore arbitrario, diciamo gamma, fino a un limite massimo ragionevole calcolato sulla
base dei possibili cammini all’interno della rete (riquadro 1). In genere gamma non
è minore di due.
Questa
tecnica è detta di backoff.
Conclusione
Implementare il protocollo TCP non
è certo banale. Il che tra l’altro fa capire come non tutti i pacchetti TCP siano
uguali: anzi, è proprio il contrario. Una scelta oculata degli algoritmi implementativi
può fare seriamente la differenza fra un prodotto e un altro. Il fatto che essi
implementino lo stesso standard non dà alcuna indicazione sulla qualità delle
prestazioni dello stack che state utilizzando. Se poi alcuni parametri possono essere
personalizzati dall’utente, una opportuna calibrazione del programma studiata sulle
caratteristiche specifiche della vostra rete, può modificare significativamente i tempi
di risposta del sistema. Naturalmente non è fra gli scopi di questi articoli entrare nel
dettaglio di tutte le problematiche TCP/IP. |
|
|