Simulacijski modeli grešaka paketa - GASERI


Obzirom da mreže nisu apsolutno pouzdani sustavi i da su ograničenog kapaciteta, moguće je da dođe do gubitaka određenih dijelova podataka koji se šalju. Tu se najčešće događaju dvije situacije:



Onion Details



Page Clicks: 1

First Seen: 03/15/2024

Last Indexed: 10/23/2024

Domain Index Total: 397



Onion Content



Preskoči na sadržaj Simulacijski modeli grešaka paketa Obzirom da mreže nisu apsolutno pouzdani sustavi i da su ograničenog kapaciteta, moguće je da dođe do gubitaka određenih dijelova podataka koji se šalju. Tu se najčešće događaju dvije situacije: gubitak paketa zbog grešaka , odnosno jedan ili više bitova paketa su iskrivljeni i kod primanja mrežna kartica to prepoznaje (na temelju vrijednosti CRC-a) i odbacuje paket, gubitak paketa zbog prepunjenog reda čekanja , odnosno na nekom od usmjerivača je ukupan kapacitet reda čekanja prepunjen i neki od paketa je morao biti odbačen. Gubitkom paketa zbog grešaka bavimo se u ovoj vježbi; u idućoj vježbi baviti ćemo se gubitkom zbog prepunjenog reda čekanja. Model grešaka paketa (ns-3 klasa ErrorModel ) opisuje način na koji će biti odlučeno koji će paketa u određenom uzorku biti označeni kao iskrivljeni te biti odbačeni prilikom primitka. Note Implementacija grešaka paketa je donekle specifična. ErrorModel radi tako da paket označava kao iskrivljen umjesto da mijenja vrijednosti bitova, s ciljem povećanja efikasnosti i time brzine izvođenja simulacije. Kao što je već rečeno, također s ciljem povećanja efikasnosti, ns-3 prilikom simulacije u zadanim postavkama uopće ne računa CRC paketa, već se svim paketima CRC postavlja na vrijednost 0, a provjera ispravnosti se vrši na temelju ErrorModel -a. U slučaju da ErrorModel nije dio simulacije, svi se paketi smatraju ispravnima. Programska podloga Da bi mogli razumijeti način korištenja modela grešaka (i kasnije redova čekanja), moramo prvo proširiti znanje o programiranju. Pametni pokazivači i predlošci Prisjetimo se da u C/C++-u razlikujemo statičku i dinamičku alokaciju memorije. Kod statičke alokacije, tip int i klasu Node alocirali bi i koristili na način int broj = 5 ; Node node0 ; node0 . GetDevice ( 0 ); i ne bi bilo potrebno izvršavati brisanje jer to program odrađuje sam. Kod dinamičke alokacije i dealokacije objektima pristupamo pomoću pokazivača, što je za tip int i klasu Node oblika int * broj = new int ( 5 ); Node * node0 = new Node (); node0 -> GetDevice ( 0 ); // ekvivalentno (*node0).GetDevice (0); delete broj ; delete node0 ; Naravno, u C-u bi koristili malloc() i free() umjesto new i delete (respektivno). Uočimo da metodi GetDevice() pristupamo na drugačiji način nego u statički alociranoj varijanti, obzirom da je node0 pokazivač na objekt tipa Node te je prvo potrebno napraviti dereferenciranje. Operator -> je samo kraći zapis za dereferenciranje pokazivača i pristup metodi objekta. Uočimo također da klasa Node ne zahtijeva navođenje parametara kod stvaranja; općenito za klase to ne mora biti slučaj. Jedini problem ovog načina rada je dosta složeno baratanje objektima u situaciji kada imate više od jednog pokazivača na isti objekt; postavlja se pitanje kada pozvati delete . Odgovor na to pitanje ns-3 nudi u vidu pametnih pokazivača (engl. smart pointers ), koji su implementirani u klasi Ptr . Za Node je pametni pokazivač oblika: Ptr node0 = CreateObject (); // metode objekta se koriste se na isti način kao kod i običnih pokazivača node0 -> GetDevice ( 0 ); // pokazivači se dereferenciraju na isti način kao i obični pokazivači ( * node0 ). GetId (); U ovom slučaju funkcija CreateObject () preuzima ulogu naredbe new ; naime, ona će stvoriti objekt tipa Node i vratiti pokazivač na njega tipa Ptr koji će biti pohranjen u varijablu node0 . S tim na umu, uočite kako ovdje nije napravljen delete ; naime pametni pokazivač učinit će da program će u izvođenju sam izvodi dealokaciju memorije za objekte na koje više niti jedan pokazivač ne pokazuje (u tome je njegova "pametnost"). Način rada koji se ovime postiže naziva se sakupljanjem smeća (engl. garbage collection ) i vrlo je čest u praksi. Pored toga, uočimo još nešto: "špičaste" zagrade su oznaka za predloške (engl. templates ). Mi se ovdje definiranjem funkcija s predlošcima nećemo baviti, obzirom da ćete se s njima sresti na kolegiju Objektno orijentirano programiranje, već ćemo samo ukratko objasniti ideju i primjene. Kada bi implementirali pametne pokazivače bez korištenja predložaka, to bi zahtijevalo da za svaku klasu koja postoji u vašem kodu (npr. Node ) imate definiranu dodatnu klasu (npr. PtrNode ), što nije problem kada je tih klasa malo, ali je kada ih program ima nekoliko stotina. Naime, svaka promjena u načinu rada pokazivača zahtijeva nekoliko stotina promjena u kodu (koje su pored toga trivijalne i mukotrpne jer svi pametni pokazivači rade na istom načelu). Stoga se zajednički dio koda koji implementira načelo rada pametnog pokazivača apstrahira i definira tako da se može koristiti s bilo kojom klasom, strukturom ili tipom podataka, te se tek kod korištenja dobiva specijalizirani pokazivač na tu klasu, strukturu ili tip podataka. Potpuno je analogna stvar sa funkcijama; za svaku definiranu klasu (npr. Node ) bilo bi potrebno definirati posebnu funkciju (npr. CreateNodeObject() ) koja bi stvarala objekt i vraćala pokazivač na njega. Ponovno, moguće je definirati funkciju koja implementira načelo rada neovisno o klasi, strukturi ili tipu podataka, a onda se kod korištenja dobiva specijalizirana funkcija za tu klasu, strukturu ili tip podataka. Note Više informacija o ovim temama i poveznice na dodatnu literaturu možete naći Wikipedijinim stranicama o metaprogramiranju korištenjem predložaka , pametnim pokazivačima i sakupljanju smeća . Povratni poziv funkcije (engl. callback ) je referenca na funkciju koja se prosljeđuje funkciji kao argument na sličan način kao i obično korišteni tipovi podataka. Primjerice, u kodu #include #include void IspisDvaBroja ( double ( * izvorBrojeva ) ( int ), int a , int b ) { std :: cout TraceConnectWithoutContext ( "PhyTxBegin" , MakeCallback ( & PhyTxLogging )); Ovdje funkcija MakeCallback() radi povratni poziv od reference na funkciju koja joj se proslijedi kao argument. Note Funkcija TraceConnectWithoutContext() vrši provjeru je li niz znakova koji prima kao prvi argument (u našem slučaju "PhyTxBegin" ) ispravan, i korisniku to signalizira na način da vraća true ako je, i false ako nije. Zbog toga program neće prekinuti izvođenje ako je uneseni niz znakova neispravan. Dakle, ako želite biti sigurni da se povezivanje zaista dogodilo, možete to provjeriti na način: bool success = devices . Get ( 0 ) -> TraceConnectWithoutContext ( "PhyTxBegin" , MakeCallback ( & PhyTxLogging )); std :: cout p ) { std :: cout #include #include #include #include using namespace ns3 ; void PhyTxLogging ( Ptr p ) { std :: cout TraceConnectWithoutContext ( "PhyTxBegin" , MakeCallback ( & PhyTxLogging )); InternetStackHelper stack ; stack . Install ( nodes ); Ipv4AddressHelper address ; address . SetBase ( "10.1.1.0" , "255.255.255.0" ); Ipv4InterfaceContainer interfaces ; interfaces = address . Assign ( devices ); UdpEchoServerHelper echoServer ( 9 ); ApplicationContainer serverApps = echoServer . Install ( nodes . Get ( 1 )); serverApps . Start ( Seconds ( 1.0 )); serverApps . Stop ( Seconds ( 10.0 )); UdpEchoClientHelper echoClient ( interfaces . GetAddress ( 1 ), 9 ); echoClient . SetAttribute ( "MaxPackets" , UintegerValue ( 4 )); echoClient . SetAttribute ( "Interval" , TimeValue ( Seconds ( 2.0 ))); echoClient . SetAttribute ( "PacketSize" , UintegerValue ( 1024 )); ApplicationContainer clientApps = echoClient . Install ( nodes . Get ( 0 )); clientApps . Start ( Seconds ( 2.0 )); clientApps . Stop ( Seconds ( 10.0 )); Simulator :: Run (); Simulator :: Destroy (); return 0 ; } Dodatak: korištenje ns-3 logging podsustava Kada bi htjeli biti konzistentni s ostatkom ns-3-a, mogli bi koristiti makro funkciju NS_LOG_UNCOND() koja bezuvjetno (engl. unconditionally ) ispisuje na standardni izlaz za greške vrijednost danog argumenta, i koristi std::cerr . Da bi makro funkcija NS_LOG_UNCOND() mogla raditi, potrebno je definirati ime naše simulacije koje će se koristiti za praćenje. Za to ćemo iskoristiti makro funkciju NS_LOG_COMPONENT_DEFINE() na početku našeg simulacijskog programa, nakon #include<> naredbi i prije funkcije main() . Ona kao argument prima upravo to ime, koje može biti proizvoljno ali mora biti jedinstveno u ns-3-u. NS_LOG_COMPONENT_DEFINE ( "VjezbaTracing" ); Zatim unutar funkcije main() možemo koristiti NS_LOG_UNCOND() na način int main () { // ... int x = 3 ; NS_LOG_UNCOND ( "Varijabla x ima vrijednost " em = CreateObject (); Postavljamo atribute: ErrorUnit , koji određuje jedinice u kojima će se mjeriti učestalost grešaka, moguće vrijednosti su ERROR_UNIT_BIT : bitovi, ERROR_UNIT_BYTE : bajtovi (zadana vrijednost), ERROR_UNIT_PACKET : paketi, ErrorRate , koji određuje učestalost pojavljivanja grešaka, na vrijednost 0.01 , RanVar , koji je slučajna varijabla na temelju čije vrijednosti će se odlučivati o tome hoće li paket biti označen kao iskrivljen ili ne, na vrijednost uobičajeno korištene uniformne slučajne variajble. em -> SetAttribute ( "ErrorUnit" , EnumValue ( RateErrorModel :: ERROR_UNIT_PACKET )); em -> SetAttribute ( "ErrorRate" , DoubleValue ( 0.01 )); em -> SetAttribute ( "RanVar" , StringValue ( "ns3::UniformRandomVariable[Min=0.0|Max=1.0]" )); Zatim iz NetDeviceContainer -a devices dohvaćamo karticu čvora koji ima odvod za pakete i postavljamo na njen atribut ReceiveErrorModel vrijednost upravo stvorenog modela grešaka, koja je u sustavu atributa tipa PointerValue . devices . Get ( 1 ) -> SetAttribute ( "ReceiveErrorModel" , PointerValue ( em )); Ovo će učiniti da će neki paketi biti odbačeni kod primitka (u prosjeku 1 na njih 100). Da bi to vidjeli, definirat ćemo funkciju PrintPhyRxPacketDrop() koja će služiti kao odvod za praćenje. Ime funkcije je proizvoljno, a zadano je da je tipa void i da prima jedan argument tipa Ptr . void PrintPhyRxPacketDrop ( Ptr p ) { std :: cout GetUid () TraceConnectWithoutContext ( "PhyRxDrop" , MakeCallback ( & PrintPhyRxPacketDrop )); Nakon pokretanja simulacije, na ekran će se ispisati u kojim su vremenskim trenucima odbačeni paketi kod primanja. Za razliku od paketa u stvarnosti, svaki paket stvoren u simulaciji ima identifikator (UID) koji služi za međusobno razlikovanje paketa i nije dio podataka koje paket nosi. Cjelokupan kod primjera je #include #include #include #include #include using namespace ns3 ; void PrintPhyRxPacketDrop ( Ptr p ) { std :: cout GetUid () em = CreateObject (); em -> SetAttribute ( "ErrorUnit" , EnumValue ( RateErrorModel :: ERROR_UNIT_PACKET )); em -> SetAttribute ( "ErrorRate" , DoubleValue ( 0.01 )); em -> SetAttribute ( "RanVar" , StringValue ( "ns3::UniformRandomVariable[Min=0.0|Max=1.0]" )); devices . Get ( 1 ) -> SetAttribute ( "ReceiveErrorModel" , PointerValue ( em )); devices . Get ( 1 ) -> TraceConnectWithoutContext ( "PhyRxDrop" , MakeCallback...