Manuale di Network Simulator v.2

Capitolo 28 - Agenti UDP

a cura di Sandro Petrizzelli


 

Introduzione

 

Gli agenti UDP per NS sono implementati nei seguenti due file:

 

·      ~ns/udp.cc

·      ~ns/udp.h

 

 Un agente UDP accetta i dati dalle applicazioni in blocchi di dimensione variabile e esegue la segmentazione di tali dati se necessario (ossia se ciascun blocco ricevuto è troppo grande per essere contenuto in un unico pacchetto UDP).

 I pacchetti UDP contengono un numero di sequenza (che viene incrementato monotonicamente per ogni pacchetto) ed una etichetta temporale (timestamp) RTP. Questi due campi non sono in effetti presenti nei pacchetti UDP delle reti reali, ma sono comunque molto utili nelle simulazioni e, al tempo stesso, non comportano alcun sovraccarico sulle simulazioni stesse.

 La massima dimensione di un segmento UDP (MSS, maximum segment size) è impostata, per default, al valore 1000 byte:

 

Agent/UDP set packetSize_ 1000

 

 Questa variabile OTcl, “packetSize_” è collegata all’analoga variabile “size_” presente nel codice che implementa l’agente UDP in C++.

 Le applicazioni possono accedere agli agenti UDP usando la funzione C++ denominata “sendmsg()”. In alternativa, possono direttamente usare due metodi in OTcl, chiamati rispettivamente “send” e “sendmsg” ([1]). 

 

28.1 Struttura interna

 

La classe UDPAgent è definita nel file udp.h, nel modo seguente:

 

class UdpAgent : public Agent {

public:

   UdpAgent();

   UdpAgent(packet_t);

   virtual void sendmsg(int nbytes, const char *flags = 0);

protected:

   int seqno_;

   int off_rtp_;

};

 

 Come si può notare, si tratta di una definizione estremamente semplice. Sono intanto previsti due distinti costruttori, uno privo di argomenti e l’altro con un solo argomento, di tipo “packet type”. L’unico compito assolto da questi costruttori è quello di effettuare il binding delle variabili C++ di tale classe con le corrispondenti variabili accessibili in OTcl:

 

UdpAgent::UdpAgent() : Agent(PT_UDP), seqno_(-1)

{

    bind("packetSize_", &size_);

    bind("off_rtp_", &off_rtp_);

}

 

UdpAgent::UdpAgent(packet_t type) : Agent(type)

{

    bind("packetSize_", &size_);

}

 

 Il costruttore senza argomenti effettua il binding delle variabili C++ denominate size_ (che in OTcl diventa packetSize_) e off_rtp_ (che in OTcl assume lo stesso nome). Il costruttore con unico argomento si limita invece al binding di size_.

 Entrambi i costruttori, prima di compiere qualunque operazione, invocano il costruttore della classe base Agent.

 Tornando alla definizione della classe UdpAgent, si nota che vengono definite due sole variabili: seqno_ corrisponde al numero di sequenza attribuito ai pacchetti UDP; off_rtp_ serve invece ad individuare l’header RTP in ciascun pacchetto simulato, in modo da poter accedere ad alcuni campi di tale header usati anche dall’UDP.

 Infine, nella definizione della classe UdpAgent compare la funzione sendmsg(), che le applicazioni possono invocare per inviare i propri dati.

 Per concludere, nel file udp.cc è riportata la definizione della classe per il linkage tra la classe C++ degli agenti UDP e la corrispondente classe OTcl (denominata Agent/UDP):

 

static class UdpAgentClass : public TclClass {

public:

    UdpAgentClass() : TclClass("Agent/UDP") {}

    TclObject* create(int, const char*const*) {

         return (new UdpAgent());

    }

} class_udp_agent;

 

28.1.1 Funzione sendmsg()

 

Questa funzione viene definita nel file udp.cc nel modo seguente:

 

void UdpAgent::sendmsg(int nbytes, const char* flags)

{

     Packet *p;

     int n;

 

     if (size_)

           n = nbytes / size_;

     else

           printf("Error: UDP size = 0\n");

 

     if (nbytes == -1) {

           printf("Error:  sendmsg() for UDP should not be -1\n");

           return;

     }    

     double local_time =Scheduler::instance().clock();

     while (n-- > 0) {

           p = allocpkt();

           hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);

           rh->flags() = 0;

           rh->seqno() = ++seqno_;

           hdr_cmn::access(p)->timestamp() =

              (u_int32_t)(SAMPLERATE*local_time);

           // add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)

           if (flags && (0 ==strcmp(flags, "NEW_BURST")))

                rh->flags() |= RTP_M;

           target_->recv(p);

     }

     n = nbytes % size_;

     if (n > 0) {

           p = allocpkt();

           hdr_cmn::access(p)->size() = n;

           hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);

           rh->flags() = 0;

           rh->seqno() = ++seqno_;

           hdr_cmn::access(p)->timestamp() =

              (u_int32_t)(SAMPLERATE*local_time);

           // add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)

           if (flags && (0 == strcmp(flags, "NEW_BURST")))

                rh->flags() |= RTP_M;

           target_->recv(p);

     }

     idle();

}

 

 Non essendo stata definita una apposita funzione command() per la classe UdpAgent, viene utilizzata quella della classe superiore, ossia Agent: di conseguenza, dato che nella definizione della funzione Agent::command() (nel file agent.cc), il comando “sendmsg” comporta la chiamata della funzione sendmsg(), è proprio la funzione UdpAgent::sendmsg() ad essere usata per trasmettere eventuali dati di livello application.

 Tale funzione, come si vede nel relativo listato, vuole in ingresso il numero nbytes di byte di dati di livello application da trasferire. Tramite l’istruzione “n=nbytes/size_”, la funzione calcola il numero n di pacchetti UDP necessari per trasferire i suddetti dati. Da notare, a questo proposito, che vengono visualizzati dei messaggi di errore qualora il numero di byte sia nullo (nel qual caso l’agente UDP segnala sostanzialmente di non aver nulla da fare) oppure sia pari a –1 (il che equivarrebbe ad un trasferimento indefinito di byte, che però non è supportato dagli agenti UDP).

 Successivamente, una volta stabiliti quanti pacchetti vanno creati ed inviati, la funzione passa ad allocare tali pacchetti uno per volta, usando un ciclo while:

 

 

 A questo punto, per simulare la consegna del pacchetto a destinazione, viene invocato il metodo recv() dell’oggetto puntato dal puntatore target_: quest’ultimo è un puntatore ereditato dalla classe Connector (da cui deriva la classe Agent), che viene impostato, all’atto della configurazione della simulazione, in modo che punti proprio all’oggetto ricevente. E’ anche possibile impostare, direttamente in OTcl, l’indirizzo contenuto in target_, in modo da stabilire “manualmente” l’oggetto che dovrà ricevere i pacchetti creati.

 Infine, la funzione sendmsg() invoca la funzione Agent::idle(), che gli agenti possono usare per segnalare all’applicazione di aver terminato il proprio compito. Il listato della funzione, riportato nel file agent.cc, è il seguente:

 

void Agent::idle()

{

    if (app_)

         app_->resume();

}

 

 Questa funzione non fa altro che invocare a sua volta la funzione Application::resume() dell’applicazione sovrastante, individuata tramite l’indirizzo contenuto nella variabile puntatore app_. Il listato della funzione Application::resume() è altrettanto semplice:

 

void Application::resume()

{

     if (! enableResume_)

           return;

     Tcl& tcl = Tcl::instance();

     tcl.evalf("%s resume", name_);

}

 

28.2 Esempio

Quello che segue è un semplice esempio di come un agente UDP possa essere usato in una simulazione. In tale esempio, viene usata, come applicazione, un generatore di traffico CBR (Constant Bit Rate), il quale si appoggia appunto su un agente UDP situato nel suo stesso nodo. La simulazione viene avviata all’istante 1.0, in corrispondenza del quale il generatore di traffico comincia a chiamare periodicamente l’agente UDP tramite la funzione “sendmsg()”:

 

set ns [new Simulator]

set n0 [$ns node]

set n1 [$ns node]

$ns duplex-link $n0 $n1 5Mb 2ms DropTail

 

set udp0 [new Agent/UDP]

$ns attach-agent $n0 $udp0

set cbr0 [new Application/Traffic/CBR]

$cbr0 attach-agent $udp0

$udp0 set packetSize_ 536    ;# set MSS to 536 bytes

 

set null0 [new Agent/Null]

$ns attach-agent $n1 $null0

$ns connect $udp0 $null0

$ns at 1.0 "$cbr0 start"

 

Le prime quattro righe di questo codice servono ad impostare la topologia della rete, costituita da due nodi collegati da un link bidirezionale (banda 5 Mbit/s, ritardo 2 ms, disciplina di coda di tipo DropTail).

Il successivo blocco di righe serve a creare l’agente di trasporto (Agent/UDP) e l’applicazione (Application/Traffic/CBR) che userà tale agente per inviare i propri dati.

La prima riga dell’ultimo blocco crea un “null agent” (Agent/Null), ossia una specie di semplice “cestino” che riceve i pacchetti dal mittente e non compie alcuna operazione su di essi. La riga successiva collega tale agente al nodo destinazione (n1). La riga ancora successiva stabilisce la connessione a livello di trasporto, tra l’agente UDP mittente e il null agent ricevente. L’ultima riga, infine, indica allo schedulatore degli eventi di impartire, all’applicazione CBR, la direttiva “start” in corrispondenza dell’istante t=1 secondo.

 

28.3 Comandi principali

 

Vediamo infine una lista dei principali comandi da usarsi per impostare un agente UDP in una simulazione:

 

·     set udp0 [new Agent/UDP]

Questo comando crea l’agente UDP desiderato, assegnandogli il riferimento udp0, che l’utente potrà poi usare per la configurazione dell’agente stesso

 

·     $ns_ attach-agent <node> <agent>

Questo comando serve a collegare l’agente <agent> al nodo <node>

 

·     $traffic-gen attach-agent <agent>

Questo comando corrisponde ad un metodo della classe Application/Traffic/<traffictype> e serve a connettere il generatore di traffico in questione con un agente <agent> specificato. Ad esempio, se volessimo impostare un generatore di traffico CBR affinché usi un agente UDP precedentemente istanziato e denominato udp1, dovremmo semplicemente scrivere quanto segue:

 

set cbr1 [new Application/Traffic/CBR]

$cbr1 attach-agent $udp1

 

·     $ns connect <src-agent> <dst-agent>

Questo comando imposta la connessione end-to-end tra due agenti (a livello di trasporto), rispettivamente <src-agent> (sorgente) e <dst-agent> (destinazione)

 

Segue adesso una breve lista dei comandi da usare per impostare i valori di alcuni parametri caratteristici degli agenti UDP (i valori di default per questi parametri si possono individuare nel file ~ns/tcl/lib/ns-default.tcl):

 

$udp set packetSize_ <pktsize>

$udp set dst_addr_ <address>

$udp set dst_port_ <portnum>

$udp set class_ <class-type>

$udp set ttl_ <time-to-live>

..... etc



[1] Si veda, in proposito, il paragrafo 32.2.3

 

 


Continua con:        Indice       Capitolo 27      Capitolo 29


Aggiornamento: 29 aprile 2001

home