L'architettura di un sistema di
comunicazione, a causa della sua complessità, viene generalmente
suddivisa in livelli funzionali. Ciascuno strato implementa un insieme
di servizi utilizzati dal livello successivo per realizzare compiti più
complessi. L'ultimo livello quindi per svolgere le proprie funzioni si
avvale della collaborazione di tutti i livelli sottostanti. Il modello
OSI (Open System Interconnection), ad esempio, divide l'operazione di
trasferimento di dati da un luogo ad un altro in sette fasi
gerarchiche, ognuna delle quali contribuisce ad assemblare e/o
disassemblare i pacchetti che costituiscono il flusso delle
informazioni.
La
famiglia di protocolli TCP/IP è stata sviluppata prima che il modello
OSI fosse definito, essa definisce solo quattro livelli ed a ciascun di
questi è associato almeno un protocollo che specifica il comportamento
di determinate funzioni di rete.
Poiché
nell'ambito delle applicazioni di controllo di processo è essenziale
disporre di un sistema di comunicazione affidabile, l'articolo
focalizzerà l'attenzione sul livello di trasporto, che è il principale
responsabile per il conseguimento di tale obiettivo.
Introduzione
La
suite di protocolli TCP/IP mette a disposizione due differenti
protocolli che implementano il livello di trasporto; di seguito saranno
discussi i principali vantaggi e svantaggi di entrambi, al fine di
capire quale sia il migiore candidato per l'implementazione della
comunicazione tra processi che costituiscono un'applicazione di
controllo distribuita.
Il
protocollo IP della famiglia di protocolli TCP/IP, corrisponde al
livello di rete nel modello OSI e fornisce le funzionalità base del
trasferimento di dati che includono: indirizzamento, instradamento e
frammentazione. Il livello di trasporto di questo stesso modello è
costituito dal protocollo TCP, che risiede sopra il livello di rete, e
implementa sia la comunicazione punto a punto, sia un'interfaccia
comune utilizzabile dal livello applicazione.
In
genere l'affidabilità della comunicazione è gestita o a livello di
trasporto, o a livello di applicazione. Poiché la maggior parte dei bus
di campo furono sviluppati prima che il TCP/IP diventasse popolare,
implementano il concetto di affidabilità a livello di applicazione.
Tuttavia esistono anche alcuni protocolli di bus di campo, quali il
MODBUS/TCP, che si affidano al meccanismo di consegna garantita del TCP.
Il
livello di trasporto dello stack TCP/IP mette a disposizione due
protocolli, ciascuno dei quali può essere impiegato nelle reti di
controllo: lo User Data Protocol (UDP) e il "trasmission Control
Protocol" (TCP), entrambi esaminati in questo articolo.
User Datagram Protocol (UDP)
Il
protocollo UDP fornisce un servizio di trasporto senza connessione e
non affidabile, poiché non spedisce alla stazione trasmittente alcuna
informazione sui dati ricevuti o persi. L'integrità dei dati, quindi,
non è assicurata poiché è possibile la ricezione di pacchetti fuori
sequenza o doppioni, all'insaputa della stazione di origine.
Dal
punto di vista della correttezza e affidabilità dei dati l'UDP non pare
migliore del protocollo IP, ma introduce un concetto nuovo, utilizzato
dal livello applicazione, i numeri di porta.
Figura 1
L'intestazione
del protocollo UDP è corta e semplice (figura 1): richiede solo un
supplemento di 8 byte. I campi relativi alle porte della stazione
sorgente e di destinazione hanno una lunghezza di 16 bit ciascuno,
perciò occupano 4 byte. Il campo "Message length", di 16 bit, indica la
lunghezza dell'intestazione più quella dei dati. Infine il campo
"checksum" di 16 bit è utilizzato per controllare la validità
dell'intestazione e dei dati.
Il
livello di trasporto affida il datagramma UDP al protocollo IP che lo
memorizza nel proprio campo dati ed inserisce propria intestazione gli
indirizzi delle stazioni (ricevente e trasmittente). A sua volta il
datagramma IP è incapsulato all'interno del frame Ethernet, che
implementa il livello di collegamento dati, e finalmente spedito alla
stazione desiderata. Una volta che il frame è giunto a destinazione
viene eseguito il processo nell'ordine inverso. E' importante notare
che l'unico contributo fornito dal protocollo UDP è l'assegnazione al
messaggio di un numero di porta, che sarà utilizzato dal livello
applicazione.
L'utilizzo
del protocollo UDP implica che tutti i controlli relativi
all'ordinamento ed alla quantità dei messaggi spediti e ricevuti,
all'eliminazione dei pacchetti doppioni e all'eventuale ritrasmissione
di pacchetti errati, siano tutte operazioni che devono essere eseguite
dal livello applicazione. Allora se il livello applicazione è
originariamente progettato per fornire un servizio di comunicazione
affidabile, non c'è motivo di averlo anche nel livello di trasporto,
rendendo sensato l'utilizzo del protocollo UDP. In questo caso il
protocollo UDP risulta adeguato anche per applicazioni di controllo
poiché ha un basso consumo delle risorse ed è eseguito con alte
prestazioni.
I numeri di porta
Il
protocollo IP consente di trasmettere un messaggio ad una determinata
stazione ma, una volta giunto a destinazione, è necessario un
meccanismo che consenta di recapitarlo all'applicazione corretta; per
questo motivo il protocollo UDP introduce i numeri di porta. A ciascuna
applicazione (o servizio) può essere associato un numero di porta ed
ogni volta che la stazione riceve un datagramma UDP, inserisce i dati
nella coda associata al numero di porta specificato nell'intestazione
del pacchetto. Poiché è ammesso l'utilizzo di molti numeri di porta
differenti è possibile supportare la comunicazione con più applicazioni
anche contemporaneamente sulla stessa macchina.
I
numeri di porta sono codificati utilizzando 16 bit e classificati in
tre differenti categorie: assegnati, registrati e dinamici.
I
numeri di porta "assegnati" sono compresi nell'intervallo di valori da
0 a 1023 e sono stati definiti da "Internet Assigned Numbers Autority"
(IANA) ed utilizzati da varie applicazioni che oggi sono considerate
parte integrante della famiglia di protocolli TCP/IP, come TELNET, FTP
ed altre. I numeri di porta appartenenti a questo intervallo sono
definiti anche "well known" e non possono essere utilizzati da altre
applicazioni.
I
numeri di porta "registrati", invece, sono quelli che un organizzazione
richiede allo IANA quando vuole definire un nuovo servizio. Le altre
organizzazioni devono rispettare questo assegnamento e non utilizzare
né i numeri di porta assegnati, né quelli registrati.
Infine
i numeri di porta "dinamici" sono scelti in modo casuale da una
stazione trasmittente per definire la porta sorgente a cui inviare la
risposta. Per esempio, se la stazione A richiede il servizio "trivial
file transfer protocol (TFTP)
alla stazione B, inserirebbe 69 (una porta "well known" indicante il
servizio TFTP) come porta di destinazione ed una porta dinamica (cioè
un numero casuale ma non in conflitto con altre porte) come porta
sorgente, infine la richiesta viene spedita alla stazione B. La
stazione B riceve il pacchetto e riconosce che si tratta di una
richiesta TFTP poiché il numero di porta di destinazione vale 69,
quindi crea un pacchetto di risposta inserendo 69 come numero di porta
sorgente e come porta di destinazione utilizza il numero di porta
dinamico che trova nel pacchetto ricevuto, infine trasmette la risposta.
L'utilizzo
dei numeri di porta assieme all'indirizzo IP costituisce ciò che è
comunemente chiamato "socket" ed è rappresentato come <netid,
hostid, portid>. Poiché esiste una procedura per l'assegnamento dei
numeri di
porta, il socket diventa una rappresentazione univoca di una particolare applicazione nell'intera rete IP.
Transmition control protocol (TCP)
Il
secondo protocollo del livello di trasporto è il TCP. Questo protocollo
fornisce ai processi un servizio di consegna dei messaggi affidabile
basato sulla connessione. Questo solleva il livello applicazione dalla
responsabilità di garantire la consegna dei messaggi. Oltre alle
connessioni affidabili il TCP mette a disposizione il controllo del
flusso per assicurare che le stazioni non siano intasate per la
quantità di dati trasmessi.
I dati scambiati tra le stazioni
sono spediti come una sequenza di pacchetti e riassemblati dalla
stazione ricevente in modo da ricreare il flusso di dati iniziale.
Durante la trasmissione i pacchetti possono corrompersi, perdersi o
raggiungere la stazione di arrivo con un ordine differente da quello di
partenza. Una tecnica utilizzata per risolvere questo tipo di problemi
consiste nell'associare a ciascun pacchetto un numero di sequenza,
indipendentemente da quale sia il numero di sequenza del pacchetto
iniziale, al secondo pacchetto sarà associato un numero di sequenza
pari a quello del primo pacchetto più uno, e tutti i successivi
pacchetti del flusso di dati avranno numeri sequenza crescenti,
ciascuno incrementato di una unità rispetto a quello precedente. Ogni
volta che la stazione di arrivo riceve un nuovo pacchetto senza errori
spedisce un messaggio di acknowledgment per informare la stazione di
origine della avvenuta ricezione del pacchetto. Quando tutti i
pacchetti sono stati ricevuti, allora i numeri di sequenza sono
utilizzati per ordinare in modo corretto la sequenza di pacchetti. Se
tutti i pacchetti risulteranno corretti, allora il flusso di dati è
stato ricevuto correttamente e la stazione ricevente può informare la
stazione di partenza dell'avvenutà ricezione, altrimenti invia una
richiesta di ritrasmissione del solo pacchetto mancante o corrotto. In
questo modo si evita di dover richiedere la ritrasmissione dell'intero
flusso di dati.
E' bene notare che i messaggi di
"acknowledgment" non richiedono una trasmissione separata; per motivi
di efficienza il TCP consente di spedire tale messaggio con i dati: se
la stazione B sta rispondendo alla richiesta della stazione A, allora i
dati possono essere spediti contemporaneamente al messaggio di
acknowledgment del pacchetto di richiesta della stazione A. Questa
tecnica velocizza l'intero processo.
In realtà il TCP fornisce
un protocollo sequenziale orientato al byte, che risulta più robusto
rispetto allo schema orientato ai pacchetto descritto in precedenza.
Invece di sequenzializzare ciascun pacchetto, il TCP associa un numero
di sequenza a ciascun byte: più precisamente il TCP associa un numero
di sequenza al primo byte di dati presente nel pacchetto iniziale,
mentre il secondo pacchetto avrà un numero di sequenza pari al primo
numero di sequenza più il numero di byte presenti nel primo pacchetto.
La stazione ricevente segnala la ricezione di un pacchetto valido
trasmettendo un "acknowledgment" che contiene il valore del prossimo
numero di sequenza previsto, cioè il numero di sequenza dell'ultimo
byte del pacchetto ricevuto o più uno. Una volta ricevuto questo
"acknowledgement" il trasmettirore può assumere che tutti i byte
predenti a quello con il numero di sequenza indicato siano stati
ricevuti correttamente e può quindi può "dimenticarli". Affiché la
stazione di partenza possa eliminare i byte spediti dal buffer di
trasmissione è necessario che attenda questo "acknowledgment" poiché il
ricevente può sempre richiedere una ritrasmissione dei byte.
Figura 2
L'intestazione
del TCP è più grande di quella del UDP (figura 2), ma usa lo stesso
schema di quest'ultimo per l'assegnamento delle porte . A differenza
dell'UDP, l'intestazione contiene anche un numero di sequenza a 32 bit,
un numero di "acknowledgement" e molti bit di flag. Poiché
l'intestazione del TCP può essere di lunghezza variabile, a seconda del
contenuto del campo "options", è presente un ulteriore campo chiamato
"data offset" per determinare l'inzio dei dati. Il campo "padding" è
utilizzato per allineare il campo "options" sul 32 bit.
Il
campo window indica al trasmettitore quanti dati il ricevitore vuole
accettare. Infine i dati seguono l'intestazione del TCP. L'intestazione
può essere composto al massimo da 40 byte. L'insieme dell'intestazione
più i dati successivi è detto segmento. Il campo di cheksum assicura
l'integrità dell'intestazione dei dati.
Utilizzare le connessioni
Quando
si utilizza il TCP, prima di tutto deve essere stabilita e mantenuta
una connessione, in modo da creare un canale attraverso cui trasmettere
il flusso dei dati. Instaurando una connessione tra due stazioni
vengono creati alcuni buffer di dimensioni opportune per mantenere i
dati trasmessi e ricevuti.
Quando la stazione A vuole comunicare
con la stazione B, stabilisce una connessione che gestisce la
sincronizzazione ed il numero degli "acknowlegment". Questo processo è
mostrato in figura 3. Uno dei flag dell'intestazione del TCP è il bit
SYN, che è utilizzato per indicare il numero di sequenza iniziale
quando viene stabilita una connessione. Questo informa il ricevitore di
sincronizzare il suo controllo degli errori con questo numero di
sequenza. Perciò la stazione A spedisce alla stazione B un segmento TCP
con il suo numero di sequenza (che nel caso rappresentato in figura è
10) ed il flag SYN impostato. La stazione B risponde spedendo un
"acknowledgment" con il valore 11, cioè il valore del prossimo numero
di sequenza che si aspetta di ricevere ed il proprio numero di
sequenza, che nel nostro caso è 30. Questo valore è confermato dalla
stazione A che a sua volte trasmette un 31. Al termine di queste
operazioni sono state stabilite due connessioni, attraverso le quali le
due stazioni possono scambiarsi i dati. Ogni volta che la stazione A
spedisce dei dati alla stazione B, la prima incrementa il numero di
sequenza e la seconda lo conferma. Questo tipo di connessione è
denomitata "full duplex", perché utilizza due canali contemporanemente:
una da A a B e l'altra da B ad A. Queste connessioni rimangono attive
finché non sono esplicitamente terminate.
Figura 3
Una
volta che il trasferimento dei dati è completato, le due connessioni
dovrebbero essere terminate in modo da liberare la memoria allocata per
i buffer nelle due stazioni. Per terminare una connessione è utilizzato
il flag FIN: la stazione A
spedisce un segmento TCP con il flag FIN impostato ed un numero di
sequenza, la stazione B riconosce la richiesta e spedisce un ACK,
quando la stazione A riceve l'ACK la connessione da A a B è terminata.
E' necessario seguire la medesima procedura per chiudere anche la connessione da B ad A.
Controllo di flusso
Il
controllo di flusso riguarda la gestione del traferimento di dati tra
due stazioni. A seconda del ruolo della stazione, client o server, o
della sua potenza di calcolo, una stazione può non essere in grado di
sostenere il traffico della rete. Per rallentare gli eventi
l'intestazione del TCP ha un campo chiamato "window". La stazione
ricevente utilizza tale campo per specificare alla stazione
trasmittente quanti byte è in grado di accettare. La finistra ha una
dimesione dinamica che può essere aumentata non appena sarà disponibile
nuovo spazio nel buffer del ricevitore. La finistra può anche avere
dimensione zero, bloccando così la trasmissione dei dati. Se il
trasmettitore avesse la necessità di comunicare informazioni nonostante
la dimesione della finestra non lo consenta, può spedire un pacchetto
con il flag URG impostato ed un numero di sequenza nel campo "urgent
pointer" (che indica il primo byte che segue i dati urgenti). Il
ricevitore dovrebbe avere sempre dello spazio per consentire la
ricezione di dati urgenti.
Conclusioni
Sebbene
il TCP sia più affidabile del protocollo UDP, quest'ultimo può
risultare adatto se il software del livello applicazione è in grado di
trattare il controllo dell'errore e la ritrasmissione; invece, per le
applicazioni che richiedono una comunicazione sicura, il TCP è la
scelta migliore.