Tutorial pentru simulatorul de rețea ns-3. Capitolul 4

Tutorial pentru simulatorul de rețea ns-3. Capitolul 4
capitolele 1,2
capitol 3

4 Prezentarea conceptului
4.1 Abstracții cheie
4.1.1 Node (Nod)
4.1.2 Application (Aplicație)
4.1.3 Channel (Canal)
4.1.4 Net Device (Dispozitiv de rețea)
4.1.5 Asistenți topologici
4.2 Primul script ns-3
4.2.1 Cod boilerplate
4.2.2 Module conectabile
4.2.3 Spațiul de nume ns3
4.2.4 Jurnalizare
4.2.5 Funcția principală
4.2.6 Utilizarea asistenților topologici
4.2.7 Utilizarea aplicației
4.2.8 Simulator
4.2.9 Construirea scenariului dumneavoastră
4.3 Cod sursă ns-3

Capitolul 4

Prezentarea conceptului

Primul lucru pe care trebuie să-l facem înainte de a începe să studiem sau să scriem cod ns-3 este să explicăm câteva noțiuni de bază și abstracții din sistem. Multe dintre acestea, pentru unii, pot părea evidente, dar recomandăm să acordați timp pentru a citi această secțiune pentru a vă asigura că începeți pe o fundație solidă.

4.1 Abstracții cheie

În această secțiune, vom examina termenii care sunt de obicei utilizați în rețea, dar au un anumit înțeles în ns-3.

4.1.1 Node (Nod)

În jargonul internetului, un dispozitiv de computer care se conectează la rețea este numit gazdă sau, uneori, sistem final. Din moment ce ns-3 este un simulator de rețea, nu un simulator de Internet, nu folosim intenționat termenul gazdă, deoarece este strâns legat de Internet și protocoalele sale. În schimb, folosim un termen mai general, utilizat și de altele simulatoare, care își are originea în teoria grafurilor – nod (node).

În ns-3, abstracția de bază a unității de calcul este numită nod. Această abstracție este reprezentată în C++ prin clasa Node. Clasa NodeNode (nod) oferă metode pentru gestionarea reprezentărilor unităților de calcul în simulări.

Trebuie să înțelegeți Node cum este un computer căruia îi veți adăuga funcționalitate. Veți adăuga lucruri precum aplicații, stive de protocoale și plăci periferice cu drivere, care permit computerului să efectueze muncă utilă. Folosim același model de bază în ns-3.

4.1.2 Application (Aplicație)

În general, software-ul de calculator este împărțit în două categorii largi. Software-ul de sistem organizează diverse resurse de calculator, precum memorie, cicluri CPU, disc, rețea etc., conform unui anumit model de calcul. Software-ul de sistem nu folosește, de obicei, aceste resurse pentru a îndeplini sarcini care aduc beneficii imediate utilizatorului. Utilizatorul pornește, de obicei, o aplicație pentru a atinge un anumit scop, care obține și folosește resursele controlate de software-ul de sistem.

Adesea, linia de demarcație între software-ul de sistem și cel aplicație este trasată în funcție de schimbarea nivelului de privilegii, care are loc în capcanele sistemului de operare. În ns-3 nu există o adevărată concept de sistem de operare și, în consecință, nu există noțiuni de niveluri de privilegii sau apeluri de sistem. Cu toate acestea, avem ideea de aplicație. Asemenea «lumii reale», aplicațiile software funcționează pe computere pentru a îndeplini sarcini, aplicațiile ns-3 funcționează pe nodurile ns-3 pentru a gestiona simulările în lumea simulată.

În ns-3, abstractizarea de bază pentru programul utilizatorului care generează o anumită activitate pentru modelare este aplicația. Această abstractizare este reprezentată în C++ de clasa Application. Clasa Application oferă metode pentru managementul simulărilor reprezentărilor versiunilor noastre de aplicații la nivel de utilizator. Se așteaptă de la dezvoltatori să specializeze clasa Application pentru a crea aplicații noi în sensul programării orientate pe obiect. În acest ghid, vom folosi specializările clasei Application, denumite UdpEchoClientApplication și UdpEchoServerApplication. Așa cum era de așteptat, aceste aplicații constituie un set de aplicații client/server utilizate pentru generarea și simularea ecoului pachetelor de rețea.

4.1.3 Channel (Canal)

În lumea reală, un computer poate fi conectat la o rețea. Adesea, mediile prin care sunt transferate datele în aceste rețele sunt numite canale. Când conectezi un cablu Ethernet la o priză de pe perete, conectezi computerul la canalul de comunicație Ethernet. În lumea simulată ns-3, un nod se conectează la un obiect care reprezintă canalul de comunicație. Aici, principala abstractizare a subrețelei de comunicație se numește canal și este reprezentată în C++ prin clasa Channel.

Clasă ChannelChannel oferă metode pentru a gestiona interacțiunea obiectelor din subrețea și conectarea lor la noduri. Canalele pot fi, de asemenea, specializate de dezvoltatori în ceea ce privește programarea orientată pe obiect. Specializarea canalului poate modela ceva simplu, cum ar fi un cablu. Un canal specializat poate de asemenea să modeleze lucruri complexe, cum ar fi un switch Ethernet mare sau un spațiu tridimensional plin de obstacole în cazul rețelelor wireless.

În acest ghid, vom folosi versiuni specializate ale canalului denumite CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel și WifiChannelWifiChannel. CsmaChannel, de exemplu, modelează o versiune a subrețelei de comunicație care implementează un mediu de comunicație multiaccident controlat. Aceasta ne oferă funcționalitate similară cu Ethernet.

4.1.4 Net Device (Dispozitiv de rețea)

Cândva, dacă doreai să conectezi un computer la rețea, trebuia să cumperi un anumit cablu de rețea și un dispozitiv hardware numit (în terminologia PC) placă de periferie, care trebuia instalată în computer. Dacă placa de periferie implementa anumite funcții de rețea, acestea erau numite plăci de interfață de rețea sau plăci de rețea. Astăzi, majoritatea calculatoarelor vin cu echipamente integrate de interfață de rețea, iar utilizatorii nu le percep ca dispozitive separate.

Placa de rețea nu va funcționa fără un driver software care să controleze hardware-ul acesteia. În Unix (sau Linux), o parte a echipamentului periferic este clasificată ca un dispozitiv. Dispozitivele sunt gestionate prin drivere de dispozitive, iar dispozitivele de rețea (NIC) sunt gestionate prin drivere de dispozitive de rețea (network device drivers) și au un nume colectiv de dispozitive de rețea (net devices). În Unix și Linux, accesați dispozitivele de rețea prin nume precum eth0.

În ns-3, abstractizarea dispozitivului de rețea acoperă atât driverul software, cât și echipamentul simulat. În timpul simulării, dispozitivul de rețea este „instalat” în nod pentru a-i permite să comunice cu alte noduri prin canale. La fel ca într-un computer real, un nod poate fi conectat la mai multe canale prin intermediul mai multor dispozitive. NetDevices.

Abstractizarea rețelei dispozitivului este reprezentată în C++ prin clasa NetDevice. Clasa NetDevice oferă metode de gestionare a conexiunilor cu obiectele Node și Channel; și pot fi specializate de dezvoltatori în sensul programării orientate pe obiect. În acest ghid, vom folosi câteva versiuni specializate ale NetDevice sub denumirile CsmaNetDevice, PointToPointNetDevice și WifiNetDevice. La fel cum adaptorul de rețea Ethernet este destinat să lucreze cu rețeaua Ethernet, CsmaNetDevice este destinat să lucreze cu CsmaChannel, PointToPointNetDevice este destinat să lucreze cu PointToPointChannel, iar WifiNetDevice — destinat să lucreze cu WifiChannel.

4.1.5 Asistenți topologici

Într-o rețea reală, veți găsi computere gazdă cu plăci de rețea adăugate (sau integrate). În ns-3, am spune că veți vedea noduri cu NetDevices conectate. Într-o rețea mare simulat, va trebui să organizați conexiunile între numeroase obiecte. Node, NetDevice și Canal.

Având în vedere că conectarea NetDevices la noduri, NetDevices la canale, atribuirea adreselor IP etc. în ns-3 este o sarcină comună, pentru a o face cât mai simplă, oferim așa-numitele asistenți topologici. De exemplu, pentru a crea un NetDevice, trebuie să efectuați o mulțime de operații de nucleu ns-3, să adăugați un MAC, să instalați acest dispozitiv de rețea în Nod, să configurați stiva de protocoale a nodului și apoi să conectați NetDevice la Channel. Vor fi necesare și mai multe operații pentru a conecta mai multe dispozitive la canale multipoint și apoi pentru a conecta rețelele individuale într-o rețea unificată (Internetworks). Oferim obiecte de asistență topologică care, pentru comoditatea dumneavoastră, combină aceste numeroase operații într-un model ușor de utilizat.

4.2 Primul script ns-3

Dacă ați instalat sistemul așa cum a fost sugerat mai sus, veți avea un release ns-3 în directorul numit repos din directorul dumneavoastră personal. Accesați directorul release

Dacă nu aveți un astfel de director, înseamnă că nu ați specificat directorul de ieșire la construirea versiuni release a ns-3, efectuați construirea astfel:
$ .\/waf configure —build-profile=release —out=build\/release,
$ .\/waf build

Acolo ar trebui să vedeți o structură de director similară cu următoarea:

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

Navigați la directorul examples/tutorial. Ar trebui să vedeți un fișier numit first.cc. Acesta este un script care va crea o conexiune simplă punct-la-punct între două noduri și va transmite un pachet între noduri. Să examinăm acest script linie cu linie, deschizând first.cc în editorul dumneavoastră preferat.

4.2.1 Cod boilerplate
Prima linie din fișier este linia de modul al editorului emacs. Aceasta spune emacs despre convențiile de formatare (stilul de codare) pe care le vom folosi în codul nostru sursă.

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

Aceasta este întotdeauna o întrebare destul de controversată, așa că trebuie să facem clarificări pentru a o elimina din calea noastră. Proiectul ns-3, ca majoritatea proiectelor mari, a adoptat un stil de codare cu care trebuie să se conformeze orice cod furnizat. Dacă doriți să contribuiți cu codul vostru la proiect, va trebui în final să respectați standardul de codare ns-3, așa cum este descris în fișierul doc/codingstd.txt sau prezentat pe pagina web a proiectului: https://www.nsnam.org/develop/contributing-code/coding-style/.

Vă recomandăm să vă obișnuiți cu aspectul codului ns-3 și să aplicați acest standard de fiecare dată când lucrați cu codul nostru. Întreaga echipă de dezvoltatori și contribuitori a convenit asupra acestuia după câteva frământări. Linia de modul emacs menționată mai sus facilitează formatarea corectă dacă folosiți editorul emacs.

Simulatorul ns-3 este licențiat sub GNU General Public License. Veți vedea un antet legal corespunzător GNU în fiecare fișier din distribuția ns-3. Adesea puteți vedea o notificare de copyright pentru una dintre instituțiile participante în proiectul ns-3 deasupra textului GPL și a autorului, prezentată mai jos.

/* 
* 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 Module conectabile

Codul propriu-zis începe cu o serie de directive de includere (include).

#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"

Pentru a ajuta utilizatorii noștri de scripturi avansate să gestioneze numărul mare de fișiere de antet prezente în sistem, le grupăm în module mari conform utilizării lor. Oferim un fișier de antet care va încărca recursiv toate fișierele de antet utilizate în acest modul. În loc să căutați care anume antet vă este necesar și să obțineți, poate, o listă corectă de dependențe, vă oferim oportunitatea de a încărca un grup de fișiere cu un grad înalt de detaliu. Aceasta nu este cea mai eficientă abordare, dar cu siguranță face scrierea scripturilor mult mai ușoară.

Fiecare dintre fișierele incluse ns-3 este plasat într-un director numit ns3 (subdirectorul de construire), pentru a evita conflictele de nume de fișiere în timpul procesului de construire. Fișierul ns3/core-module.h corespunde modulului ns-3, pe care îl veți găsi în directorul src/core în versiunea pe care ați instalat-o. În lista acestui director veți găsi un număr mare de fișiere de antet. Când efectuați construcția, Waf procesează fișierele de antet publice în directorul ns3 în subdirectorul build/debug

Dacă nu aveți un astfel de director, înseamnă că nu ați specificat directorul de ieșire la construirea versiuni release a ns-3, efectuați construirea astfel:
$ ./waf configure --build-profile=debug --out=build/debug
$ .\/waf build
sau
$ ./waf configure --build-profile=optimized --out=build/optimized
$ .\/waf build

sau build/optimized, în funcție de configurația dumneavoastră. Waf de asemenea, va genera automat un fișier de antet al modulului pentru a încărca toate fișierele de antet publice. Deoarece, desigur, urmați cu strictețe acest ghid, ați realizat deja

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

pentru a configura proiectul pentru a efectua construcții de depanare, incluzând exemple și teste. De asemenea, ați realizat

$ ./waf

pentru a construi proiectul. Așadar, acum, când priviți în directorul ../../../build/debug/ns3, veți găsi acolo, printre altele, fișierele de antet ale celor patru module menționate mai sus. Puteți să aruncați o privire în conținutul acestor fișiere și să descoperiți că conțin toate fișierele publice utilizate de modulele respective.

4.2.3 Spațiul de nume ns3

Următoarea linie din scriptul first.cc este declarația spațiului de nume.

using namespace ns3;

Proiectul ns‑3 este implementat în spațiul de nume C++, numit ns3. Acesta grupează toate anunțurile legate de ns‑3 în domeniul de vizibilitate din afara spațiului de nume global, ceea ce, sperăm, va ajuta la integrarea cu alt cod. Utilizarea operatorului C++ introduce spațiul de nume ns‑3 în regiunea de declarație curentă (globală). Este un mod elegant de a spune că, după această declarație, nu va trebui să introduceți operatorul de rezolvare ns3:: înainte de tot codul ns‑3 pentru a-l folosi. Dacă nu sunteți familiarizat cu spațiile de nume, consultați practic orice manual de C++ și comparați spațiul de nume ns3 cu utilizarea spațiului de nume std și declarațiile. using namespace std; în exemple de utilizare a operatorului de ieșire cout și fluxuri.

4.2.4 Jurnalizare

Linia următoare a scriptului este următoarea:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Vom folosi această afirmație ca un loc convenabil pentru a discuta despre sistemul nostru de documentare. DoxygenDacă aruncați o privire pe site-ul proiectului ns‑3, veți găsi un link denumit „Documentație” (Documentation) în panoul de navigare. Dacă selectați acest link, veți ajunge pe pagina noastră de documentație. Există un link pentru „Ultima versiune”, care vă va duce la documentația pentru cea mai recentă versiune stabilă ns‑3. Dacă selectați linkul „Documentația API”, veți ajunge la pagina documentației API ns‑3.

În partea stângă a paginii, veți găsi o reprezentare grafică a structurii documentației. Un loc bun pentru a începe este „cartea” Modules ns‑3 din arborele de navigare ns‑3. Dacă extindeți Modules, veți vedea o listă a documentației modulelor ns‑3. Așa cum s-a discutat mai sus, conceptul de modul este direct legat de fișierele incluse în modulul de mai sus. Subsystema de jurnalizare ns‑3 este discutată în secțiunea Utilizarea modulului de jurnalizare, așa că ne vom întoarce la ea mai târziu în acest ghid, dar puteți afla despre afirmația de mai sus uitându-vă la modulul Nucleu, apoi deschizând cartea Instrumente de depanare, și apoi alegând pagina Logging. Faceți clic pe Logging.

Acum ar trebui să examinați documentația Doxygen pentru modul Logging. În lista macrocomenzilor din partea de sus a paginii, veți vedea o înregistrare pentru NS_LOG_COMPONENT_DEFINE. Înainte de a face clic pe link, asigurați-vă că ați consultat „Descrierea detaliată” a modulului de logare pentru a înțelege funcționarea acestuia în ansamblu. Pentru a face acest lucru, puteți derula în jos sau selecta „More…” sub diagramă.

Odată ce aveți o idee generală despre ce se întâmplă, continuați și consultați documentația pentru NS_LOG_COMPONENT_DEFINE specific. Nu voi dubla documentația aici, dar, ca un rezumat, această linie declară un component de logare numit FirstScriptExample, care permite activarea și dezactivarea înregistrării mesajelor în consolă printr-un link către nume.

4.2.5 Funcția principală

În următoarele linii ale scriptului, veți vedea,

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

Aceasta este doar declarația funcției principale a programului (scriptului) dumneavoastră. Ca și în orice program C++, trebuie să definiți funcția principală, care se execută prima. Nu este nimic special aici. Scriptul dvs. ns‑3 este pur și simplu un program C++. Linia următoare stabilește rezoluția în timp la 1 nanosecondă, care este valoarea implicită:

Time::SetResolution (Time::NS);

Rezoluția în timp sau, pur și simplu, rezoluția, este cea mai mică valoare temporală care poate fi utilizată (cea mai mică diferență percepută între două valori de timp). Puteți schimba rezoluția exact o dată. Mecanismul care oferă această flexibilitate consumă memorie, așa că, odată ce rezoluția este stabilită explicit, eliberăm memoria, prevenind actualizările ulterioare. (Dacă nu stabiliți rezoluția explicit, aceasta va fi 1 nanosecundă implicit, iar memoria va fi eliberată la începutul simulării.)

Următoarele două linii ale scriptului sunt folosite pentru a activa două componente de logare care sunt integrate în aplicații EchoClient și EchoServer:

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

Dacă ați citit documentația pentru componenta Logging, veți observa că există mai multe niveluri de detaliere a jurnalizării pe care le puteți activa pentru fiecare componentă. Aceste două linii de cod activează jurnalizarea de debug la nivel INFO pentru clienții și serverele de tip echo. La acest nivel, aplicația va imprima mesaje în timpul simulării când trimite și primește pachete.

Acum vom trece direct la crearea topologiei și la lansarea simulării. Vom folosi obiecte auxiliare de topologie pentru a face această lucrare cât mai simplă.

4.2.6 Utilizarea asistenților topologici

Următoarele două linii de cod din scriptul nostru vor crea de fapt obiecte Node ns-3, care vor reprezenta calculatoarele din simulare.

NodeContainer nodes;
nodes.Create (2);

Înainte de a continua, să găsim documentația pentru clasa NodeContainer. O altă modalitate de a accesa documentația pentru această clasă este prin tab-ul Classes de pe paginile Doxygen. Dacă aveți deja deschis Doxygen, derulați înapoi în partea de sus a paginii și selectați tab-ul Classes. Ar trebui să vedeți un nou set de tab-uri, dintre care unul este lista claselor. Sub acest tab veți vedea lista tuturor claselor ns-3. Derulați în jos, până la ns3 :: NodeContainer. Când găsiți clasa, selectați-o pentru a accesa documentația acesteia.

Așa cum ne amintim, una dintre ab grandațiile noastre cheie este nodul. Acesta reprezintă un calculator la care vom adăuga lucruri precum stive de protocoale, aplicații și plăci de extensie. Asistentul de topologie NodeContainer oferă o modalitate convenabilă de a crea, gestiona și accesa orice obiecte Node, pe care le creăm pentru a lansa simularea. Prima linie de mai sus declară NodeContainer, pe care o numim nodes. A doua linie apelează metoda Create pentru obiectul nodes și cere containerului să creeze două noduri. Așa cum este descris în Doxygen, containerul solicită sistemului ns-3 crearea a două obiecte Node și păstrează pointere la aceste obiecte în interiorul său.

Nodurile create în script în prezent nu fac nimic. Următorul pas în construirea topologiei este conectarea nodurilor noastre la rețea. Cea mai simplă formă de rețea pe care o susținem este conexiunea punct-la-punct între două noduri. Acum vom crea o astfel de conexiune.

PointToPointHelper

Creăm o conexiune bidirecțională acționând conform unui șablon familiar, folosind un obiect de asistență topologic pentru a efectua munca la nivel inferior necesară pentru conectare. Să ne amintim că cele două abstracții cheie ale noastre NetDevice și Canal. În lumea reală, acești termeni corespund aproximativ plăcilor periferice și cablurilor de rețea. De obicei, aceste două lucruri sunt strâns legate între ele, iar nimeni nu se poate aștepta la schimburi, de exemplu, între dispozitive Ethernet printr-un canal wireless. Asistenții noștri topologici respectă această legătură strânsă și, prin urmare, în acest scenariu veți folosi un singur obiect PointToPointHelper pentru a configura și conecta obiectele ns‑3 PointToPointNetDevice și PointToPointChannel. Următoarele trei linii din scenariu:

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

Prima linie,

PointToPointHelper pointToPoint;

creează o instanță a obiectului în stivă PointToPointHelper. Din perspectiva de nivel înalt, următoarea linie,

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

spune obiectului PointToPointHelper să folosească valoarea „5 Mbps” (cinci megabiți pe secundă) caDataRate».

Dintr-o perspectivă mai specifică, linia „DataRate” corespunde a ceea ce numim atribut PointToPointNetDevice. Dacă te uiți la Doxygen pentru clasa ns3::PointToPointNetDevice și în documentația metodei GetTypeId vei găsi o listă de atribute definite pentru dispozitiv. Printre acestea se va afla atributul „DataRate”. Cele mai multe obiecte vizibile pentru utilizator din ns‑3 au liste de atribute similare. Folosim acest mecanism pentru a configura simplu simulația fără recompilare, așa cum vei vedea în secțiunea următoare.

La fel ca „DataRate” în PointToPointNetDevice, vei găsi atributul „Delay” asociat cu PointToPointChannel. Linia finală,

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

spune PointToPointHelper folosește valoarea „2 ms” (două milisecunde) ca valoare a întârzierii de propagare pe canalul punct-la-punct pe care îl creează ulterior.

NetDeviceContainer

În acest moment, avem în scenariul nostru NodeContainer, care conține două noduri. Avem PointToPointHelper, care este pregătit pentru a crea obiecte PointToPointNetDevices și a le conecta folosind un obiect PointToPointChannel. La fel cum am folosit un obiect de asistență topologic NodeContainer pentru a crea noduri, vom solicita PointToPointHelper să efectueze pentru noi munca necesară creării, configurării și instalării dispozitivelor noastre. Ne va trebui o listă a tuturor obiectelor create NetDevice, de aceea folosim NetDeviceContainer pentru a le stoca la fel cum am folosit NodeContainer pentru a stoca nodurile create de noi. Următoarele două linii de cod,

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

lucrează la configurarea dispozitivelor și a canalului. Prima linie declară containerul de dispozitive menționat mai sus, iar a doua face munca principală. Metoda Instalează of the facility PointToPointHelper acceptă NodeContainer ca parametru. În interior NetDeviceContainer pentru fiecare nod aflat în NodeContainer se creează (pentru legătura punct-la-punct ar trebui să existe exact două) PointToPointNetDevice se creează și este salvat în containerul de dispozitive. PointToPointChannel se creează și se atașează două PointToPointNetDevices. După ce obiectele sunt create, atributelor stocate în PointToPointHelper, sunt folosite pentru a inițializa atributele corespunzătoare în obiectele create.

După ce se execută apelul pointToPoint.Install (nodes) vom avea două noduri, fiecare cu un dispozitiv de rețea «punct-la-punct» instalat și un canal «punct-la-punct» între ele. Ambele dispozitive vor fi configurate pentru a transmite date cu o viteză de cinci megabiți pe secundă, cu o întârziere de transmisie pe canal de două milisecunde.

InternetStackHelper

Acum avem nodurile și dispozitivele configurate, dar pe nodurile noastre nu sunt instalate stivele de protocoale. Următoarele două linii de cod se vor ocupa de acest lucru.

InternetStackHelper stack;
stack.Install (nodes);

InternetStackHelper — reprezintă un ajutor topologic pentru stivele de internet, similar cu PointToPointHelper pentru dispozitivele de rețea punct-la-punct. Metoda Instalează primește NodeContainer ca parametru. La execuție, va instala stiva Internet (TCP, UDP, IP etc.) pe fiecare nod din container.

Ipv4AddressHelper

Apoi, trebuie să legăm dispozitivele noastre la adrese IP. Oferim un ajutor topologic pentru gestionarea distribuției adreselor IP. Singurul API vizibil pentru utilizator este configurarea unei adrese IP de bază și a unei măști de subrețea pentru a fi utilizate în timpul distribuirii efective a adreselor (acest lucru se face la un nivel inferior în cadrul ajutorului). Următoarele două linii de cod din exemplul nostru de script first.cc,

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

declară un obiect auxiliar de adresă și îi spune să înceapă să aloce adrese IP din rețeaua 10.1.1.0, folosind masca de rețea 255.255.255.0 pentru a determina. În mod implicit, adresele alocate vor începe de la unu și vor crește monoton, așa că prima adresă alocată din această bază va fi 10.1.1.1, apoi 10.1.1.2 și așa mai departe. În realitate, la un nivel inferior, sistemul ns-3 reține toate adresele IP alocate și generează o eroare fatală dacă ai creat din greșeală o situație în care aceeași adresă este generată de două ori (de fapt, această eroare este greu de depanat).

Linia următoare de cod,

Ipv4InterfaceContainer interfaces = address.Assign (devices);

efectuează alocarea efectivă a adresei. În ns-3, stabilim o legătură între adresa IP și dispozitiv folosind obiectul Ipv4Interface. Așa cum uneori avem nevoie de o listă de dispozitive de rețea create de asistent pentru utilizări ulterioare, uneori avem nevoie de o listă de obiecte Ipv4Interface. Ipv4InterfaceContainer oferă această funcționalitate.

Am construit o rețea punct-la-punct, cu stivele instalate și adrese IP alocate. Acum avem nevoie de aplicații pe fiecare nod pentru a genera trafic.

4.2.7 Utilizarea aplicației

O altă dintre abstarciile fundamentale ale sistemului ns-3 este Application (aplicație). În acest scenariu, folosim două specializări ale clasei de bază Application ns-3 numită UdpEchoServerApplication și UdpEchoClientApplication. Așa cum am făcut în cazurile anterioare, folosim obiecte auxiliare pentru a configura și gestiona obiectele de bază. Aici folosim UdpEchoServerHelper și UdpEchoClientHelperobiecte, pentru a ne ușura viața.

UdpEchoServerHelper

Următoarele linii de cod din exemplul nostru de script first.cc sunt folosite pentru a configura aplicația serverului UDP echo pe unul din nodurile pe care le-am creat anterior.

UdpEchoServerHelper echoServer (9);

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

Prima linie de cod din fragmentul de mai sus creează UdpEchoServerHelper. Așa cum ne-am obișnuit, acesta nu este o aplicație în sine, ci un obiect care ne ajută să creăm aplicații reale. Una dintre convențiile noastre este de a transmite atributele necesare constructorului obiectului auxiliar (asistent). În acest caz, asistentul nu poate face nimic util dacă nu i se oferă numărul portului pe care serverul va aștepta pachetele, acest număr trebuie să fie cunoscut și clientului. În acest caz, transmitem constructorului asistentului numărul portului. Constructorul, la rândul său, pur și simplu execută SetAttribute cu valoarea transmisă. Mai târziu, dacă doriți, puteți folosi SetAttribute pentru a stabili o altă valoare a atributului „Port”.

La fel ca multe alte obiecte de asistență, obiectul UdpEchoServerHelper are o metodă Instalează. Executarea acestei metode duce, de fapt, la crearea unei aplicații de bază pentru un server de ecou care se leagă de nod. Interesant este că metoda Instalează acceptă NodeContainter primește ca parametru la fel ca și alte Instalează metode pe care le-am văzut.

Conversia implicită C++, care funcționează aici, ia rezultatul metodei node.Get(1) (care returnează un pointer inteligent la obiectul nod — Ptr) și îl folosește în constructorul pentru un obiect anonim NodeContainer, care este apoi transmis metodei Instalează. Dacă nu puteți determina în codul C++ ce metodă cu ce semnătură se compilează și se execută, căutați printre conversiile implicite.

Acum vedem că echoServer.Install se pregătește să instaleze aplicația UdpEchoServerApplication pe nodul găsit în NodeContainer, pe care îl folosim pentru a gestiona nodurile noastre, nodul cu indicele 1. Metoda Instalează va returna un container care conține pointere către toate aplicațiile (în acest caz, una, deoarece am transmis un anonim NodeContainer, care conține un nod) creat de asistent.

Aplicațiile au nevoie să indice momentul de început al generării de trafic „start” și pot necesita, de asemenea, să indice timpul în care să fie oprite „stop”.Furnizăm ambele parametrii. Aceste vremuri sunt stabilite cu ajutorul metodelor ApplicationContainer Start și Stop. Aceste metode acceptă parametrii de tip Timp. În acest caz, folosim o secvență de conversie explicită C++ pentru a lua double 1.0 și transformă-l într-un obiect tns‑3 Time, folosind obiectul Seconds pentru a-l converti în secunde. Rețineți că regulile de conversie pot fi controlate de autorul modelului, iar C++ are propriile sale reguli, așa că nu puteți conta întotdeauna că parametrii vor fi convertiți așa cum ați așteptat. Două linii,

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

vor conduce la faptul că aplicația de echo server se va porni (se va activa automat) după o secundă de la începutul simulării și se va opri (se va dezactiva) după zece secunde de simulare. Având în vedere că am declarat un eveniment de simulare (evenimentul de oprire a aplicației), care va fi executat după zece secunde, va fi simulată o activitate de cel puțin zece secunde a rețelei.

UdpEchoClientHelper

Aplicația client echo se configurează într-un mod foarte asemănător serverului. Există un obiect de bază UdpEchoClientApplication, gestionat de
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));;

Cu toate acestea, pentru echo client trebuie să stabilim cinci atribute diferite. Primele două atribute sunt stabilite în timp ce îl creăm UdpEchoClientHelper. Transmitem parametrii care sunt utilizați (în interiorul asistentului) pentru a stabili atributele "RemoteAddress" și "RemotePort" conform convenției noastre de a transmite parametrii necesari constructorului asistentului.

Să ne amintim că am folosit Ipv4InterfaceContainer pentru a urmări adresele IP pe care le-am atribuit dispozitivelor noastre. Interfața zero din containerul de interfețe va coresponda adresei IP a nodului zero din containerul nodurilor. Prima interfață din containerul de interfețe corespunde adresei IP a primului nod din containerul nodurilor. Așadar, în prima linie de cod (de sus) creăm asistentul și îi spunem că adresa IP a clientului va fi adresa IP atribuită nodului pe care se află serverul. De asemenea, spunem că trebuie organizată trimiterea pachetelor pe portul nouă.

Atributul „MaxPackets” informează clientul despre numărul maxim de pachete pe care putem să le trimitem în timpul simulării. Atributul „Interval” îi spune clientului cât de mult să aștepte între pachete, iar atributul „PacketSize” îi comunică clientului cât de mari ar trebui să fie payload-urile pachetului. Prin această combinație de atribute, spunem clientului să trimită un pachet de 1024 de octeți.

La fel ca în cazul serverului echo, setăm atributele pentru clientul echo, Start și Stop, dar aici lansăm clientul la o secundă după activarea serverului (la două secunde după începerea simulării).

4.2.8 Simulator

În această etapă, trebuie să lansăm simularea. Acest lucru se face folosind funcția globală Simulator::Run.

Simulator::Run ();

Când am invocat anterior metodele,

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

de fapt, am programat evenimente în simulator pentru 1,0 secunde, 2,0 secunde și două evenimente la 10,0 secunde. După apelul Simulator::Run, sistemul va începe să parcurgă lista de evenimente programate și să le execute. Mai întâi va lansa evenimentul la 1,0 secunde, ceea ce va activa aplicația serverului echo (acest eveniment poate, la rândul lui, să programeze multe alte evenimente). Apoi va lansa evenimentul programat la t = 2,0 secunde, care va activa aplicația clientului echo. Din nou, acest eveniment poate programa multe alte evenimente. Implementarea evenimentului de lansare în clientul echo va începe faza de transfer de date pentru simulare, trimițând un pachet către server.

Actul de trimitere a pachetului către server va invoca o serie de evenimente care vor fi programate automat în culise și care vor implementa mecanica trimiterii pachetului de semnale echo conform parametrilor de sincronizare pe care i-am setat în scenariul nostru.

În cele din urmă, având în vedere că trimitem doar un singur pachet (să ne amintim, atributul MaxPackets a fost setat la unu), lanțul de evenimente inițiat de această singură solicitare echo de client se va încheia și simularea va trece în modul de așteptare. Odată ce se întâmplă acest lucru, evenimentele rămase programate vor fi evenimentele Stop pentru server și client. Când aceste evenimente se vor desfășura, nu vor mai rămâne evenimente pentru procesare ulterioară și Simulator::Run va returna controlul. Simularea s-a încheiat.

Rămâne doar să facem curățenie. Acest lucru se face prin apelarea funcției globale Simulator::Destroy. Deoarece au fost apelate funcții de asistență (sau cod de nivel inferior ns-3), care sunt organizate astfel încât să fie incluse hook-uri în simulator pentru distrugerea tuturor obiectelor create. Nu este necesar să urmăriți niciunul dintre aceste obiecte singuri — tot ce trebuie să faceți este să apelați Simulator::Destroy și să ieșiți. Sistemul ns-3 va face acest lucru dificil pentru voi. Linile rămase din primul nostru script ns-3, first.cc, fac exact acest lucru:

Simulator::Destroy ();
return 0;
}

Când se va opri simulatorul?

ns-3 este un simulator de evenimente discrete (DE). Într-un astfel de simulator, fiecare eveniment este legat de momentul în care are loc, iar simularea continuă prin prelucrarea evenimentelor în ordinea apariției lor pe parcursul simulării. Evenimentele pot cauza planificarea unor evenimente viitoare (de exemplu, un timer poate să se reprogrameze pentru a termina un număr în următoarea perioadă).

Evenimentele inițiale sunt de obicei inițiate de un obiect, de exemplu, IPv6 va planifica descoperirea serviciilor din rețea, cereri de vecinătate etc. Aplicația planifică primul eveniment de trimitere a pachetului etc. Când un eveniment este prelucrat, poate genera zero, unul sau mai multe evenimente. Pe măsură ce simularea progresează, evenimentele se finalizează pur și simplu sau generează altele noi. Simularea se va opri automat dacă coada de evenimente devine goală sau dacă apare un eveniment special Stop. Evenimentul Stop este generat de funcția Simulator::Stop (oprește timpul).

Există un caz tipic în care Simulator::Stop este absolut necesar pentru a opri simularea: atunci când există evenimente auto-susținute. Evenimentele auto-susținute (sau repetitive) sunt acele evenimente care se reprogramează întotdeauna. Ca urmare, acestea mențin întotdeauna coada de evenimente non-golu. Există multe protocoale și module care conțin evenimente repetitive, cum ar fi:

• FlowMonitor — verificare periodică pentru pachete pierdute;

• RIPng — difuzare periodică a actualizărilor tabelelor de rutare;

• etc.

În astfel de cazuri Simulator::Stop este necesar pentru a opri corect simularea. În plus, când ns-3 se află în modul de emulare, RealtimeSimulator este utilizat pentru a sincroniza ceasurile de simulare cu ceasurile mașinii, iar Simulator::Stop este necesar pentru a opri procesul.

Multe dintre programele de simulare din manual nu sunt apelate Simulator::Stop este evident, deoarece se finalizează automat la epuizarea evenimentelor din coadă. Cu toate acestea, aceste programe vor accepta și apelul Simulator::Stop. De exemplu, următoarea instrucțiune suplimentară din primul exemplu de program va programa o oprire explicită la secunda 11:

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

Așa cum este menționat mai sus, acest lucru nu va schimba comportamentul programului, deoarece acest model specific se încheie natural în 10 secunde. Dar dacă ați modifica timpul de oprire din instrucțiunea de mai sus de la 11 secunde la 1 secundă, ați observa că simularea se oprește înainte ca orice rezultat să fie afișat pe ecran (deoarece rezultatele apar la aproximativ 2 secunde de timp de simulare).

Este important să apelăm Simulator::Stop înainte de a apela Simulator::Run; altfel, Simulator::Run poate să nu returneze niciodată controlul programului principal pentru a efectua oprirea!

4.2.9 Construirea scenariului dumneavoastră

Am făcut crearea scripturilor voastre simple trivială. Tot ce trebuie să faceți este să plasați scriptul în directorul scratch, iar acesta va fi compilat automat atunci când rulați Waf. Hai să încercăm. Întoarceți-vă în directorul de nivel superior și copiați examples/tutorial/first.cc în directorul scratch

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

Acum compilați primul exemplu de script folosind waf:

$ ./waf

Ar trebui să vedeți mesaje indicând că primul vostru exemplu a fost creat cu succes.

Waf: Intrând în directorul `/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: Părăsind directorul `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizat cu succes (2.357s)

Acum puteți rula exemplul (rețineți că, dacă compilați programul vostru în directorul scratch, atunci trebuie să-l rulați și din scratch):

$ ./waf --run scratch/myfirst

Ar trebui să vedeți un rezultat similar:

Waf: Intrând în directorul `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Părăsind directorul `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizat cu succes (0.418s) Trimise 1024 de octeți către 10.1.1.2
Receput 1024 de octeți de la 10.1.1.1
Receput 1024 de octeți de la 10.1.1.2

Aici puteți vedea că sistemul de construcție verifică dacă fișierul a fost compilat, apoi îl execută. Observați că înregistrarea componentelor de pe clientul echo indică că a trimis un pachet de 1024 de octeți către serverul echo 10.1.1.2. De asemena, puteți verifica componenta de jurnal pe serverul echo, care arată că acesta a primit 1024 de octeți de la 10.1.1.1. Serverul echo își repetă în tăcere pachetul, iar în jurnalul clientului echo vedeți că acesta a primit pachetul său înapoi de la server.

4.3 Cod sursă ns-3

Acum, când ați folosit unele dintre asistenții ns-3, puteți arunca o privire asupra unor coduri sursă care implementează această funcționalitate. Cel mai recent cod poate fi vizualizat pe serverul nostru web la următorul link: https://gitlab.com/nsnam/ns-3-dev.git. Acolo veți vedea o pagină rezumată Mercurial pentru arborele nostru de dezvoltare ns-3. În partea de sus a paginii, veți vedea câteva linkuri,

summary | shortlog | changelog | graph | tags | files

Mergeți mai departe și selectați linkul pentru fișiere. Iată cum va arăta nivelul superior al celor mai multe dintre repositoarele noastre:

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

Exemplele noastre de scripturi se află în directorul examples. Dacă faceți clic pe exemple, veți vedea o listă de subdirectoare. Unul dintre fișierele din subdirectorul tutorial — first.cc. Dacă faceți clic pe first.cc veți vedea codul pe care tocmai l-ați studiat.

Codul sursă se află în principal în directorul src. Puteți vizualiza codul sursă făcând clic pe numele directorului sau pe linkul fișiere din dreapta numelui directorului. Dacă dați clic pe directorul src, veți obține o listă de subdirectoare src. Apoi, dacă dați clic pe subdirectorul core, veți găsi o listă de fișiere. Primul fișier pe care îl veți vedea (în momentul redactării acestui ghid) — abort.h. Dacă faceți clic pe linkul abort.h, veți fi trimis la fișierul sursă pentru abort.h, care conține macro-uri utile pentru ieșirea din scripturi atunci când sunt detectate condiții anormale. Codul sursă pentru asistenții pe care i-am utilizat în acest capitol poate fi găsit în directorul src/Applications/helper. Nu ezitați să răsfoiți arborele de directoare pentru a înțelege ce este unde și a descoperi stilul programării ns‑3.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster