Tutorial sul simulatore di rete ns-3. capitolo 4

Tutorial sul simulatore di rete ns-3. capitolo 4
capitolo 1,2
capitolo 3

4 Panoramica dei concetti
4.1 Astrazioni chiave
4.1.1 Nodo
4.1.2 Applicazione
4.1.3 Canale
4.1.4 Dispositivo di rete
4.1.5 Assistenti topologici
4.2 Primo script ns-3
4.2.1 Codice caldaia
4.2.2 Plug-in
4.2.3 Spazio dei nomi ns3
4.2.4 Registrazione
4.2.5 Funzione principale
4.2.6 Utilizzo degli assistenti di topologia
4.2.7 Utilizzo dell'applicazione
4.2.8 Simulatore
4.2.9 Costruire il tuo script
4.3 ns-3 Codice sorgente

Capitolo 4

Panoramica del concetto

La prima cosa che dobbiamo fare prima di iniziare a imparare o scrivere il codice ns-3 è spiegare alcuni concetti e astrazioni di base del sistema. Gran parte di ciò può sembrare ovvio ad alcuni, ma ti consigliamo di dedicare del tempo a leggere questa sezione per assicurarti di iniziare su basi solide.

4.1 Astrazioni chiave

In questa sezione esamineremo alcuni termini comunemente utilizzati sul Web ma che hanno un significato specifico in ns-3.

4.1.1 Nodo

Nel gergo di Internet, un dispositivo informatico che si connette a una rete è chiamato host o talvolta sistema finale. Poiché ns-3 è un simulatore di rete e non un simulatore di Internet, non utilizziamo deliberatamente il termine host, poiché è strettamente correlato a Internet e ai suoi protocolli. Utilizziamo invece un termine più generale, utilizzato anche da altri simulatori, che ha origine nella teoria dei grafi: nodo (nodo).

In ns-3, l'astrazione sottostante di un dispositivo informatico è chiamata nodo. Questa astrazione è rappresentata in C++ dalla classe Node. Classe NodoNodo (nodo) fornisce metodi per manipolare le rappresentazioni dei dispositivi informatici nelle simulazioni.

Devi capire Nodo come un computer a cui aggiungi funzionalità. Aggiungerai elementi come applicazioni, stack di protocolli e schede periferiche con driver che consentono al computer di svolgere un lavoro utile. Usiamo lo stesso modello base in ns-3.

4.1.2 Applicazione

In generale, il software per computer è diviso in due grandi classi. Il software di sistema organizza varie risorse del computer come memoria, cicli del processore, disco, rete, ecc. Secondo un modello di calcolo. Il software di sistema in genere non utilizza queste risorse per eseguire attività a diretto vantaggio dell'utente. Un utente in genere esegue un'applicazione per raggiungere un obiettivo specifico, che ottiene e utilizza risorse controllate dal software di sistema.

Spesso la linea di separazione tra il sistema e il software applicativo viene tracciata in corrispondenza dei cambiamenti a livello di privilegi che si verificano nelle trappole del sistema operativo. ns-3 non ha un vero e proprio concetto di sistema operativo e quindi nessun concetto di livelli di privilegio o chiamate di sistema. Tuttavia, abbiamo un'idea per un'app. Proprio come nel “mondo reale” le applicazioni software vengono eseguite sui computer per eseguire attività, le applicazioni ns-3 vengono eseguite sui nodi ns-3 per controllare le simulazioni nel mondo simulato.

In ns-3, l'astrazione di base per un programma utente che genera attività per la modellazione è un'applicazione. Questa astrazione è rappresentata in C++ dalla classe Application. La classe Application fornisce metodi per manipolare le visualizzazioni della nostra versione delle applicazioni a livello utente nelle simulazioni. Ci si aspetta che gli sviluppatori specializzino la classe Application in un senso di programmazione orientata agli oggetti per creare nuove applicazioni. In questo tutorial utilizzeremo le specializzazioni della classe Application chiamata Applicazione UdpEchoClient и UdpEchoServerApplicazione. Come ci si potrebbe aspettare, queste applicazioni costituiscono un insieme di applicazioni client/server utilizzate per generare ed eseguire l'eco dei pacchetti di rete.

4.1.3 Canale

Nel mondo reale, puoi connettere un computer a una rete. Spesso i media su cui vengono trasmessi i dati in queste reti sono chiamati canali. Quando colleghi un cavo Ethernet a una presa a muro, stai collegando il computer a un collegamento Ethernet. Nel mondo simulato ns-3, un nodo è collegato a un oggetto che rappresenta un canale di comunicazione. In questo caso l'astrazione di base della sottorete di comunicazione è chiamata canale ed è rappresentata in C++ dalla classe Channel.

classe CanaleCanale fornisce metodi per gestire l'interazione degli oggetti della sottorete e connettere i nodi ad essi. I canali possono anche essere specializzati dagli sviluppatori in un senso di programmazione orientata agli oggetti. La specializzazione del canale può modellare qualcosa di semplice come un filo. Un canale dedicato può anche modellare cose complesse come un grande switch Ethernet o uno spazio tridimensionale pieno di ostacoli nel caso delle reti wireless.

In questo tutorial utilizzeremo versioni specializzate del canale chiamate CsmaChannelCsmaChannel, Punto a punto canale Punto a punto canale и WifiChannelWifiChannel. Canale Csma, ad esempio, modella una versione di una sottorete di comunicazione che implementa un ambiente di comunicazione ad accesso multiplo con rilevamento del gestore. Questo ci offre funzionalità simili a Ethernet.

4.1.4 Dispositivo di rete

In passato, se si desiderava connettere un computer a una rete, era necessario acquistare un cavo di rete specifico e un dispositivo hardware chiamato (nella terminologia PC) una scheda periferica che doveva essere installata nel computer. Se una scheda periferica implementava alcune funzioni di rete, veniva chiamata scheda di interfaccia di rete o scheda di rete. Oggi, la maggior parte dei computer è dotata di hardware di interfaccia di rete integrato e non viene vista dagli utenti come dispositivi separati.

Una scheda di rete non funzionerà senza un driver software che ne controlli l'hardware. In Unix (o Linux), un'apparecchiatura periferica è classificata come dispositivo. I dispositivi vengono gestiti utilizzando i driver di dispositivo e i dispositivi di rete (NIC) vengono gestiti utilizzando i driver di dispositivo di rete (driver dei dispositivi di rete) e sono collettivamente chiamati dispositivi di rete (dispositivi di rete). In Unix e Linux, ti riferisci ai dispositivi di rete con nomi come eth0.

In ns-3, l'astrazione del dispositivo di rete abbraccia sia il software del driver che l'hardware da modellare. Nella simulazione, un dispositivo di rete viene "installato" in un nodo per consentirgli di comunicare con altri nodi attraverso canali. Proprio come un vero computer, un nodo può essere connesso a più canali attraverso più dispositivi NetDevice.

L'astrazione di rete di un dispositivo è rappresentata in C++ dalla classe NetDevice... Classe NetDevice fornisce metodi per la gestione delle connessioni agli oggetti Nodo e Canale; e può essere specializzato dagli sviluppatori nel senso della programmazione orientata agli oggetti. In questo tutorial utilizzeremo diverse versioni specializzate di NetDevice chiamate Dispositivo CsmaNet, Dispositivo PointToPointNet и Dispositivo WifiNet. Proprio come un adattatore di rete Ethernet è progettato per funzionare con una rete Ethernet, Dispositivo CsmaNet progettato per funzionare con Canale Csma, Dispositivo PointToPointNet progettato per funzionare con Canale PuntoAPuntoE Dispositivo WifiNet - Progettato per funzionare con Canale Wifi.

4.1.5 Assistenti topologici

In una rete reale troverai computer host con schede di rete aggiunte (o integrate). In ns-3 diremmo che vedrai i nodi con NetDevices collegati. In una grande rete simulata, dovrai organizzare le connessioni tra molti oggetti Nodo, NetDevice и canale.

Poiché si collegano i NetDevice ai nodi, i NetDevice ai collegamenti, si assegnano indirizzi IP, ecc. in ns-3 sono un compito comune, per renderlo il più semplice possibile forniamo i cosiddetti aiutanti della topologia. Ad esempio, per creare un NetDevice, è necessario eseguire molte operazioni del kernel ns-3, aggiungere un indirizzo MAC, installare il dispositivo di rete nel nodo, configurare lo stack di protocolli del nodo e quindi connettere il NetDevice al canale. Sarà necessario ancora più lavoro per connettere più dispositivi a collegamenti multipunto e quindi connettere le singole reti in una rete Internetworks. Forniamo oggetti di supporto per la topologia che combinano queste numerose operazioni in un modello facile da usare per la tua comodità.

4.2 Primo script ns-3

Se hai installato il sistema come suggerito sopra, avrai la versione ns-3 in una directory chiamata repos nella tua home directory. Vai alla directory rilasciare

Se non disponi di una directory di questo tipo, significa che non hai specificato la directory di output durante la creazione della versione di rilascio di ns-3, compila in questo modo:
$ ./waf configure —build-profile=release —out=build/release,
$ ./waf build

lì dovresti vedere una struttura di directory simile alla seguente:

AUTHORS       examples      scratch       utils       waf.bat*
bindings      LICENSE       src           utils.py    waf-tools
build         ns3           test.py*      utils.pyc   wscript
CHANGES.html  README        testpy-output VERSION     wutils.py
doc           RELEASE_NOTES testpy.supp   waf*        wutils.pyc

Vai alla directory esempi/tutorial. Dovresti vedere un file situato lì chiamato prima.cc. Questo è uno script che creerà una semplice connessione punto a punto tra due nodi e trasmetterà un pacchetto tra i nodi. Diamo un'occhiata a questo script riga per riga; per fare ciò, apri first.cc nel tuo editor preferito.

4.2.1 Codice caldaia
La prima riga nel file è la riga della modalità editor emacs. Indica a emacs le convenzioni di formattazione (stile di codifica) che utilizziamo nel nostro codice sorgente.

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

Questa è sempre una questione piuttosto controversa, quindi dobbiamo mettere le cose in chiaro per toglierla di mezzo subito. Il progetto ns-3, come la maggior parte dei progetti di grandi dimensioni, ha adottato uno stile di codifica a cui tutto il codice contribuito deve conformarsi. Se vuoi contribuire con il tuo codice al progetto, dovrai eventualmente conformarti allo standard di codifica ns-3, come descritto nel file doc/codingstd.txt o mostrato sulla pagina web del progetto: https://www.nsnam.org/develop/contributing-code/coding-style/.

Ti consigliamo di abituarti all'aspetto del codice ns-3 e di applicare questo standard ogni volta che lavori con il nostro codice. L'intero team di sviluppo e i contributori hanno accettato questo dopo alcune lamentele. La riga della modalità emacs sopra semplifica la formattazione corretta se si utilizza l'editor emacs.

Il simulatore ns-3 è concesso in licenza utilizzando GNU General Public License. Vedrai l'intestazione legale GNU appropriata in ciascun file di distribuzione ns-3. Spesso vedrai un avviso di copyright per una delle istituzioni partecipanti al progetto ns-3 sopra il testo e l'autore GPL, mostrati di seguito.

/* 
* This program is free software; you can redistribute it and/or modify 
* it under the terms of the GNU General Public License version 2 as 
* published by the Free Software Foundation; 
*
* This program is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
* GNU General Public License for more details. 
* 
* You should have received a copy of the GNU General Public License 
* along with this program; if not, write to the Free Software 
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

4.2.2 Plug-in

Il codice stesso inizia con una serie di istruzioni di inclusione (includere).

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"

Per aiutare i nostri utenti di scripting di alto livello a far fronte al gran numero di file di intestazione presenti nel sistema, li raggruppiamo in base al loro utilizzo in moduli di grandi dimensioni. Forniamo un singolo file di intestazione che caricherà in modo ricorsivo tutti i file di intestazione utilizzati in un determinato modulo. Invece di dover cercare esattamente l'intestazione di cui hai bisogno ed eventualmente ottenere l'elenco corretto delle dipendenze, ti diamo la possibilità di scaricare un gruppo di file con grande granularità. Non è l'approccio più efficiente, ma sicuramente rende molto più semplice la scrittura degli script.

Ciascuno dei file include ns-3 viene inserito in una directory chiamata ns3 (sottodirectory di compilazione) per evitare conflitti di nomi di file durante il processo di compilazione. File ns3/modulo-core.h corrisponde al modulo ns-3, che troverai nella directory sorgente/nucleo nella versione installata. Nell'elenco di questa directory troverai un gran numero di file header. Quando fai l'assemblaggio, Waf inserisce i file di intestazione pubblici nella directory ns3 in una sottodirectory costruire/debug

Se non disponi di una directory di questo tipo, significa che non hai specificato la directory di output durante la creazione della versione di rilascio di ns-3, compila in questo modo:
$ ./waf configure --build-profile=debug --out=build/debug
$ ./waf build
o
$ ./waf configure --build-profile=ottimizzato --out=build/ottimizzato
$ ./waf build

o costruire/ottimizzare, a seconda della configurazione. Waf genererà inoltre automaticamente un file di inclusione del modulo per caricare tutti i file di intestazione pubblici. Poiché ovviamente stai seguendo religiosamente questa guida, l'hai già fatto

$ ./waf -d debug --enable-examples --enable-tests configure

per configurare il progetto per eseguire build di debug che includono esempi e test. Anche tu lo hai fatto

$ ./waf

per assemblare il progetto. Quindi ora quando guardi nella directory ../../build/debug/ns3, poi lì troverai, tra gli altri, i file header dei quattro moduli mostrati sopra. Puoi guardare il contenuto di questi file e scoprire che includono tutti i file pubblici utilizzati dai moduli corrispondenti.

4.2.3 Spazio dei nomi ns3

Riga successiva nello script prima.cc è una dichiarazione dello spazio dei nomi.

using namespace ns3;

Il progetto ns-3 è implementato in uno spazio dei nomi C++ chiamato ns3. Questo raggruppa tutte le dichiarazioni relative a ns-3 in un ambito esterno allo spazio dei nomi globale, il che si spera possa aiutare con l'integrazione con altro codice. L'utilizzo dell'operatore C++ introduce lo spazio dei nomi ns-3 nella regione dichiarativa (globale) corrente. Questo è un modo elegante per dire che dopo questa dichiarazione, non sarà necessario digitare l'operatore di autorizzazione ns3::scope prima di tutto il codice ns-3 per usarlo. Se non hai familiarità con gli spazi dei nomi, fai riferimento a quasi tutti i libri di testo C++ e confronta lo spazio dei nomi ns3 utilizzando lo spazio dei nomi e la dichiarazione std using namespace std; negli esempi di lavoro con l'operatore di output cout e flussi.

4.2.4 Registrazione

La riga successiva dello script è,

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Utilizzeremo questa dichiarazione come un luogo conveniente per discutere del nostro sistema di documentazione Doxygen. Se guardi il sito web del progetto ns-3, troverai un collegamento alla documentazione nella barra di navigazione. Se fai clic su questo collegamento verrai indirizzato alla nostra pagina di documentazione. È presente un collegamento "Ultima versione" che ti porterà alla documentazione dell'ultima versione stabile di ns-3. Se selezioni il collegamento "Documentazione API", verrai indirizzato alla pagina della documentazione dell'API ns-3.

Nella parte sinistra della pagina troverete una rappresentazione grafica della struttura della documentazione. Un buon punto di partenza è il "libro" Moduli ns-3 nell'albero di navigazione di ns-3. Se riveli moduli, verrà visualizzato un elenco della documentazione dei moduli ns-3. Come discusso in precedenza, il concetto di modulo qui è direttamente correlato ai file inclusi nel modulo precedente. Il sottosistema di registrazione ns-3 è discusso nella sezione Utilizzo del modulo di registrazione, quindi ci torneremo più avanti in questo tutorial, ma puoi conoscere l'affermazione precedente guardando il modulo Nucleoe poi aprire il libro Strumenti di debuge quindi selezionando la pagina Registrazione. Clicca su Registrazione.

Ora dovresti rivedere la documentazione Doxygen per modulo Registrazione. Nell'elenco delle macro nella parte superiore della pagina, vedrai una voce per NS_LOG_COMPONENT_DEFINE. Prima di fare clic sul collegamento, assicurati di guardare la "Descrizione dettagliata" del modulo di registrazione per capire come funziona in generale. Per fare ciò puoi scorrere verso il basso o selezionare "Altro..." sotto il grafico.

Una volta che hai un'idea generale di cosa sta succedendo, vai avanti e guarda la documentazione per lo specifico NS_LOG_COMPONENT_DEFINE. Non duplicherò la documentazione qui, ma per riassumere, questa riga dichiara un componente di registrazione chiamato FirstScriptEsempio, che consente di abilitare o disabilitare la registrazione dei messaggi sulla console facendo riferimento a un nome.

4.2.5 Funzione principale

Nelle righe seguenti dello script vedrai,

int 
main (int argc, char *argv[])
{ 

Questa è semplicemente una dichiarazione della funzione principale del tuo programma (script). Come con qualsiasi programma C++, è necessario definire una funzione principale, questa verrà eseguita per prima. Non c'è niente di speciale qui. Il tuo script ns-3 è solo un programma C++. La riga seguente imposta la risoluzione temporale su 1 nanosecondo, che è l'impostazione predefinita:

Time::SetResolution (Time::NS);

La risoluzione temporale, o semplicemente risoluzione, è il più piccolo valore temporale che può essere utilizzato (la più piccola differenza rappresentabile tra due tempi). Puoi modificare la risoluzione esattamente una volta. Il meccanismo che fornisce questa flessibilità consuma memoria, quindi una volta impostata esplicitamente la risoluzione, liberiamo la memoria, impedendo ulteriori aggiornamenti. (Se non imposti la risoluzione in modo esplicito, verrà impostata per impostazione predefinita su un nanosecondo e la memoria verrà liberata all'avvio della simulazione.)

Le seguenti due righe di script vengono utilizzate per abilitare due componenti di registrazione integrati nelle applicazioni EchoClient и Ecoserver:

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO); LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);

Se leggi la documentazione per il componente Logging, vedrai che esistono diversi livelli di logging/granularità che puoi abilitare su ciascun componente. Queste due righe di codice abilitano la registrazione del debug al livello INFO per client e server echo. A questo livello, l'applicazione stamperà i messaggi mentre invia e riceve pacchetti durante la simulazione.

Ora ci occuperemo della creazione della topologia e dell'esecuzione della simulazione. Utilizziamo oggetti helper della topologia per rendere questo lavoro il più semplice possibile.

4.2.6 Utilizzo degli assistenti di topologia

Le prossime due righe di codice nel nostro script creeranno effettivamente gli oggetti Node ns-3 che rappresenteranno i computer nella simulazione.

NodeContainer nodes;
nodes.Create (2);

Prima di continuare, troviamo la documentazione della classe NodoContenitore. Un altro modo per accedere alla documentazione di una determinata classe è tramite la scheda Classi sulle pagine Doxygen. Se hai già Doxygen aperto, scorri semplicemente fino alla parte superiore della pagina e seleziona la scheda Classi. Dovresti vedere una nuova serie di schede, una delle quali è un elenco di classi. Sotto questa scheda vedrai un elenco di tutte le classi ns-3. Scorri verso il basso fino a ns3::ContenitoreNodo. Quando trovi una classe, selezionala per accedere alla documentazione della classe.

Come ricordiamo, una delle nostre astrazioni chiave è il nodo. Rappresenta il computer al quale aggiungeremo elementi come stack di protocolli, applicazioni e schede periferiche. Assistente di topologia NodoContenitore fornisce un modo conveniente per creare, gestire e accedere a qualsiasi oggetto Nodo, che creiamo per eseguire la simulazione. La prima riga sopra dichiara semplicemente NodoContenitore, che chiamiamo nodi. La seconda riga chiama il metodo Create sull'oggetto nodes e chiede al contenitore di creare due nodi. Come descritto in Doxygen, il contenitore richiede al sistema ns-3 di creare due oggetti Nodo e memorizza internamente i puntatori a questi oggetti.

I nodi creati nello script non fanno ancora nulla. Il prossimo passo nella costruzione della topologia è connettere i nostri nodi alla rete. La forma più semplice di rete che supportiamo è una connessione punto a punto tra due nodi. Ora creeremo tale connessione.

PuntoAPuntoHelper

Creiamo una connessione punto a punto utilizzando un modello familiare, utilizzando un oggetto helper della topologia per eseguire il lavoro di basso livello richiesto per la connessione. Ricordiamo le nostre due astrazioni chiave NetDevice и canale. Nel mondo reale, questi termini corrispondono approssimativamente a schede periferiche e cavi di rete. In genere, queste due cose sono strettamente correlate tra loro e nessuno può contare sulla condivisione, ad esempio, dei dispositivi Ethernet su un canale wireless. I nostri assistenti per la topologia seguono questa stretta relazione e pertanto utilizzerai un singolo oggetto in questo scenario PuntoAPuntoHelper per configurare e connettere oggetti ns-3 Dispositivo PointToPointNet и Canale PuntoAPunto. Le tre righe successive nello script:

PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); 
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

Prima linea,

PointToPointHelper pointToPoint;

crea un'istanza di un oggetto nello stack PuntoAPuntoHelper. Da un punto di vista di livello superiore la riga seguente,

pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));

racconta l'oggetto PuntoAPuntoHelper utilizzare il valore "5 Mbit/s" (cinque megabit al secondo) come "Velocità dati'.

Da un punto di vista più specifico, la stringa "DataRate" corrisponde a quello che chiamiamo attributo Dispositivo PointToPointNet. Se guardi Doxygen per classe ns3::PointToPointNetDevice e nella documentazione del metodo Ottieni IDTipo troverai l'elenco degli attributi definiti per il dispositivo. Tra questi ci sarà l'attributo “Velocità dati" La maggior parte degli oggetti ns-3 visibili all'utente hanno elenchi di attributi simili. Utilizziamo questo meccanismo per impostare facilmente la simulazione senza ricompilazione, come vedrai nella sezione successiva.

Simile a "Velocità dati" in PointToPointNetDevice troverai l'attributo "Delay" associato a PointToPointChannel. La riga finale

pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

parla PuntoAPuntoHelper utilizzare il valore "2 ms" (due millisecondi) come valore del ritardo di propagazione per il collegamento punto a punto creato successivamente.

NetDeviceContenitore

Al momento abbiamo nella sceneggiatura NodoContenitore, che contiene due nodi. Abbiamo PuntoAPuntoHelper, che è preparato per la creazione di oggetti Dispositivi PointToPointNet e collegarli utilizzando un oggetto PointToPointChannel. Proprio come abbiamo utilizzato l'oggetto helper della topologia NodeContainer per creare i nodi, chiederemo PuntoAPuntoHelper eseguire per noi lavori relativi alla creazione, configurazione e installazione dei nostri dispositivi. Abbiamo bisogno di un elenco di tutti gli oggetti creati NetDevice, quindi usiamo NetDeviceContenitore per conservarli nello stesso modo in cui li utilizzavamo NodoContenitore per memorizzare i nodi che abbiamo creato. Le prossime due righe di codice,

NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);

completare la configurazione del dispositivo e del canale. La prima riga dichiara il contenitore del dispositivo menzionato sopra, mentre la seconda svolge il lavoro principale. Metodo Installazione oggetto PuntoAPuntoHelper prende NodoContenitore come parametro. Dentro NetDeviceContenitore per ciascun nodo situato in NodoContenitore viene creato (per la comunicazione punto a punto devono essercene esattamente due) Dispositivo PointToPointNet viene creato e salvato nel contenitore del dispositivo. Canale PuntoAPunto viene creato e due sono collegati ad esso Dispositivi PointToPointNet. Dopo aver creato gli oggetti, gli attributi memorizzati in PuntoAPuntoHelper, vengono utilizzati per inizializzare gli attributi corrispondenti negli oggetti creati.

Dopo aver effettuato una chiamata pointToPoint.Install (nodi) avremo due nodi, ciascuno con un dispositivo di rete punto-punto installato e un collegamento punto-punto tra di loro. Entrambi i dispositivi saranno configurati per trasmettere dati a una velocità di cinque megabit al secondo con un ritardo di trasmissione di due millisecondi sul canale.

Internet StackHelper

Ora abbiamo nodi e dispositivi configurati, ma sui nostri nodi non sono installati stack di protocolli. Le prossime due righe di codice si occuperanno di questo.

InternetStackHelper stack;
stack.Install (nodes);

Internet StackHelper - è un helper di topologia per stack Internet, simile a PointToPointHelper per dispositivi di rete punto-punto. Metodo Installazione accetta NodeContainer come parametro. Una volta eseguito, installerà lo stack Internet (TCP, UDP, IP, ecc.) su ciascun nodo contenitore.

IPv4AddressHelper

Quindi dobbiamo associare i nostri dispositivi agli indirizzi IP. Forniamo un assistente di topologia per gestire l'allocazione degli indirizzi IP. L'unica API visibile all'utente è l'impostazione dell'indirizzo IP di base e della maschera di rete da utilizzare durante l'effettiva distribuzione dell'indirizzo (questo viene fatto a un livello inferiore all'interno dell'helper). Le prossime due righe di codice nel nostro script di esempio prima.cc,

Ipv4AddressHelper address;
address.SetBase ("10.1.1.0", "255.255.255.0");

dichiarare l'oggetto helper indirizzo e dirgli che dovrebbe iniziare ad allocare indirizzi IP dalla rete 10.1.1.0, utilizzando la maschera di bit 255.255.255.0 per determinarlo. Per impostazione predefinita, gli indirizzi allocati inizieranno da uno e aumenteranno in modo monotono, quindi il primo indirizzo allocato da questa base sarà 10.1.1.1, quindi 10.1.1.2, ecc. In realtà, a basso livello, il sistema ns-3 ricorda tutti gli indirizzi IP allocati e genera un errore fatale se si crea accidentalmente una situazione in cui lo stesso indirizzo viene generato due volte (a proposito, questo errore è difficile da eseguire il debug).

La seguente riga di codice,

Ipv4InterfaceContainer interfaces = address.Assign (devices);

esegue l'effettiva assegnazione dell'indirizzo. In ns-3 stabiliamo una connessione tra un indirizzo IP e un dispositivo utilizzando l'oggetto Interfaccia IPv4. Proprio come a volte abbiamo bisogno di un elenco di dispositivi di rete creati dall'assistente per un uso successivo, a volte abbiamo bisogno di un elenco di oggetti Interfaccia IPv4. Contenitore di interfaccia IPv4 fornisce questa funzionalità.

Abbiamo costruito una rete punto a punto, con stack installati e indirizzi IP assegnati. Ora abbiamo bisogno di applicazioni in ciascun nodo per generare traffico.

4.2.7 Utilizzo dell'applicazione

Un'altra delle principali astrazioni del sistema ns-3 è Applicazioni (applicazione). In questo scenario utilizziamo due specializzazioni di classe base Applicazioni ns-3 ha chiamato UdpEchoServerApplicazione и Applicazione UdpEchoClient. Come nei casi precedenti, utilizziamo oggetti ausiliari per configurare e gestire gli oggetti base. Qui usiamo UdpEchoServerHelper и UdpEchoClientHelper oggetti per semplificarci la vita.

UdpEchoServerHelper

Le seguenti righe di codice nel nostro script di esempio first.cc vengono utilizzate per configurare un'applicazione server echo UDP su uno dei nodi che abbiamo creato in precedenza.

UdpEchoServerHelper echoServer (9);

ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));
serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));

La prima riga di codice nello snippet sopra crea UdpEchoServerHelper. Come al solito, non si tratta di un'applicazione in sé, ma di un oggetto che ci aiuta a creare vere e proprie applicazioni. Una delle nostre convenzioni è passare gli attributi richiesti al costruttore dell'oggetto helper. In questo caso l'helper non può fare nulla di utile a meno che non gli venga fornito il numero di porta su cui il server ascolterà i pacchetti, questo numero deve essere noto anche al client. In questo caso, passiamo il numero di porta al costruttore helper. Il costruttore, a sua volta, lo fa semplicemente Imposta attributo con il valore passato. Successivamente, se lo si desidera, è possibile utilizzare SetAttribute per impostare un valore diverso per l'attributo Port.

Come molti altri oggetti ausiliari, l'oggetto UdpEchoServerHelper ha un metodo Installazione. L'esecuzione di questo metodo crea effettivamente un'applicazione server echo di base e la associa all'host. È interessante notare che il metodo Installazione prende NodoContenitore come parametro come gli altri Installazione metodi che abbiamo visto.

La conversione implicita C++ che funziona qui prende il risultato del metodo nodo.Get(1) (che restituisce un puntatore intelligente all'oggetto nodo - Ptr ) e lo utilizza nel costruttore per l'oggetto anonimo NodoContenitoreche viene poi passato al metodo Installazione. Se non riesci a determinare nel codice C++ quale firma del metodo viene compilata ed eseguita, cerca tra le conversioni implicite.

Ora lo vediamo echoServer.Install sta per installare l'applicazione UdpEchoServerApplicazione su trovato in NodoContenitoreche utilizziamo per gestire i nostri nodi, nodo con indice 1. Metodo Installazione restituirà un contenitore che contiene puntatori a tutte le applicazioni (in questo caso una, poiché abbiamo passato un file anonimo NodoContenitore, contenente un nodo) creato dall'helper.

Le applicazioni devono specificare quando iniziare a generare traffico "inizio" e potrebbe essere necessario specificare inoltre un'ora in cui interromperlo "fermare". Forniamo entrambe le opzioni. Questi tempi vengono impostati utilizzando i metodi Contenitore dell'applicazione Inizio и Fermare. Questi metodi accettano parametri di tipo Ora. In questo caso utilizziamo una sequenza esplicita di conversioni C++ per prendere C++ doppio 1.0 e convertirlo in un oggetto Time tns-3 che utilizza l'oggetto Seconds per convertire in secondi. Ricorda che le regole di conversione possono essere controllate dall'autore del modello e C++ ha le proprie regole, quindi non puoi sempre contare sul fatto che i parametri vengano convertiti nel modo previsto. Due righe

serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));

farà sì che l'applicazione echo server si avvii (si accenda automaticamente) un secondo dopo l'avvio della simulazione e si arresti (si spenga) dopo dieci secondi di simulazione. Poiché abbiamo dichiarato un evento di simulazione (evento di arresto dell'applicazione), che verrà eseguito in dieci secondi, verranno simulati almeno dieci secondi di funzionamento della rete.

UdpEchoClientHelper

Applicazione cliente eco configurato in modo quasi simile al server. C'è un oggetto base Applicazione UdpEchoClient, che è gestito
UdpEchoClientHelper.

UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9);
echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
echoClient.SetAttribute ("PacketSize", UintegerValue (1024));

ApplicationContainer clientApps = echoClient.Install (nodes.Get (0));
clientApps.Start (Seconds (2.0));
clientApps.Stop (Seconds (10.0));;

Tuttavia, per l'echo client dobbiamo impostare cinque diversi attributi. I primi due attributi vengono impostati al momento della creazione UdpEchoClientHelper. Passiamo i parametri che vengono utilizzati (all'interno dell'helper) per impostare gli attributi "Indirizzo remoto" и "PortaRemota" in conformità con il nostro accordo per passare i parametri necessari al costruttore helper.

Ricordiamoci che abbiamo usato Contenitore di interfaccia IPv4 per tracciare gli indirizzi IP che abbiamo assegnato ai nostri dispositivi. L'interfaccia nulla nel contenitore delle interfacce corrisponderà all'indirizzo IP del nodo nullo nel contenitore dei nodi. La prima interfaccia nel contenitore delle interfacce corrisponde all'indirizzo IP del primo nodo nel contenitore dei nodi. Quindi, nella prima riga di codice (sopra), creiamo un helper e gli diciamo che l'indirizzo remoto del client sarà l'indirizzo IP assegnato all'host su cui si trova il server. Diciamo anche che dobbiamo organizzare l'invio dei pacchetti alla porta nove.

L'attributo "MaxPackets" indica al client il numero massimo di pacchetti che possiamo inviare durante la simulazione. L'attributo "Interval" indica al client quanto tempo attendere tra i pacchetti e l'attributo "PacketSize" indica al client quanto dovrebbe essere grande il carico utile del pacchetto. Con questa combinazione di attributi diciamo al client di inviare un singolo pacchetto da 1024 byte.

Come con l'echo server, impostiamo gli attributi dell'echo client Inizio и Fermare, ma qui avviamo il client un secondo dopo l'accensione del server (due secondi dopo l'inizio della simulazione).

4.2.8 Simulatore

A questo punto dobbiamo eseguire la simulazione. Questo viene fatto utilizzando la funzione globale Simulatore::Esegui.

Simulator::Run ();

Quando in precedenza abbiamo chiamato metodi,

serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));
... 
clientApps.Start (Seconds (2.0));
clientApps.Stop (Seconds (10.0));

in realtà abbiamo programmato eventi nel simulatore a 1,0 secondi, 2,0 secondi e due eventi a 10,0 secondi. Dopo la chiamata Simulatore::Esegui, il sistema inizierà a visualizzare l'elenco degli eventi programmati e ad eseguirli. Per prima cosa attiverà un evento dopo 1,0 secondi, che attiverà l'applicazione echo server (questo evento può a sua volta pianificare molti altri eventi). Verrà quindi attivato un evento programmato a t=2,0 secondi che avvierà l'applicazione client echo. Ancora una volta, questo evento potrebbe avere molti altri eventi in programma. L'implementazione dell'evento di avvio nel client echo inizierà la fase di trasferimento dei dati della simulazione inviando un pacchetto al server.

L'atto di inviare un pacchetto al server attiverà una catena di eventi che verranno automaticamente pianificati dietro le quinte e che implementeranno la meccanica di invio di un pacchetto echo secondo i parametri temporali che abbiamo impostato nello script.

Di conseguenza, poiché stiamo inviando un solo pacchetto (ricordate, l'attributo MaxPackets era impostato su uno), la catena di eventi avviata da questo singolo ping del client terminerà e la simulazione entrerà in modalità standby. Una volta che ciò accade, gli eventi rimanenti in programma saranno gli eventi Fermare per server e client. Quando questi eventi vengono eseguiti, non rimarranno eventi per ulteriori elaborazioni e Simulatore::Esegui restituirà il controllo. La simulazione è completa.

Non resta che ripulire te stesso. Questo viene fatto chiamando la funzione globale Simulatore::Distruggi. Perché sono state chiamate le funzioni di supporto (o codice ns-3 di basso livello), che sono organizzate in modo tale che gli hook siano stati inseriti nel simulatore per distruggere tutti gli oggetti creati. Non avevi bisogno di rintracciare nessuno di questi oggetti: tutto quello che dovevi fare era chiamare Simulatore::Distruggi ed esci. Il sistema ns-3 farà questo duro lavoro per te. Le restanti righe del nostro primo script ns-3, first.cc, fanno proprio questo:

Simulator::Destroy ();
return 0;
}

Quando si fermerà il simulatore?

ns-3 è un simulatore di eventi discreti (DE). In un simulatore di questo tipo, ogni evento è associato al suo tempo di esecuzione e la simulazione continua elaborando gli eventi nell'ordine in cui si verificano man mano che la simulazione procede. Gli eventi possono causare la pianificazione di eventi futuri (ad esempio, un timer può riprogrammarsi per terminare il conteggio nell'intervallo successivo).

Gli eventi iniziali vengono solitamente avviati dall'entità, ad esempio IPv6 pianificherà il rilevamento dei servizi sulla rete, le richieste dei vicini, ecc. L'applicazione pianifica il primo evento di invio del pacchetto e così via. Quando un evento viene elaborato, può generare zero, uno o più eventi. Man mano che la simulazione procede, si verificano eventi, che finiscono o ne creano di nuovi. La simulazione si interromperà automaticamente se la coda degli eventi è vuota o viene rilevato un evento speciale Fermare. Evento Fermare generato dalla funzione Simulatore::Stop (tempo di stop).

Esiste un caso tipico in cui Simulator::Stop è assolutamente necessario per interrompere la simulazione: quando si verificano eventi autosufficienti. Gli eventi autosufficienti (o ricorrenti) sono eventi che vengono sempre riprogrammati. Di conseguenza, mantengono sempre la coda degli eventi non vuota. Esistono molti protocolli e moduli contenenti eventi ricorrenti, ad esempio:

• FlowMonitor: controllo periodico dei pacchetti persi;

• RIPng – trasmissione periodica degli aggiornamenti della tabella di routing;

• eccetera.

In tali casi Simulatore::Stop necessario per interrompere correttamente la simulazione. Inoltre, quando ns-3 è in modalità di emulazione, RealtimeSimulator viene utilizzato per sincronizzare l'orologio della simulazione con l'orologio della macchina e Simulatore::Stop necessario per fermare il processo.

Molti dei programmi di simulazione presenti nel libro di testo non chiamano Simulatore::Stop esplicitamente, poiché terminano automaticamente quando gli eventi in coda sono esauriti. Tuttavia, questi programmi accetteranno anche la chiamata Simulator::Stop. Ad esempio, la seguente istruzione aggiuntiva nel primo programma di esempio pianificherebbe un arresto esplicito a 11 secondi:

+ Simulator::Stop (Seconds (11.0));
  Simulator::Run ();
  Simulator::Destroy ();
  return 0;
}

Quanto sopra non cambierà effettivamente il comportamento di questo programma, poiché questa particolare simulazione termina naturalmente dopo 10 secondi. Ma se dovessi modificare il tempo di arresto nell'istruzione precedente da 11 secondi a 1 secondo, noterai che la simulazione si interrompe prima che qualsiasi output raggiunga lo schermo (poiché l'output avviene dopo circa 2 secondi di tempo di simulazione).

È importante chiamare Simulator::Stop prima di chiamare Simulator::Run; altrimenti Simulator::Run potrebbe non restituire mai il controllo al programma principale per eseguire l'arresto!

4.2.9 Costruire il tuo script

Abbiamo reso banale la creazione dei tuoi semplici script. Tutto quello che devi fare è inserire il tuo script nella directory scratch e verrà creato automaticamente se lo esegui Waf. Proviamo. Torna alla directory di livello superiore e copia esempi/tutorial/first.cc catalogare graffiare

$ cd ../.. 
$ cp examples/tutorial/first.cc scratch/myfirst.cc

Ora crea il tuo primo script di esempio utilizzando waf:

$ ./waf

Dovresti visualizzare dei messaggi che indicano che il tuo primo esempio è stato creato correttamente.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
[614/708] cxx: scratch/myfirst.cc -> build/debug/scratch/myfirst_3.o
[706/708] cxx_link: build/debug/scratch/myfirst_3.o -> build/debug/scratch/myfirst
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (2.357s)

Ora puoi eseguire l'esempio (nota che se crei il tuo programma nella directory scratch, devi eseguirlo da graffiare):

$ ./waf --run scratch/myfirst

Dovresti vedere un output simile:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s) Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Qui puoi vedere che il sistema di compilazione verifica che il file sia stato creato e quindi lo esegue. La voce del componente sull'echo client indica che ha inviato un singolo pacchetto da 1024 byte all'echo server 10.1.1.2. Anche tu vedi il componente di registrazione sul server echo per dire che ha ricevuto 1024 byte da 10.1.1.1. L'echo server riproduce silenziosamente il pacchetto e puoi vedere nel registro dell'echo client che ha ricevuto il pacchetto dal server.

4.3 ns-3 Codice sorgente

Ora che hai utilizzato alcuni degli helper ns-3, puoi dare un'occhiata ad alcuni codici sorgente che implementano questa funzionalità. Il codice più recente può essere visualizzato sul nostro server web al seguente link: https://gitlab.com/nsnam/ns-3-dev.git. Lì vedrai la pagina di riepilogo di Mercurial per il nostro albero di sviluppo ns-3. Nella parte superiore della pagina vedrai diversi collegamenti,

summary | shortlog | changelog | graph | tags | files

Vai avanti e seleziona il collegamento dei file. Ecco come apparirà il livello superiore della maggior parte dei nostri repository:

drwxr-xr-x                               [up]
drwxr-xr-x                               bindings python  files
drwxr-xr-x                               doc              files
drwxr-xr-x                               examples         files
drwxr-xr-x                               ns3              files
drwxr-xr-x                               scratch          files
drwxr-xr-x                               src              files
drwxr-xr-x                               utils            files
-rw-r--r-- 2009-07-01 12:47 +0200 560    .hgignore        file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 1886   .hgtags          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 1276   AUTHORS          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 30961  CHANGES.html     file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 17987  LICENSE          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 3742   README           file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 16171  RELEASE_NOTES    file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 6      VERSION          file | revisions | annotate
-rwxr-xr-x 2009-07-01 12:47 +0200 88110  waf              file | revisions | annotate
-rwxr-xr-x 2009-07-01 12:47 +0200 28     waf.bat          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 35395  wscript          file | revisions | annotate
-rw-r--r-- 2009-07-01 12:47 +0200 7673   wutils.py        file | revisions | annotate

I nostri script di esempio si trovano nella directory Esempi. Se fai clic sugli esempi vedrai un elenco di sottodirectory. Uno dei file nella sottodirectory tutorial - primo.cc. Se clicchi su prima.cc vedrai il codice che hai appena imparato.

Il codice sorgente si trova principalmente nella directory src. È possibile visualizzare il codice sorgente facendo clic sul nome della directory o facendo clic sul collegamento dei file a destra del nome della directory. Se fai clic sulla directory src, otterrai un elenco di sottodirectory src. Se poi fai clic sulla sottodirectory core, troverai un elenco di file. Il primo file che vedrai (al momento della stesura di questa guida) è interrompi.h. Se clicchi sul link interrompi.h, verrai inviato al file sorgente per interrompi.h, che contiene macro utili per uscire dagli script se vengono rilevate condizioni anomale. Il codice sorgente per gli helper che abbiamo utilizzato in questo capitolo può essere trovato nella directory src/Applicazioni/helper. Sentiti libero di curiosare nell'albero delle directory per capire dove si trovano e comprendere lo stile dei programmi ns-3.

Fonte: habr.com

Aggiungi un commento