Introduzione all'uso del simulatore di rete cnet

<< Introduzione alla simulazione basata sugli eventi | Indice | Un segmento di rete Ethernet >>

Simulazioni

Il protocollo di data-link stop-and-wait

Lo scopo di questo protocollo che opera nello strato di collegamento dati (data-link) è di fornire al livello superiore di rete (network) una comunicazione affidabile ed efficiente lungo un canale di tipo singolo e punto-a-punto.

Nel caso di una rete con soli due nodi, lo strato Data-Link è l'unico strato necessario tra lo strato Applicativo e quello Fisico. Perciò, per convenzione, ci riferiremo allo strato superiore dello strato di Collegamento Fisico come lo strato Applicativo, ma nulla cambia se esiste anche un ulteriore strato centrale di rete.

Per questa simulazione si utilizzano i due file di dati descritti in seguito, presenti nella directory examples/ della distribuzione di cnet (in Crux si trovano precisamente in /usr/share/cnet/).

Topologia e parametri: il file STOPANDWAIT

Questo è un file di topologia, cioè definisce la struttura della rete da simulare: come sono connessi i vari nodi e gli attributi sia dei nodi coinvolti sia dei collegamenti tra questi.

I link in cnet sono singoli ma bidirezionali e possono essere sia affidabili (caso ideale) che non (caso reale), a seconda dei loro parametri sulle probabilità di errore. I parametri di un link possono essere definiti in modo diverso a seconda della direzione.

Nel nostro caso i nodi sono solo 2 ovvero si tratta di un collegamento del tipo punto-punto (i due nodi sono adiacenti). Analizziamo il file linea per linea:


compile                 = "stopandwait.c"

È un attributo che riguarda il singolo nodo e può essere definito o ridefinito in modo specifico nella definizione di ciascun nodo. Quando compare al di fuori di una definizione di nodo come in questo caso stabilisce un valore di default per tutti i nodi.

Indica il nome del file (o dei file nel caso il codice sia suddiviso in più file) sorgente che contiene il programma C che definisce i protocolli "centrali" della pila ISO/OSI seguiti da un nodo (cioè quelli tra lo strato di Applicazione e lo strato Fisico). Inoltre può includere anche altre opzioni da passare al compilatore o al linker.


drawframes              = true

È un attributo globale, cioè riguarda la simulazione e non un nodo o un link particolare.

Abilita la rappresentazione animata dei frame che attraversano lo strato Fisico, possibile solo per le reti con due soli nodi. Il numero dei campi visualizzati e i colori per ciascun tipo di frame (frame di dati o di acknowledgment) è definito dal codice C che implementa il protocollo e definisce anche il formato dei frame stessi.


messagerate             = 1000ms,

Attributo di nodo. Indica la velocità massima con cui lo strato di Applicazione può generare i messaggi da trasmettere. L'unità di misura si indica tramite i suffissi usec (microsecondi), msec (millisecondi) e s (secondi).


propagationdelay        = 2000ms,

È un attributo del collegamento e può essere definito globalmente come valore di default per tutti i link oppure ridefinito sul singolo link.

Indica il ritardo di propagazione tra i due punti finali della comunicazione.


probframecorrupt        = 2

Attributo di link. Rappresenta la probabilità che un frame su questo link venga corrotto dal rumore.

Le probabilità sono riferite ad una distribuzione uniforme. Per default le probabilità sono 0, che significa che non viene introdotto alcun errore. Inoltre non si specifica direttamente il valore delle probabilità di errore, ma l'opposto del suo logaritmo in base 2. Ad esempio probframecorrupt = 2 significa che la probabilità che un frame venga corrotto è di 1 a 8, ovvero pari numericamente a 1/23 = 1/8 = 12.5%.


probframeloss           = 2

Attributo di link. La probabilità che un frame su questo link venga del tutto perso. In questo esempio probframeloss = 2 vuol dire che il link da Perth a Melbourne (due città costiere del sud dell'Australia) perde in media 1 frame ogni 4, ovvero che la probabilità che un frame venga perso è pari a 1/22 = 0.25 = 25%.


host perth {
    x=80,    y=50
    winx=10, winy=200
}

Gli attributi locali di un nodo vanno dichiarati in un nuovo blocco, che è delimitato dalle parentesi graffe (come le istruzioni composte in C). In questo esempio si dichiarano le coordinate dell'icona che rappresenta il nodo perth sulla finestra principale del simulatore e le coordinate della finestra del nodo sullo schermo.


host melbourne {
    winx=580, winy=200
    east east east east of perth
    link to perth
}

Similmente al precedente questo blocco definisce un nodo chiamato melbourne, disegnato a destra di perth e connesso direttamente ad esso (con i parametri di default definiti prima per un link, applicati ad entrambe le direzioni).


Implementazione: il file stopandwait.c

È il file sorgente C del programma che implementa il protocollo. Cnet compila (o ricompila dopo una modifica) ed esegue automaticamente questo file.

Questo protocollo impiega due tipi di frame: i frame di data e quelli di conferma (acknowledgement). Non supporta la tecnica del piggybacking (ovvero il trasporto degli ack dei frame ricevuti da un nodo nei successivi frame in uscita dal nodo stesso) e nè utilizza i risconti negativi (negative acknowledgements).

                          header                           |      payload
       +------------+------------+------------+------------+------- ... -------+
       |    kind    |     len    |  checksum  |     seq    |        msg        |
       | {DATA,ACK} |     int    |    int     |     int    |      char []      |
       +------------+------------+------------+------------+------- ... -------+
byte:  0            4            8           12           16                 16+len
Struttura di un frame nel linguaggio C per un tipo int a 32 bit.

I dettagli dell'implementazione in C sono descritti nel manuale di cnet. Per una più facile comprensione, riporto una versione in pseudo-codice del programma stopandwait.c, privata dei dettagli implementativi, non necessari in prima analisi. Per ogni event-handler viene indicato l'evento a cui risponde, una descrizione generale del suo scopo e il dettaglio delle azioni di cui si fa carico.


inizializzazioni statiche:

nextframetosend = 0 (numero di sequenza del prossimo frame da trasmettere)
frameexpected = 0 (numero di sequenza del prossimo frame atteso in ricezione)
ackexpected = 0 (numero di sequenza dell'ultimo frame trasmetto, di cui si attende conferma di ricezione)


reboot_node: EV_REBOOT. Inizializza il nodo e stabilisce il suo ruolo.

application_ready: EV_APPLICATIONREADY. Preleva e trasmette un messaggio.

physical_ready: EV_PHYSICALREADY. Riceve una conferma o un messaggio.

timeouts: EV_TIMER1.

Note:

Simulazioni passo-passo

Avvio

Per questo tipo di simulazione, adatta per comprendere un protocollo o per effettuarne il debugging, occorre avviare cnet con il seguente comando:

$ cnet -m 60 STOPANDWAIT
supponendo che l'intera simulazione non duri più di 60 minuti. Occorre specificare l'opzione -m altrimenti per default cnet termina dopo 3 minuti, probabilmente insufficienti per una esecuzione passo passo e occorrerà ricominciare tutto daccapo. Il motivo per cui esiste questo meccanismo di terminazione forzata è spiegato nelle FAQ. In breve si tratta di una misura per evitare problemi quando si lanciano dei protocolli scorretti.

Finestre di dialogo

schermata principale
La schermata principale della simulazione all'avvio.

Controllare gli attributi di default per i nodi e per i link definiti nel file STOPANDWAIT aprendo le finestre default node attributes e default link attributes dal menu Subwindow:

default node attributes
Gli attributi di default dei nodi.

default link attributes
Gli attributi di default dei link.

Per guadagnare spazio sul desktop, chiudete queste finestre e tenete invece aperte le finestre di output dei due nodi, che si possono far apparire o nascondere semplicemente facendo click su un nodo:

Perth
La finestra di output del nodo 0:Perth all'inizio.

Melbourne
Idem per il nodo 1:Melbourne.

Cliccando vicino ad un link appare la finestra di dialogo che mostra le statistiche della trasmissione relative a quel collegamento. Dato che i link sono di tipo bidirezionale, per selezionare il link in una direzione piuttosto che l'altra, fare click sul link dalla parte più vicina al suo nodo di sorgente:

Perth->Melbourne
Le statistiche del link Perth->Melbourne all'inizio.

Melbourne->Perth
Le statistiche del link Melbourne->Perth all'inizio.

Un valore -1 qui è speciale e sta ad indicare che verrà utilizzato il valore di default per quell'attributo. Invece uno 0 indica nessun costo o probabilità di errore nulla.

Gli attributi visualizzati dei nodi (la velocità di generazione dei messaggi e le loro dimensioni), come pure quelle dei link (costi di tramissione e probabilità di errore) o quelle di default globali per tutti i link e i nodi, possono essere modificati anche durante la simulazione, quindi il file STOPANDWAIT definisce solo i valori iniziali di questi parametri.

Visualizzazione dei frame

Limitatamente alle reti di soli 2 nodi, cnet è in grado di visualizzare in forma animata i frame, sia di dati sia di ack, che attraversano lo strato fisico, utilizzando delle piccole icone, il cui formato preciso è definito dal codice del protocollo. Valgono comunque le seguenti restrizioni sul formato delle icone: l'icona che rappresenta un frame è composta da una sequenza di semplici rettangoli colorati (sono programmabili solo la larghezza e colore). Al centro di ciascun frame animato può essere stampata una breve stringa, nel formato deciso dal codice del protocollo (questo consente ad esempio di mostrare, per tutta la durata dell'animazione del frame, i valori di alcuni campi del frame stesso, come ad es. il numero di sequenza).

In questo caso il file stopandwait.c stabilisce che ogni frame di ack abbia un solo campo, largo 10 pixel. I frame di dati invece hanno 2 campi, per un totale di 40 pixel di larghezza. I frame (sia di dati sia di ack) con un numero di sequenza pari a 0 hanno il loro primo campo di colore rosso; gli altri numeri di sequenza (solo 1 in questo protocollo) hanno invece questo campo di colore viola. Il secondo campo di un frame di dati, che rappresenta il payload, è colorato di verde. Un frame di ack porta stampato solo il suo numero di sequenza, mentre un frame di dati ha un'etichetta centrata col formato data=seq, dove seq è ancora il numero di sequenza del frame. La tabella seguente riassume tutte queste convenzioni grafiche:

Convenzioni per la visualizzazione dei frame per il protocollo stop-and-wait
frame parti significato
data=0 seq | payload frame di dati con seq=0
data=0 grayed seq | payload frame di dati corrotto con seq=0
data=0 lost seq | payload frame di dati perso con seq=0
data=1 seq | payload frame di dati con seq=1
data=1 grayed seq | payload frame di dati corrotto con seq=1
data=1 lost seq | payload frame di dati perso con seq=1
0 seq frame di ack con seq=0
0 grayed seq frame di ack corrotto con seq=0
0 lost seq frame di ack perso con seq=0
1 seq frame di ack con seq=1
1 grayed seq frame di ack corrotto con seq=1
1 lost seq frame di ack perso con seq=1

Notare che quando si perde un frame (sia di dati sia di ack), possibile se l'attributo probframeloss del link su cui viaggia è non nullo, cnet continua ad rendere con animazione il viaggio del frame fino a destinazione, ma gli cambia colore - il frame diventa tutto bianco, come se fosse un fantasma :-)

Invece quando un frame viene corrotto dal rumore (probframecorrupt diverso da zero), appare l'icona di un fulmine e il frame che giunge a destinazione avrà tutti i campi colorati di grigio.

Esempio di output

Si utilizza il tasto Single event per eseguire passo passo la simulazione. Ogni pressione del tasto fa riprendere la simulazione, che durerà fino a quando non si verifica un evento. Una volta che l'evento è stato gestito, lo scorrere del tempo di simulazione viene fermato, fino alla successiva pressione del tasto.

Premendo il tasto due volte, si verifica l'evento EV_REBOOT e vengono inizializzati entrambi i nodi. Si nota che compare un pulsante etichettato State nella finestra di output di ciascun nodo. La pressione di questo pulsante causa la stampa del valore corrente delle variabili di stato del protocollo stop-and-wait e viene contato come uno degli eventi di debug. Anche il disegno di un frame avviene per effetto di un evento speciale (EV_DRAWFRAME) e richiede quindi una pressione del tasto per generare il prossimo evento.

Ecco un esempio dell'output informativo generato in ogni nodo durante una simulazione; ogni volta che lo stato del protocollo nei nodi cambia è stato premuto il tasto State per produrne una traccia nell'output. Inoltre per facilitare la comprensione, l'output nei due nodi è stato indicato fianco a fianco, immaginando che la linea del tempo che scandisce gli eventi scorra dall'alto verso il basso e spezzato in più parti consecutive, ciascuna seguita da un commento che spiega in sintesi quello che è avvenuto.

Per evitare confusione e appesantimenti, nello stato di Perth è stata indicata solo la variabile frameexpected. Infatti, dal momento che Perth svolge solo il ruolo di ricevitore e non trasmette mai dati, le variabili ackexpected e nextframetosend rimangono sempre al loro valore iniziale (zero). Analogamente, Melbourne non riceve mai dati, quindi la sua variabile di stato frameexpected rimane non utilizzata al valore iniziale nullo e quindi è stata omessa.

Perth                           Melbourne

frameexpected   = 0
                                ackexpected     = 0
                                nextframetosend = 0

                                down from application, seq=0
                                DATA transmitted, seq=0

                                ackexpected     = 0
                                nextframetosend = 1

DATA received, seq=0, up to application
ACK transmitted, seq=0

frameexpected   = 1
                                ACK received, seq=0

                                ackexpected     = 1
                                nextframetosend = 1

Melbourne ha inviato un primo frame che è arrivato correttamente a Perth e questo ha subito trasmesso con successo l'ack.

Perth                           Melbourne

                                down from application, seq=1
                                DATA transmitted, seq=1

                                ackexpected     = 1
                                nextframetosend = 0

DATA received, seq=1, up to application
ACK transmitted, seq=1

frameexpected   = 0

                                ACK received, seq=1

                                ackexpected     = 0
                                nextframetosend = 0

Idem caso precedente, stavolta per un secondo frame trasmesso. Notate come prima Perth e poi Melbourne siano tornati nello stato iniziale e come, se le cose andassero sempre bene così, la numerazione dei frame (sia di dati sia di ack) mediante l'uso di un numero di sequenza (anche se solo di 1 bit) sarebbe inutile, anzi sarebbe inutile anche la trasmissione dei frame di ack. Evidentemente nella realtà i disturbi e le interferenze non si possono mai prevedere o eliminare del tutto.

Perth                           Melbourne

                                down from application, seq=0
                                DATA transmitted, seq=0

                                ackexpected     = 0
                                nextframetosend = 1

                                timeout, seq=0
                                DATA transmitted, seq=0

DATA received, seq=0, up to application
ACK transmitted, seq=0

frameexpected   = 1

                                BAD checksum - frame ignored

                                timeout, seq=0
                                DATA transmitted, seq=0

DATA received, seq=0, ignored
ACK transmitted, seq=0

                                ACK received, seq=0

                                ackexpected     = 1
                                nextframetosend = 1

Il terzo frame invece viene perduto e Melbourne ne ritenta l'invio non appena scade il suo tempo di attesa. Al secondo tentativo Melbourne ha successo, ovvero il frame viene ricevuto da Perth, ma purtroppo capita che la conferma di Perth contiene un errore. Melbourne, vedendosi arrivare un pacchetto errato, non sa se il terzo frame è stato ricevuto o meno e per sicurezza assume che non sia stato ricevuto e quindi scaduto il suo tempo di attesa ne rispedisce una copia. La copia arriva correttamente, ma Perth la ignora, rendendosi conto che il frame era già stato ricevuto, in quanto si aspettava un frame con numero di sequenza 1 e non 0. Comunque ne rimanda la conferma, che viene finalmente ricevuta correttamente e quindi si chiude anche la trasmissione di questo terzo frame. Notate come la numerazione dei frame sia stata essenziale affinché Perth abbia potuto riconoscere che si trattava di una copia e scartarla.

Simulazioni in velocità

La simulazione passo-passo è utile per analizzare nel dettaglio il comportamento di un protocollo. Se invece si vuole fare una semplice dimostrazione pratica del suo funzionamento, oppure si vuole testare il comportamento del protocollo a lungo termine e con grossi volumi di dati a scopo statistico, occorre aumentare la velocità di simulazione.

Ci sono due possibilità. La prima è di modificare alcuni attributi temporali nel file STOPANDWAIT, ad esempio i parametri:

messagerate      = 1000ms,
propagationdelay = 2000ms,
in
messagerate      = 10ms,
propagationdelay = 20ms,

Bisogna comunque fare attenzione a non abbassare troppo questi valori, in quanto potrebbero diventare paragonabili al tempo di elaborazione che passa tra l'invocazione di un handler e il successivo (durante il quale viene eseguito il codice del dispatcher di cnet). In tal caso cnet non riescirebbe più a tenere il passo della simulazione. Infatti cnet gira sotto Unix che è un sistema time-sharing e non real-time, quindi allo scadere del time slot assegnatogli, il processo sotto cui gira verrà interrotto per concedere un po' di tempo di CPU anche agli altri processi del sistema. Inoltre se cnet deve stampare molto output, specialmente se l'output avviene nelle finestre grafiche, non riuscirà a condurre la simulazione più velocemente oltre un certo limite.

Il secondo metodo, che non presenta il problema succitato, consiste nell'utilizzare l'opzione -T quando si avvia il simulatore, che trasforma cnet da un simulatore temporizzato in uno ad eventi discreti. Quando cnet agisce da simulatore temporizzato, utilizza l'orologio di sistema del computer per sincronizzare i tempi della simulazione con quelli realmente trascorsi. In questo caso un tempo di simulazione di 1 secondo corrisponde effettivamente ad 1 secondo del tempo dell'utente. Invece nella modalità di simulazione ad eventi discreta, cnet non aspetta 1 secondo di tempo reale per completare un secondo di tempo di simulazione, ma gli eventi vengono schedulati appena possibile (il tempo di simulazione comunque viene correttamente aggiornato). È proprio come se il tempo della simulazione fosse accelerato il più velocemente possibile, del fattore massimo consentito dalla velocità del proprio sistema di elaborazione.

Effetti della scelta dell'RTT

In questa simulazione metteremo in evidenza gli effetti di sovrastime e sottostime dell'RTT per questo protocollo del tipo fermati-e-aspetta.

Utilizziamo il file STOPANDWAIT e il programma stopandwait.c di default. Ripetiamo la simulazione per tre volte, per valori del coefficiente utilizzato per il calcolo dell'RTT pari a 0.5, 3, 18 (il fattore 3 di default è stato una volta diviso e una volta moltiplicato per 6). Si avvia ogni simulazione tramite il comando:

$ cnet -m 60 -q -s -T STOPANDWAIT

L'opzione -q sta per quiet e disabilita l'output nelle finestre di output dei nodi, che in questo tipo di simulazione non ci interessa. In questo modo risparmiamo memoria e tempo di esecuzione. L'opzione -z fa sì che all'uscita vengano stampate le statistiche in formato testo nella finestra del terminale da cui si è avviato cnet. Questo può essere utile per rielaborazioni successive.

Una volta apparsa la finestra principale di cnet, tramite il menù Subwindows far apparire la finestra statistics. Poi tramite il tasto Run avviare la simulazione e interromperla quando si raggiunge all'incirca un certo numero di messaggi generati dallo strato Applicativo prefissato e sufficientemente elevato, per esempio 50000. Ripetere questo procedimento per ciascuno dei coefficiente usati per il calcolo dell'RTT. Ogni volta che si modifica stopandwait.c, occorre uscire da cnet e riavviarlo per rendere effettive le modifiche (il codice del protocollo deve essere ricompilato).

Ecco un esempio dei possibili risultati per ciascuna prova, come appaiono sia nella finestra che nel terminale testuale:

coefficiente 0.5
2 HOSTS, 0 ROUTERS and 1 LINK
Simulation time              : 358400165175   
Events raised                : 777092
Messages generated           : 50038
Messages delivered           : 50038
Efficiency (bytes AL/PL)     : 13.81
Average delivery rate        : 0.56
Average delivery time        : 2788129
Frames transmitted           : 558738
Frames received              : 419391
Frames corrupted / collisions: 105284
Frames lost                  : 139345
EV_REBOOT                    : 2
EV_APPLICATIONREADY          : 50038
EV_PHYSICALREADY             : 419391
EV_TIMER1                    : 307661
statistics

coefficiente 3
2 HOSTS, 0 ROUTERS and 1 LINK
Simulation time              : 900077287909   
Events raised                : 343826
Messages generated           : 50093
Messages delivered           : 50092
Efficiency (bytes AL/PL)     : 31.15
Average delivery rate        : 0.22
Average delivery time        : 6713307
Frames transmitted           : 247163
Frames received              : 185579
Frames corrupted / collisions: 46569
Frames lost                  : 61583
EV_REBOOT                    : 2
EV_APPLICATIONREADY          : 50093
EV_PHYSICALREADY             : 185579
EV_TIMER1                    : 108152
statistics

coefficiente 18
2 HOSTS, 0 ROUTERS and 1 LINK
Simulation time              : 4138806541712
Events raised                : 343351
Messages generated           : 50021
Messages delivered           : 50020
Efficiency (bytes AL/PL)     : 31.28
Average delivery rate        : 0.05
Average delivery time        : 30120469
Frames transmitted           : 246888
Frames received              : 185337
Frames corrupted / collisions: 46441
Frames lost                  : 61551
EV_REBOOT                    : 2
EV_APPLICATIONREADY          : 50021
EV_PHYSICALREADY             : 185337
EV_TIMER1                    : 107991
statistics

Le grandezze di maggiore interesse nel nostro caso sono tre:

Efficienza - Efficiency (bytes AL/PL)
Questa misura adimensionale di efficienza è basata sul rapporto tra il numero dei byte generati dallo strato Applicativo (Application Layer, AL) e quelli trasmessi sullo strato Fisico (Physical Layer, PL). Nel caso ideale sarebbe 100%, ma siccome i protocolli richiedono che oltre ai dati vengano trasmesse anche dati di controllo (intestazioni) nei frame e nei pacchetti, che vengano ritramessi i frame di dati perduti e generati dei pacchetti al solo scopo di controllo (come ad es. gli ack), la percentuale può essere molto più bassa. Questo è il prezzo da pagare per fronteggiare il problema del rumore e assicurare comunque una connessione affidabile. A dispetto del nome, questo parametro non rappresenta l'unica variabile che determina l'efficienza di un protocollo, ma occorre considerare anche i parametri seguenti.
Velocità di consegna media - Average delivery rate
È definito come il rapporto tra il totale dei Kbyte ricevuti (1 KByte = 1024 bytes) dallo strato Applicativo e il tempo complessivo di durata del collegamento espresso in secondi (Simulation time). Naturalmente maggiore è questo rapporto, migliore è il collegamento.
Tempo di consegna medio - Average delivery time
È il rapporto tra il tempo complessivo impiegato per la consegna dei messaggi e il numero totale di messaggi consegnati (Messages delivered). In situazioni ideali è pari al propagationdelay. È misurato in microsecondi. Chiaramente interessa anche minimizzare questo tempo (si pensi ad applicazioni che devono trasmettere dati in tempo reale come in una conversazione telefonica o la trasmissione di un segnale video).

Ora è interessante valutare un parametro di efficienza che combina tutte e tre queste grandezze:

coefficiente 0.5 coefficiente 3 coefficiente 18
Efficiency (bytes AL/PL) * Average delivery rate
Average delivery time
2.77 1.02 0.05

Cosa significa questo? Siamo arrivati ai limiti dell'approccio stop-and-wait. Nonostante abbiamo un collegamento con una larghezza di banda di (bandwidth) di ben 100Mbps, con un protocollo di questo tipo i tempi di consegna medi rimangono sempre piuttosto bassi, dell'ordine di 1Kbps e non riusciamo ad utilizzare pienamente il nostro canale di comunicazione. Le cose migliorano, ma solo un po', quando si sottostima il timeout. In pratica quello che accade è che in questo caso di uno stesso pacchetto viaggiano più copie contemporaneamente sulla linea di comunicazione: in questo modo se qualcuna si perde o viene affetta dal rumore, il trasmettitore avrà già trasmesso un duplicato e si minimizzano i tempi di ritrassione, altrimenti lo sforzo fatto dal trasmettitore sarà del tutto inutile. Questa strategia si nota molto bene se si lancia il simulatore con il comando:

cnet -q STOPANDWAIT
avendo utilizzato un coefficiente (ad es.) 0.25 per il calcolo dell'RTT.

schermata principale
Effetto della sottostima dell'RTT.

Anche in questo modo però l'utilizzazione della banda non cresce molto. Per poter sfruttare meglio la capacità del canale occorre purtroppo ricorrere a protocolli più complessi, che trasmettono più frame diversi per volta e sono in grado di riordinare i frame anche se li ricevono integri non in ordine a causa degli effetti deleteri del rumore.

Debugging del protocollo

Cnet possiede alcune funzionalità addizionali, che facilitano il debugging dei protocolli e che sono descritte ampiamente nel manuale.

A scopo di esempio introduciamo esplicitamente un errore nel protocollo, modificando il file stopandwait.c ed eliminando (o commentando) il test che verifica che il numero di sequenza di un frame di ack sia quello atteso. Basta eliminare queste due righe all'interno della funzione physical_ready:

if (f.seq == ackexpected) {
    ...
}

Poi nella formula che stima l'RTT, sostituire il fattore 3.0 con un valore inferiore, come ad esempio 2.0 o anche 1.0 o 0.5 (la scelta che ho fatto io è 2.0). Abbiamo già visto gli effetti di questa modifica nella sezione precedente. In quel caso non produceva nessun errore, anzi appariva come migliorativa. Però adesso, avendo di fatto eliminato la numerazione dei frame di ack, vedremo che potranno sorgere dei problemi se l'RTT è sottostimato.

Al fine di non dover aspettare troppo che capiti qualche errore, utilizziamo la simulazione ad eventi discreti, per cui non occorre apportare alcuna modifica per restringere i tempi nel file STOPANDWAIT. Il comando per avviare la simulazione ora è:

$ cnet -o debug -T STOPANDWAIT

L'opzione -o fa si che lo standard output di ciascun nodo venga copiato in un singolo file. Il nome del file è costruito utilizzando il prefisso specificato come parametro dell'opzione -o (debug) seguito dal numero del nodo come estensione. Nel nostro caso verranno creati i file debug.node0 e debug.node1. La copia nei file è necessaria per poter poi analizzare con comodo l'intero output; infatti quando si verifica un errore la simulazione viene interrotta, appare una finestra di popup che descrive l'errore e la riga del codice sorgente (nonché il nodo) in cui si è verificato e non si può far altro che uscire dal programma:

Error
Esempio di come cnet indica gli errori in un protocollo.

Il messaggio di errore appare anche sul flusso di output standard di errore, visualizzato nella finestra di shell da cui è stato invocato cnet:

Error while executing Perth, file stopandwait.c, function physical_ready(), line 124 :
Application Layer given msg out of sequence

Ecco ora i log degli eventi accaduti ai due nodi, che a questo punto diventano fondamentali per riuscire a ricostruire l'accaduto:

nodo Perth
DATA received, seq=0, up to application   
ACK transmitted, seq=0

DATA received, seq=0, ignored
ACK transmitted, seq=0

DATA received, seq=0, ignored
ACK transmitted, seq=0

DATA received, seq=0, ignored
ACK transmitted, seq=0

DATA received, seq=0, ignored
ACK transmitted, seq=0

DATA received, seq=1, up to application
Error

nodo Melbourne
down from application, seq=0   
DATA transmitted, seq=0

timeout, seq=0
DATA transmitted, seq=0

timeout, seq=0
DATA transmitted, seq=0

timeout, seq=0
DATA transmitted, seq=0

timeout, seq=0
DATA transmitted, seq=0

ACK received, seq=0

down from application, seq=1
DATA transmitted, seq=1

ACK received, seq=0

down from application, seq=0
DATA transmitted, seq=0

timeout, seq=0
DATA transmitted, seq=0

ACK received, seq=0

down from application, seq=1
DATA transmitted, seq=1
Error

Ci aiuta anche la finestra delle statistiche:

statistics
Le statistiche fino al momento dell'errore.

In sintesi ecco quello che è successo: Melbourne ha ripetutamente tentato per 5 volte di inviare il primo frame (seq=0). Il frame è stato ricevuto correttamente da Perth che ha trasmesso un ack, anche questo ricevuto correttamente da Melbourne. Poi Melbourne ha trasmesso il secondo frame (seq=1), ma successivamente è arrivato l'ack relativo ad una delle repliche del primo frame. Melbourne, credendo che questo ack si riferisca al secondo frame appena trasmesso, ha continuato a trasmettere un terzo frame (seq=0), ripetendone la trasmissione per un paio di volte. In realtà però il secondo frame si era perso. Siamo già in errore, ma continuiamo ancora per vedere come va a finire: Melbourne riceve un ack che in realtà si riferisce ancora ad uno dei tentativi di trasmissione del primo frame e lo interpreta come se fosse l'ack per il terzo frame. Trasmette quindi un quarto frame (seq=1). Il quarto frame arriva a Perth, ma il secondo e il terzo non sono arrivati, è quindi fuori sequenza e solo a questo punto Perth si accorge dell'errore.

Questo mostra come un protocollo, che funzioni correttamente per alcuni minuti, può fallire ad uno stadio successivo della sua esecuzione, quando si verifica qualche situazione particolare e può essere complicato capire dove sta il problema se non si torna indietro fino al punto in cui si è verificato. È questo uno dei motivi per cui il debugging dei protocolli (e dei programmi in generale in informatica) è difficile e richiede molta esperienza e capacità di analisi.


<< Introduzione alla simulazione basata sugli eventi | Indice | Un segmento di rete Ethernet >>