Manuale di Network Simulator v.2
Capitolo 28 - Agenti UDP
a cura di Sandro Petrizzelli
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]).
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;
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:
per prima cosa, la funzione Agent::allocpkt()
si occupa di allocare e riempire i campi fondamentali degli header di pacchetto;
successivamente, vengono riempiti alcuni
campi dell’header RTP (da cui la variabile off_rtp_)
che vengono usati come campi anche dell’header UDP: si tratta dei campi flags_
(posto a 0) e seqno_ (incrementato di 1 per ogni
pacchetto generato). Viene anche riempito il campo timestamp_
del common header.
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_);
}
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.
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
Continua con: Indice Capitolo 27 Capitolo 29
Aggiornamento: 29 aprile 2001