A Discrete-Event Network Simulator
Tutorial

Μικρορυθμίσεις

Χρησιμοποιώντας την Ενότητα Καταγραφής

Έχουμε ήδη δει σύντομα την ενότητα καταγραφής του ns-3 κατά τη διάρκεια του σεναρίου προσομοίωσης first.cc. Εδώ θα εξετάσουμε εις βάθος το σύστημα καταγραφής και τις περιπτώσεις χρήσης τις οποίες παρέχει.

Σύνοψη Καταγραφής

Είναι κοινή πρακτική τα μεγάλα συστήματα, όπως το ns-3, να παρέχουν τη δυνατότητα καταγραφής μηνυμάτων. Σε μερικές περιπτώσεις καταγράφονται μόνο τα μηνύματα λάθους στο “operator console” (που τυπικά είναι το stderr στα συστήματα Unix). Σε άλλα συστήματα, καταγράφονται μηνύματα προειδοποίησης καθώς και άλλα μηνύματα που παρέχουν περισσότερη πληροφορία. Τέλος σε μερικές περιπτώσεις, η διαδικασία καταγραφής χρησιμοποιείται για να παράγει μηνύματα αποσφαλμάτωσης που όταν παρουσιαστούν θολώνουν την έξοδο.

Κατά το ns-3, όλα τα παραπάνω επίπεδα καταγραφής είναι χρήσιμα και για το λόγο αυτό παρέχεται η δυνατότητα καταγραφής μηνυμάτων σε πολλαπλά επίπεδα. Η διαδικασία καταγραφής μπορεί να απενεργοποιηθεί πλήρως, να ενεργοποιηθεί ανά συστατικό στοιχείο (component), ή να ενεργοποιηθεί συνολικά στο σύστημα. Επίσης δίνεται η δυνατότητα επιλογής του εύρους των μηνυμάτων σε επίπεδα. Η ενότητα καταγραφής του ns-3 παρέχει έναν απλό και εύκολο τρόπο για εξαγωγή χρήσιμων πληροφοριών από μια προσομοίωση.

Πρέπει να γίνει σαφές ότι παρέχεται ένας μηχανισμός γενικού σκοπού — ιχνηλασίας — για την εξαγωγή δεδομένων από τα μοντέλα του χρήστη, ο οποίος θα έπρεπε να προτιμάται για την έξοδο της εξομοίωσης (δείτε τον τομέα “Χρησιμοποιώντας το Σύστημα Ιχνηλασίας” για περισσότερες πληροφορίες). Ο μηχανισμός της καταγραφής αντίστοιχα θα πρέπει να προτιμάται για πληροφορίες αποσφαλμάτωσης, για μηνύματα προειδοποίησης και λάθους, καθώς και για περιπτώσεις που χρειάζεται να πάρουμε ένα γρήγορο μήνυμα από ένα μοντέλο ή σενάριο προσομοίωσης.

Στο σύστημα έχουν οριστεί προς το παρόν επτά επίπεδα μηνυμάτων καταγραφής αυξανόμενου εύρους μηνυμάτων.

  • LOG_ERROR — Καταγραφή μηνυμάτων λάθους (σχετική μακροεντολή: NS_LOG_ERROR);
  • LOG_WARN — Καταγραφή μηνυμάτων προειδοποίησης (σχετική μακροεντολή: NS_LOG_WARN);
  • LOG_DEBUG — Καταγραφή σχετικά σπανίων, προσαρμοσμένων μηνυμάτων αποσφαλμάτωσης (σχετική μακροεντολή: NS_LOG_DEBUG);
  • LOG_INFO — Καταγραφή πληροφοριακών μηνυμάτων για την πρόοδο του προγράμματος (σχετική μακροεντολή: NS_LOG_INFO);
  • LOG_FUNCTION — Καταγραφή ενός μηνύματος περιγραφής για κάθε συνάρτηση που καλείται (δύο σχετικές μακροεντολές: NS_LOG_FUNCTION, που χρησιμοποιείται για member functions, και NS_LOG_FUNCTION_NOARGS, που χρησιμοποιείται για static functions);
  • LOG_LOGIC – Καταγραφή μηνυμάτων που περιγράφουν τη λογική ροή μέσα σε μια συνάρτηση (σχετική μακροεντολή: NS_LOG_LOGIC);
  • LOG_ALL — Καταγραφή όλων των παραπάνω (καμία σχετική μακροεντολή).

Για κάθε επίπεδο, εκτός από το LOG_TYPE υπάρχει και το LOG_LEVEL_TYPE το οποίο αν χρησιμοποιηθεί, ενεργοποιεί την καταγραφή όλων των επιπέδων που βρίσκονται πάνω από αυτό. (Κατά συνέπεια, τα LOG_ERROR και LOG_LEVEL_ERROR είναι ισοδύναμα μεταξύ τους όπως επίσης και τα LOG_ALL και and LOG_LEVEL_ALL.) Για παράδειγμα, ενεργοποιώντας το LOG_INFO θα παραχθούν μόνο μηνύματα από την μακροεντολή NS_LOG_INFO, ενώ ενεργοποιώντας το LOG_LEVEL_INFO θα παραχθούν επίσης και μηνύματα από τις μακροεντολές NS_LOG_DEBUG, NS_LOG_WARN και NS_LOG_ERROR.

Επίσης παρέχεται μια μακροεντολή καταγραφής χωρίς περιορισμούς, ανεξάρτητη από τα επίπεδα καταγραφής ή την επιλογή συστατικού στοιχείου.

  • NS_LOG_UNCOND – Καταγραφή του μηνύματος χωρίς περιορισμούς (κανένα σχετικό επίπεδο καταγραφής).

Το κάθε επίπεδο μπορεί να ζητηθεί μεμονωμένο ή σε συνδυασμό με τα άλλα. Η καταγραφή αντίστοιχα μπορεί να ρυθμιστεί μέσω μιας μεταβλητής του περιβάλλοντος φλοιού (NS_LOG) ή μέσω καταγραφής της κλήση συνάρτησης συστήματος. Όπως έχουμε ήδη δει νωρίτερα σε αυτό τον οδηγό, το σύστημα καταγραφής έχει τεκμηρίωση Doxygen και ίσως είναι χρήσιμο να μελετήσετε την τεκμηρίωση της Ενότητας Καταγραφής.

Αφού έχετε διαβάσει την τεκμηρίωση σε μεγάλο βαθμό, είναι ώρα να χρησιμοποιήσουμε αυτή τη γνώση για να εξάγουμε μερικές χρήσιμες πληροφορίες από το παράδειγμα scratch/myfirst.cc που έχετε ήδη δημιουργήσει.

Ενεργοποίηση Καταγραφής

Μπορούμε να χρησιμοποιήσουμε τη μεταβλητή συστήματος NS_LOG για να ενεργοποιήσουμε επιπλέον λειτουργίες καταγραφής, αλλά ας τρέξουμε αρχικά το παρακάτω σενάριο

$ ./waf --run scratch/myfirst

Θα πρέπει να βλέπετε τώρα τη γνώριμη έξοδο του πρώτου παραδείγματος από τα προγράμματα του ns-3.

$ 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.413s)
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Τα μηνύματα “Sent” και “Received” που βλέπετε παραπάνω είναι στην πραγματικότητα μηνύματα καταγραφής από τα UdpEchoClientApplication και UdpEchoServerApplication. Μπορούμε, για παράδειγμα, να ζητήσουμε από την εφαρμογή του χρήστη να τυπώσει περισσότερες πληροφορίες θέτοντας το επίπεδο καταγραφής της μέσω της μεταβλητής περιβάλλοντος NS_LOG.

Στη συνέχεια θεωρούμε ότι ο χρήστης χρησιμοποιεί έναν φλοιό που χρησιμοποιεί τη σύνταξη “VARIABLE=value” όπως ο sh. Αν χρησιμοποιείτε ένα φλοιό τύπου csh, τότε πρέπει να μετασχηματίσετε τα παρακάτω παραδείγματα σε σύνταξη “setenv VARIABLE value”.

Αυτή τη στιγμή, η εφαρμογή UDP echo client ανταποκρίνεται στην παρακάτω γραμμή κώδικα του scratch/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

Αυτή η γραμμή κώδικα ενεργοποιεί το επίπεδο καταγραφής LOG_LEVEL_INFO. Όταν περάσουμε κάποια παράμετρο επιπέδου καταγραφής, στην ουσία ενεργοποιούμε το συγκεκριμένο επίπεδο και όλα τα χαμηλότερά του. Στο συγκεκριμένο παράδειγμα, ενεργοποιούμε τα NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN και NS_LOG_ERROR. Μπορούμε να αυξήσουμε το επίπεδο καταγραφής και να πάρουμε περισσότερες πληροφορίες χωρίς να χρειαστεί να αλλάξουμε το σενάριο και να επαναμεταγλωτίσσουμε, αν θέσουμε τη μεταβλητή περιβάλλοντος NS_LOG ως εξής:

$ export NS_LOG=UdpEchoClientApplication=level_all

Αυτό θα θέσει τη μεταβλητή περιβάλλοντος NS_LOG στο αλφαριθμητικό,

UdpEchoClientApplication=level_all

Το αριστερό σκέλος της ανάθεσης είναι το όνομα του στοιχείου καταγραφής που θέλουμε να θέσουμε, ενώ το δεξί σκέλος είναι το όρισμα που θέλουμε να χρησιμοποιήσουμε. Στην περίπτωσή μας θα ενεργοποιήσουμε όλα τα επίπεδα αποσφαλμάτωσης για τη συγκεκριμένη εφαρμογή. Αν τρέξετε το σενάριο θέτοντας την NS_LOG με αυτόν τον τρόπο, το σύστημα καταγραφής του ns-3 θα δει την αλλαγή και θα πρέπει να δείτε την παρακάτω έξοδο:

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.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Η πρόσθετη πληροφορία αποσφαλμάτωσης που παρέχεται από την εφαρμογή βρίσκεται στο επίπεδο NS_LOG_FUNCTION. Αυτή εμφανίζεται κάθε φορά που καλείται μια συνάρτηση της εφαρμογής. Γενικά η χρήση της NS_LOG_FUNCTION(this) ενδείκνυται σε member functions, ενώ η NS_LOG_FUNCTION_NOARGS() σε static functions. Σημειώστε όμως ότι στο σύστημα ns-3, δεν υπάρχει η απαίτηση τα μοντέλα να υποστηρίζουν κάποια συγκεκριμένη λειτουργία καταγραφής. Η απόφαση για το εύρος της πληροφορίας που καταγράφεται, επαφίεται στον προγραμματιστή του μοντέλου. Σε περίπτωση εφαρμογής αντανάκλασης, ένα μεγάλο μέρος της εξόδου καταγραφής είναι διαθέσιμο.

Μπορείτε να δείτε μια καταγραφή των κλήσεων σε συναρτήσεις που έγιναν στην εφαρμογή. Αν κοιτάξετε προσεκτικά, θα παρατηρήσετε μια μονή στήλη μεταξύ του αλφαριθμητικού UdpEchoClientApplication και του ονόματος της μεθόδου, αντί του τελεστή :: της C++ που θα περιμένατε. Αυτό γίνεται εσκεμμένα.

Το όνομα δεν είναι στην πραγματικότητα το όνομα μιας κλάσης, αλλά το όνομα του στοιχείου καταγραφής. Όταν υπάρχει αντιστοίχηση 1-προς-1 μεταξύ του αρχείου πηγής και της κλάσης, το όνομα θα είναι γενικά ίδιο με της κλάσης. Η μονή στήλη χρησιμοποιείται αντί της διπλής για να διαχωρίσει το στοιχείο καταγραφής από το όνομα της κλάσης.

Σε μερικές περιπτώσεις είναι δύσκολο να προσδιορίσεις ποια μέθοδος ακριβώς παράγει ένα μήνυμα καταγραφής. Αν κοιτάξετε το παραπάνω κείμενο, ίσως αναρωτιέστε από που προέρχεται το αλφαριθμητικό “Received 1024 bytes from 10.1.1.2”. Μπορείτε να λύσετε την απορία σας μέσω του επιπέδου prefix_func στη μεταβλητή συστήματος NS_LOG. Δοκιμάστε το παρακάτω,

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Σημειώστε ότι τα εισαγωγικά χρειάζονται, αφού η κάθετη στήλη χρησιμοποιείται στο Unix για να υποδείξει τον τελεστή Ή.

Τώρα αν τρέξετε το σενάριο, θα παρατηρήσετε ότι το σύστημα καταγραφής προσθέτει σε κάθε μήνυμα ένα πρόθεμα με το όνομα του στοιχείου.

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.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Μπορείτε να ταυτοποιήσετε τώρα όλα τα μηνύματα που προέρχονται από την εφαρμογή πελάτη UDP echo. Το μήνυμα “Received 1024 bytes from 10.1.1.2” φαίνεται τώρα ξεκάθαρα ότι προέρχεται από την εφαρμογή του πελάτη. Αντίστοιχα το άλλο μήνυμα προέρχεται από την εφαρμογή του εξυπηρετητή UDP echo. Μπορούμε να ενεργοποιήσουμε το στοιχείο αυτό, προσθέτοντας μια λίστα στοιχείων στη μεταβλητή περιβάλλοντος NS_LOG.

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
               UdpEchoServerApplication=level_all|prefix_func'

Προσοχή: Θα χρειαστεί να αφαιρέσετε τη νέα γραμμή μετά το : στο παραπάνω κείμενο του παραδείγματος, το οποίο υπάρχει απλά για λόγους μορφοποίησης.

Αν τρέξετε το σενάριο τώρα, θα παρατηρήσετε όλα τα μηνύματα τόσο από την εφαρμογή του πελάτη όσο και του εξυπηρετητή. Αυτό μπορεί να αποδειχτεί πολύ χρήσιμο σε περιπτώσεις προβλημάτων αποσφαλμάτωσης.

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.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
UdpEchoServerApplication:HandleRead(): Echoing packet
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Σε κάποιες περιπτώσεις είναι επίσης χρήσιμο να μπορούμε να δούμε τον χρόνο εξομοίωσης κατά τον οποίο παράχθηκε ένα μήνυμα καταγραφής. Μπορείτε να το κάνετε αυτό με τον τελεστή Ή στο ψηφίο prefix_time.

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time:
               UdpEchoServerApplication=level_all|prefix_func|prefix_time'

Και εδώ, όπως προηγουμένως, πρέπει να αφαιρέσετε τη νέα γραμμή. Αν τρέξετε το σενάριο θα δείτε την παρακάτω έξοδο:

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)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Βλέπετε πως έγινε κλήση στο δημιουργό της UdpEchoServer τη χρονική στιγμή 0. Αυτό στην πραγματικότητα συμβαίνει πριν ξεκινήσει η εξομοίωση, αλλά ο χρόνος που εμφανίζεται είναι το 0. Το ίδιο συμβαίνει και για το δημιουργό της UdpEchoClient.

Θυμηθείτε ότι στο σενάριο scratch/first.cc η ενεργοποίηση την εφαρμογής του εξυπηρετητή γίνεται το πρώτο δευτερόλεπτο της εξομοίωσης. Μπορείτε να δείτε ότι η μέθοδος του εξυπηρετητή StartApplication όντως καλείται στη χρονική στιγμή 1. Μπορείτε επίσης να δείτε ότι η εφαρμογή του πελάτη ξεκινάει τη χρονική στιγμή 2, όπως ζητήσαμε στο σενάριο.

Μπορείτε να παρακολουθήσετε την πρόοδο της εξομοίωσης από την κλήση ScheduleTransmit στον πελάτη, που καλεί την Send στην επανάκληση HandleRead στην εφαρμογή του εξυπηρετητή. Σημειώστε ότι ο παρερχόμενος χρόνος για την αποστολή του πακέτου στη σύνδεση είναι 3.69 δευτερόλεπτα. Μπορείτε να δείτε επίσης το μήνυμα καταγραφής του εξυπηρετητή που αναφέρει ότι το πακέτο έφυγε και στη συνέχεια, μετά από την καθυστέρηση του καναλιού, βλέπετε την άφιξη του πακέτου στον πελάτη μέσω της μεθόδου HandleRead.

Υπάρχουν επίσης πολλά που συμβαίνουν κατά τη διάρκεια της εξομοίωσης τα οποία δεν εμφανίζονται. Μπορείτε πολύ εύκολα να παρακολουθήσετε ολόκληρη τη διαδικασία αν θέσετε τη μεταβλητή NS_LOG στην παρακάτω τιμή,

$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'

Ο αστερίσκος στην παραπάνω εντολή είναι ο τελεστής που δηλώνει ότι θέλουμε να ενεργοποιηθεί η καταγραφή σε όλα τα συστατικά στοιχεία που χρησιμοποιούνται στην εξομοίωση. Δεν θα συμπεριλάβουμε εδώ την έξοδο (μια που αυτή παράγει 1265 γραμμές απλά για ένα πακέτο), αλλά μπορείτε να ανακατευθύνετε την πληροφορία αυτή σε ένα αρχείο και να το ανοίξετε με κάποιον επεξεργαστή κειμένου,

$ ./waf --run scratch/myfirst > log.out 2>&1

Προσωπικά χρησιμοποιώ αυτή τη φλύαρη μέθοδο καταγραφής όταν παρουσιάζεται ένα πρόβλημα και δεν έχω την παραμικρή ιδέα που βρίσκεται το λάθος. Μπορώ να ακολουθήσω τη ροή της εκτέλεσης του κώδικα πολύ εύκολα χωρίς να χρειάζεται να θέσω σημεία διακοπής (breakpoints) ή να εξετάσω βήμα-βήμα τον κώδικα στον αποσφαλματωτή. Μπορώ απλά να ανοίξω την έξοδο στον αγαπημένο μου επεξεργαστή κειμένου και να ψάξω για πράγματα που περιμένω να συμβαίνουν, αλλά και για πράγματα που δεν περιμένω να συμβαίνουν. Όταν έχω μια γενική ιδέα του τι πάει λάθος, μεταβαίνω σε έναν αποσφαλματωτή για μια πλήρη εξέταση του προβλήματος. Αυτού του είδους η έξοδος μπορεί να είναι ιδιαίτερα χρήσιμη όταν το σενάριο κάνει κάτι τελείως απρόβλεπτο. Αν χρησιμοποιήσετε απλά τον αποσφαλματωτή, μπορείτε να παραβλέψετε τελείως μια απρόβλεπτη συμπεριφορά. Με την καταγραφή μπορούμε να την εντοπίσουμε γρήγορα.

Προσθήκη Καταγραφής σε Κώδικα

Μπορείτε να προσθέσετε νέες καταγραφές στις εξομοιώσεις σας καλώντας το στοιχείο καταγραφής μέσω διαφόρων μακροεντολών. Ας το επιχειρήσουμε στο σενάριο εξομοίωσης myfirst.cc που βρίσκεται στον φάκελο scratch.

Θυμηθείτε ότι έχουμε ορίσει ένα στοιχείο καταγραφής σε εκείνο το σενάριο:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Γνωρίζετε τώρα ότι μπορείτε να ενεργοποιήσετε όλες τις δυνατές καταγραφές για αυτό το στοιχείο, θέτοντας τη μεταβλητή περιβάλλοντος NS_LOG σε κάποιο επίπεδο. Ας προχωρήσουμε στην προσθήκη καταγραφής στο σενάριο. Η μακροεντολή που προσθέτει καταγραφή σε επίπεδο πληροφοριακών μηνυμάτων είναι η NS_LOG_INFO. Θέλουμε να προσθέσουμε ένα μήνυμα (πριν αρχίσουμε να δημιουργούμε κόμβους) που αναφέρει ότι το σενάριο δημιουργεί μια τοπολογία “Creating Topology”. Αυτό γίνεται όπως δείχνουμε στον παρακάτω κώδικα,

Ανοίξτε το scratch/myfirst.cc σε έναν επεξεργαστή κειμένου και προσθέστε τη γραμμή,

NS_LOG_INFO ("Creating Topology");

αμέσως πριν από τις γραμμές,

NodeContainer nodes;
nodes.Create (2);

Τώρα ας οικοδομήσουμε το σενάριο χρησιμοποιώντας το waf και καθαρίζοντας τη μεταβλητή NS_LOG ώστε να απενεργοποιήσουμε το torrent της καταγραφής που είχαμε προηγουμένως ενεργοποιήσει:

$ ./waf
$ export NS_LOG=

Αν τρέξετε το σενάριο τώρα,

$ ./waf --run scratch/myfirst

δεν θα δείτε το νέο μήνυμα, αφού το σχετικό στοιχείο καταγραφής (FirstScriptExample) δεν έχει ενεργοποιηθεί. Για να δείτε το μήνυμά σας θα πρέπει να το ενεργοποιήσετε με επίπεδο καταγραφής μεγαλύτερο ή ίσο με NS_LOG_INFO. Αν θέλετε απλά να δείτε το συγκεκριμένο επίπεδο καταγραφής, μπορείτε να το ενεργοποιήσετε ως εξής,

$ export NS_LOG=FirstScriptExample=info

Αν τρέξετε τώρα το σενάριο, θα δείτε το μήνυμα καταγραφής “Creating Topology”,

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.404s)
Creating Topology
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Χρησιμοποιώντας ορίσματα γραμμής εντολών

Παρακάμπτοντας Προκαθορισμένα Ορίσματα

Ένας άλλος τρόπος που μπορείτε να αλλάξετε τον τρόπο που τα ns-3 σενάρια συμπεριφέρονται, χωρίς να χρειάζεται επεξεργασία και οικοδόμηση, είναι μέσω ορισμάτων γραμμής εντολών. Παρέχουμε ένα μηχανισμό που να αναλύσει τα ορίσματα γραμμής εντολών και αυτόματα θέτει τις τοπικές και καθολικές μεταβλητές με βάση τα ορίσματα αυτά.

Το πρώτο βήμα για τη χρήση του συστήματος ορισμάτων γραμμής εντολών, είναι να δηλώσουμε τον αναλυτή γραμμής εντολών. Αυτό γίνεται πολύ απλά (στο κύριο πρόγραμμα σας) όπως στον ακόλουθο κώδικα,

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

  CommandLine cmd;
  cmd.Parse (argc, argv);

  ...
}

Αυτό το απλό απόσπασμα δύο γραμμών είναι πραγματικά πολύ χρήσιμο από μόνο του. Ανοίγει την πόρτα για τα συστήματα καθολικών μεταβλητών και Attributes του ns-3. Προσθέστε αυτές τις δύο γραμμές κώδικα στο σενάριο scratch/first.cc στην αρχή της main. Οικοδομήστε το σενάριο και τρέξτε το, αλλά ζητήστε βοήθεια από το σενάριο με τον ακόλουθο τρόπο,

$ ./waf --run "scratch/myfirst --PrintHelp"

Αυτό θα ζητήσει από τον Waf να τρέξει το σενάριο scratch/myfirst και να περάσει το όρισμα γραμμής εντολών --PrintHelp στο σενάριο. Τα εισαγωγικά απαιτούνται για να ορίσουμε ποιο από τα προγράμματα παίρνει το κάθε όρισμα. Ο αναλυτής της γραμμής εντολών θα δει το όρισμα --PrintHelp και θα αποκριθεί με,

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.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.

Ας επικεντρωθούμε στην επιλογή --PrintAttributes. Έχουμε ήδη υπαινιχθεί για το σύστημα Ορισμάτων ns-3 ενώ ακολουθούσαμε βήμα-βήμα το σενάριο first.cc. Αν κοιτάξουμε τις ακόλουθες γραμμές κώδικα,

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

παρατηρούμε ότι το DataRate είναι στην πραγματικότητα ένα Όρισμα του PointToPointNetDevice`. Ας χρησιμοποιήσουμε τον αναλυτή ορισμάτων γραμμής εντολών για να παρατήσουμε τα Attributes του PointToPointNetDevice. Η λίστα βοήθειας αναφέρει ότι πρέπει να παρέχουμε ένα TypeId. Αυτό αντιστοιχεί στο όνομα της κλάσης στην οποία ανήκουν τα Attributes. Σε αυτή την περίπτωση θα είναι ns3::PointToPointNetDevice. Αν το τυπώσουμε,

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice"

Το σύστημα θα τυπώσει όλα τα Attributes αυτού του είδους συσκευών δικτύου. Μεταξύ των Attributes θα δείτε είναι και το ακόλουθο,

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
  The default data rate for point to point links

Αυτή είναι η προεπιλεγμένη τιμή που θα χρησιμοποιηθεί όταν δημιουργείται στο σύστημα μία PointToPointNetDevice. Εμείς παρακάμψαμε αυτή την προεπιλογή με την ρύθμιση Attribute στο PointToPointHelper. Ας χρησιμοποιήσουμε τις προεπιλεγμένες τιμές για τις συσκευές point-to-point και τα κανάλια με τη διαγραφή των κλήσεων SetDeviceAttribute και SetChannelAttribute από το myfirst.cc στον κατάλογο scratch.

Το σενάριό σας πρέπει τώρα να δηλώσει το PointToPointHelper και να μην κάνει κάποια set ενέργεια όπως στο ακόλουθο παράδειγμα,

...

NodeContainer nodes;
nodes.Create (2);

PointToPointHelper pointToPoint;

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

...

Ας οικοδομήσουμε το νέο σενάριο με Waf (./waf) επιτρέποντας κάποια καταγραφή από την εφαρμογή διακομιστή UDP echo και ενεργοποιώντας το πρόθεμα ώρας.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

Αν τρέξουμε το σενάριο, θα πρέπει να δούμε την ακόλουθη έξοδο,

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.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Θυμηθείτε ότι την τελευταία φορα που είδαμε το χρόνο εξομοίωσης όπου το πακέτο παρελήφθηκε από τον διακομιστή, ήταν στα 2.00369 δευτερόλεπτα.

2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1

Τώρα λαμβάνει το πακέτο στα 2.25732 δευτερόλεπτα. Η αλαγή αυτή οφείλεται στη μείωση του ρυθμού μετάδοσης του PointToPointNetDevice από τα 5 megabits ανά δευτερόλεπτο στην προκαθορισμένη τιμή των 32768 bits ανά δευτερόλεπτο.

Αν παρείχαμε το νέο DataRate μέσω της γραμμής εντολών, θα μπορούσαμε να επιταχύνουμε την εξομοίωσή μας και πάλι. Αυτό γίνεται με τον ακόλουθο τρόπο,

$ ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"

Αυτό θα ορίσει την προκαθορισμένη τιμή του DataRate Attribute πάλι σε 5 megabits ανά δευτερόλεπτο. Εκπλαγήκατε από το αποτέλεσμα; Φαίνεται ότι για να επαναφέρουμε την αρχική συμπεριφορά του σεναρίου, θα πρέπει να ρυθμίσουμε την καθυστέρηση του καναλιού στην ταχύτητα του φωτός. Μπορούμε να ζητήσουμε από το σύστημα γραμμής εντολών να εκτυπώσει τα Attributes του καναλιού, ακριβώς όπως κάναμε για την δικτυακή συσκευή:

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel"

Ανακαλύπτουμε ότι το Delay Attribute του καναλιού είναι ενεργοποιημένο με τον ακόλουθο τρόπο:

--ns3::PointToPointChannel::Delay=[0ns]:
  Transmission delay through the channel

Μπορούμε λοιπόν να θέσουμε και τις δύο προκαθορισμένες τιμές μέσω του συστήματος γραμμής εντολών,

$ ./waf --run "scratch/myfirst
  --ns3::PointToPointNetDevice::DataRate=5Mbps
  --ns3::PointToPointChannel::Delay=2ms"

όπου επαναφέρουμε τον χρονισμό που είχαμε όταν θέσαμε το DataRate και το Delay στο σενάριο:

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.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.00369s Received 1024 bytes from 10.1.1.1
2.00369s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Σημειώστε ότι το πακέτο λαμβάνεται και πάλι από το διακομιστή στα 2.00369 δευτερόλεπτα. Στην ουσία θα μπορούσαμε να ορίσουμε με αυτόν τον τρόπο οποιαδήποτε από τα Attributes τα οποία χρησιμοποιούνται στο σενάριο. Ειδικότερα, θα μπορούσαμε να θέσουμε το UdpEchoClient Attribute MaxPackets σε κάποια διαφορετική τιμή από τη μονάδα.

Πώς θα το πραγματοποιούσατε αυτό; Κάντε μια δοκιμή. Να θυμάστε ότι πρέπει να σχολιάσετε το μέρος που αντικαθιστά το προεπιλεγμένο Attribute και ορίσετε ρητά το MaxPackets στο σενάριο. Στη συνέχεια θα πρέπει να ξαναοικοδομήσετε το σενάριο. Θα πρέπει επίσης να βρείτε τη σύνταξη για να ορίσετε τη νέα προεπιλεγμένη τιμή της ιδιότητας, χρησιμοποιώντας τη βοήθεια της γραμμής εντολών. Μόλις έχετε καταλάβει αυτό το βήμα, θα πρέπει να είστε σε θέση να ελέγχετε τον αριθμό των πακέτων που αντανακλώνται από τη γραμμή εντολών. Μιας που είμαστε καλά παιδιά, θα σας πούμε ότι η γραμμή εντολών σας θα πρέπει να μοιάζει κάπως έτσι,

$ ./waf --run "scratch/myfirst
  --ns3::PointToPointNetDevice::DataRate=5Mbps
  --ns3::PointToPointChannel::Delay=2ms
  --ns3::UdpEchoClient::MaxPackets=2"

Συνδέοντας τις δικές σας τιμές

Μπορείτε να προσθέσετε τις δικές σας συνδέσεις στο σύστημα γραμμής εντολών. Αυτό γίνεται με έναν απλό τρόπο, απλά χρησιμοποιώντας τη μέθοδο AddValue στον αναλυτή γραμμής εντολών.

Ας χρησιμοποιήσουμε αυτή τη λειτουργία για να ορίσουμε με έναν τελείως διαφορετικό τρόπο τον αριθμό των πακέτων που αντανακλώνται. Ας προσθέσουμε στη συνάρτηση main μία τοπική μεταβλητή με το όνομα nPackets. Θα την αρχικοποιήσουμε στην τιμή 1 για να ταυτιστεί με την προηγούμενη προκαθορισμένη τιμή. Για να επιτρέψουμε στον αναλυτή γραμμής εντολών να τροποποιήσει την τιμή αυτή, πρέπει να συνδέσουμε την τιμή στον αναλυτή. Αυτό το κάνουμε με την προσθήκη μιας κλήσης στην AddValue. Αλλάξτε το σενάριο scratch/myfirst.cc έτσι ώστε να αρχίζει με αυτόν τον κώδικα,

int
main (int argc, char *argv[])
{
  uint32_t nPackets = 1;

  CommandLine cmd;
  cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
  cmd.Parse (argc, argv);

  ...

Κυλήστε το σενάριο προς τα κάτω μέχρι το σημείο όπου θέτουμε το όρισμα MaxPackets και αλλάξτε το έτσι ώστε να δείχνει στη μεταβλητή nPackets αντί να παίρνει την τιμή 1 όπως δείχνουμε παρακάτω,

echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));

Τώρα αν τρέξετε το σενάριο και παρέχετε το όρισμα --PrintHelp, θα μπορείτε να δείτε στην οθόνη βοήθειας το νέο σας User Argument.

$ ./waf --run "scratch/myfirst --PrintHelp"
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.403s)
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.
User Arguments:
    --nPackets: Number of packets to echo

Αν θέλετε να καθορίσετε τον αριθμό των πακέτων που αντανακλώνται, μπορείτε να θέσετε το όρισμα --nPackets στην γραμμή εντολών,

$ ./waf --run "scratch/myfirst --nPackets=2"

Θα πρέπει να εμφανίζεται τώρα

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.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
Sent 1024 bytes to 10.1.1.2
3.25732s Received 1024 bytes from 10.1.1.1
3.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Έχετε αντανακλάσει τώρα δύο πακέτα. Φαίνεται ιδιαίτερα εύκολο, έτσι δεν είναι;

Αν είστε ένας χρήστης ns-3 λοιπόν, μπορείτε να χρησιμοποιείτε το σύστημα ορισμάτων γραμμής εντολών για να ελέγχετε τα Attributes και τις μεταβλητές συστήματος. Αν είστε ο συγγραφέας ενός μοντέλου, μπορείτε να προσθέτετε νέα Attributes στα Objects σας και αυτά θα είναι αυτόματα διαθέσιμα στους χρήστες σας για να θέσουν τιμές μέσω του συστήματος γραμμής εντολών. Αν είστε ο συγγραφέας ενός σεναρίου, μπορείτε να προσθέτετε νέες μεταβλητές στα σενάριά σας και να τις συνδέετε στο σύστημα γραμμής εντολών χωρίς ιδιαίτερο κόπο.

Χρησιμοποιώντας το Σύστημα Ιχνηλασίας

Το όλο νόημα της εξομοίωσης είναι να παράγουμε έξοδο για μελλοντικές μελέτες, και το σύστημα ιχνηλασίας του ns-3 είναι ένας πρωταρχικός μηχανισμός για το σκοπό αυτό. Αφού το ns-3 είναι ένα πρόγραμμα σε γλώσσα C++, μπορούν να χρησιμοποιηθούν τυποποιημένες λειτουργίες για τη παραγωγή εξόδου από προγράμματα C++.

#include <iostream>
...
int main ()
{
  ...
  std::cout << "The value of x is " << x << std::endl;
  ...
}

Θα μπορούσατε ακόμη και να χρησιμοποιήσετε τη μονάδα καταγραφής για να προσθέσετε κάποια δομή στη λύση σας. Υπάρχουν πολλά γνωστά προβλήματα που δημιουργούνται από τέτοιες προσεγγίσεις και έτσι παρέχουμε ένα υποσύστημα ιχνηλασίας γεγονότων για να αντιμετωπίσουμε τα θέματα που θεωρήσαμε ότι ήταν σημαντικά.

Οι βασικοί στόχοι του συστήματος ανίχνευσης του ns-3 είναι:

  • Για τα βασικά του καθήκοντα, το σύστημα ανίχνευσης θα πρέπει να επιτρέπει στο χρήστη να παράγει τυποποιημένη ιχνηλασία για δημοφιλείς πηγές ιχνηλασίας, και να προσαρμόζει ποια αντικείμενα δημιουργούν την ιχνηλασία;
  • Οι μέσοι χρήστες θα πρέπει να είναι σε θέση να επεκτείνουν το σύστημα ιχνηλασίας ώστε να τροποποιούν τη μορφή εξόδου που παράγεται, ή να εισάγουν νέες πηγές ιχνηλασίας, χωρίς να αλλάζουν τον πυρήνα του προσομοιωτή;
  • Οι προχωρημένοι χρήστες μπορούν να τροποποιήσουν τον πυρήνα προσομοιωτή ώστε να προσθέτουν νέες πηγές και καταβόθρες ιχνηλασίας.

Το σύστημα ιχνηλασίας ns-3 είναι χτισμένο στις έννοιες των ανεξάρτητων πηγών και καταβοθρών ιχνηλασίας, και ενός ενιαίου μηχανισμού για τη σύνδεση πηγών σε καταβόθρες. Οι πηγές ίχνους είναι οντότητες οι οποίες μπορούν να σηματοδοτήσουν γεγονότα που συμβαίνουν σε μια εξομοίωση και παρέχουν πρόσβαση σε ενδιαφέροντα δεδομένα. Για παράδειγμα, μια πηγή ίχνους θα μπορούσε να υποδείξει πότε ένα πακέτο λαμβάνεται από μία δικτυακή συσκευή και να παρέχει πρόσβαση στα περιεχόμενα του πακέτου για τους ενδιαφερόμενες καταβόθρες ίχνους.

Οι πηγές ίχνους δεν είναι χρήσιμες από μόνες τους, θα πρέπει να είναι “συνδεδεμένες” με άλλα κομμάτια κώδικα που κάνουν πραγματικά κάτι χρήσιμο με τις πληροφορίες που παρέχονται από την καταβόθρα. Οι καταβόθρες ίχνους είναι οι καταναλωτές των γεγονότων και των δεδομένων που παρέχονται από τις πηγές ίχνους. Για παράδειγμα, κάποιος θα μπορούσε να δημιουργήσει μία καταβόθρα ίχνους που θα εκτύπωνε ενδιαφέροντα μέρη του ληφθέντος πακέτου (όταν συνδέεται με την πηγή ίχνους του προηγούμενου παραδείγματος).

Το σκεπτικό για αυτή την ρητή διαίρεση είναι να επιτρέψει στους χρήστες να επισυνάπτουν νέους τύπους καταβοθρών στις υπάρχουσες πηγές ιχνηλασίας, χωρίς να απαιτείται επεξεργασία και επαναμεταγλωτισμός του πυρήνα του εξομοιωτή. Έτσι, στο παραπάνω παράδειγμα, ο χρήστης μπορεί με την επεξεργασία μόνο του σεναρίου του χρήστη να καθορίσει μια νέα καταβόθρα ιχνηλασίας στο σενάριό του και να το επισυνάψει σε μια υπάρχουσα πηγή ιχνηλασίας που ορίζεται στον πυρήνα εξομοίωσης.

Σε αυτόν τον οδηγό, θα εξετάσουμε κάποιες προκαθορισμένες πηγές και καταβόθρες και θα δείξουμε πως μπορούν να προσαρμοστούν με μια μικρή προσπάθεια. Δείτε το εγχειρίδιο ns-3 ή τις ενότητες how-to για πληροφορίες σχετικά με τη διαμόρφωση προηγμένης ιχνηλασίας, συμπεριλαμβανομένων της επέκτασης του χώρου ονομάτων ιχνηλασίας και της δημιουργίας νέων πηγών ιχνηλασίας.

Ιχνηλασία Ascii

Το ns-3 παρέχει λειτουργικότητα βοήθειας που συμπεριλαμβάνει το σύστημα ιχνηλασίας χαμηλού επιπέδου για να σας βοηθήσει με τις λεπτομέρειες που εμπλέκονται στη διαμόρφωση μερικών ευκατανόητων ιχνών πακέτων. Αν ενεργοποιήσετε αυτή τη λειτουργία, θα δείτε την εξόδο σε αρχεία ASCII — εξού και το όνομα. Για όσους είναι εξοικειωμένοι με την έξοδο του ns-2, το ίχνος αυτού του είδους είναι ανάλογο με το out.tr που παράγεται από πολλά σενάρια.

Ας πάμε κατευθείαν να προσθέσουμε κάποια έξοδο ιχνηλασίας ASCII στο σενάριό μας scratch/myfirst.cc. Ακριβώς πριν από την κλήση προς Simulator::Run (), προσθέστε τις ακόλουθες γραμμές κώδικα:

AsciiTraceHelper ascii;
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));

Όπως και σε πολλά άλλα ιδιώματα ns-3, αυτός ο κώδικας χρησιμοποιεί ένα βοηθητικό αντικείμενο για να δημιουργήσει ίχνη ASCII. Η δεύτερη γραμμή περιέχει δύο ένθετες κλήσεις μεθόδων. Η “εσωτερική” μέθοδος, CreateFileStream() χρησιμοποιεί ένα ανώνυμο ιδίωμα αντικειμένου για να δημιουργήσει ένα αντικείμενο ρεύματος αρχείου στη στοίβα (χωρίς όνομα αντικειμένου) και να το περάσει στην καλούμενη μέθοδο. Θα επιστρέψουμε στο σημείο αυτό αργότερα, αλλά αυτό που πρέπει να ξέρετε στο σημείο αυτό είναι ότι δημιουργείτε ένα αντικείμενο που αντιπροσωπεύει ένα αρχείο με το όνομα “myfirst.tr” και το διαβιβάζετε στο ns-3. Λέτε στο ns-3 να ασχοληθεί με τα θέματα χρόνου ζωής του δημιουργούμενου αντικειμένου και επίσης να χειριστεί τα προβλήματα του δημιούργησε ένας ελάχιστα γνωστός περιορισμός της C++ για ofstream αντικείμενα που σχετίζονται με την αντιγραφή των κατασκευαστών.

Η εξωτερική κλήση προς την EnableAsciiAll(), λέει στον βοηθό ότι θέλετε να ενεργοποιήσετε την ιχνηλασία ASCII σε όλες τις point-to-point συσκευές της εξομοίωσής σας. Και θέλετε το παρεχόμενο ίχνος καταβόθρας να γράψει πληροφορίες σε μορφή ASCII σχετικά με την κίνηση πακέτων.

Για όσους είναι εξοικειωμένοι με το ns-2, τα ιχνηλατημένα γεγονότα είναι ισοδύναμα με τα δημοφιλή σημεία ίχνους που καταγράφουν “+”, “-”, “d”, και “r” γεγονότα.

Μπορείτε τώρα να χτίσετε το σενάριο και να το εκτελέσετε από τη γραμμή εντολών:

$ ./waf --run scratch/myfirst

Ακριβώς όπως έχετε ήδη δει πολλές φορές, θα δείτε κάποια μηνύματα από το Waf και στη συνέχεια το μήνυμα “‘build’ finished successfully” με κάποιο αριθμό μηνυμάτων από το πρόγραμμα εκτέλεσης.

Κατά την εκτέλεση το πρόγραμμα θα έχει δημιουργήσει ένα αρχείο με το όνομα myfirst.tr. Εξαιτίας του τρόπου με τον οποίο λειτουργεί το Waf, το αρχείο δεν έχει δημιουργηθεί στο τοπικό κατάλογο, αλλά στον προκαθορισμένο κατάλογο ανώτατου επιπέδου του αποθέματος. Αν θέλετε να ελέγχετε που αποθηκεύονται τα ίχνη, μπορείτε να χρησιμοποιήσετε την επιλογή --cwd του Waf για να το καθορίσετε. Εμείς δεν το κάναμε, έτσι πρέπει να αλλάξουμε κατάλογο και να πάμε στον αρχικό κατάλογο του αποθέματος και να ανοίξουμε το αρχείο ίχνους ASCII myfirst.tr με τον αγαπημένο σας επεξεργαστή κειμένου.

Αναλύοντας Ίχνη Ascii

Στο αρχείο αυτό υπάρχει ένα μεγάλο πλήθος πληροφοριών σε μια αρκετά πυκνή μορφή, αλλά το πρώτο πράγμα που μπορείτε να παρατηρήσετε είναι ότι υπάρχει ένας πλήθος από ξεχωριστές γραμμές. Ίσως είναι δύσκολο να το δείτε ξεκάθαρα αν δεν διευρύνει το μέγεθος του παραθύρου σας σημαντικά.

Κάθε γραμμή στο αρχείο αντιστοιχεί σε ένα ίχνος γεγονότος. Σε αυτήν την περίπτωση εντοπίζουμε τα γεγονότα ιχνηλασίας στην ουρά εκπομπής που βρίσκεται σε κάθε δικτυακή συσκευή point-to-point στην εξομοίωση. Η ουρά εκπομπής είναι μια ουρά μέσω της οποίας πρέπει να περάσει κάθε πακέτο που προορίζεται για ένα κανάλι point-to-point. Σημειώστε ότι κάθε γραμμή στο αρχείο παρακολούθησης αρχίζει με ένα μοναχικό χαρακτήρα (έχει έναν κενό χαρακτήρα αμέσως μετά). Αυτός ο χαρακτήρας έχει την ακόλουθη έννοια:

  • +: Μια λειτουργία τοποθέτησης στην ουρά συνέβη στην ουρά συσκευής;
  • -: Μια λειτουργία απομάκρυνσης από την ουρά συνέβη στην ουρά συσκευής;
  • d: Ένα πακέτο απορρίφθηκε, συνήθως επειδή η ουρά ήταν πλήρης;
  • r: Ένα πακέτο παρελήφθη από την δικτυακή συσκευή.

Ας ρίξουμε μια πιο λεπτομερή ματιά στην πρώτη γραμμή του αρχείου παρακολούθησης. Θα την τμηματοποιήσουμε (τοποθετώντας εσοχές για λόγους σαφήνειας) με αριθμός αναφοράς στην αριστερή πλευρά:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
+
2
/NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
ns3::PppHeader (
  Point-to-Point Protocol: IP (0x0021))
  ns3::Ipv4Header (
    tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
    length: 1052 10.1.1.1 > 10.1.1.2)
    ns3::UdpHeader (
      length: 1032 49153 > 9)
      Payload (size=1024)

Το πρώτο τμήμα αυτού του διευρυμένου γεγονότος ίχνους (αριθμός αναφοράς 0) είναι η λειτουργία. Έχουμε ένα χαρακτήρα +, οπότε αυτό αντιστοιχεί σε μια λειτουργία τοποθέτησης στην ουρά στην ουρά εκπομπής. Το δεύτερο τμήμα (αναφορά 1) είναι ο χρόνος εξομοίωσης που εκφράζεται σε δευτερόλεπτα. Ίσως να θυμάστε ότι ζητήσαμε από το UdpEchoClientApplication να ξεκινήσετε την αποστολή πακέτων στα δύο δευτερόλεπτα. Εδώ βλέπουμε την επιβεβαίωση ότι αυτό πράγματι συμβαίνει.

Το επόμενο τμήμα του ίχνους του παραδείγματος (αναφορά 2) μας δείχνει από ποια πηγή ίχνους προήλθε αυτό το γεγονός (εκφράζεται στο χώρο ονομάτων εντοπισμού). Μπορείτε να σκεφτείτε ότι ο χώρος ονομάτων του εντοπισμού είναι παρόμοιος με τον χώρο ονομάτων αρχείων. Η ρίζα του χώρου ονομάτων είναι η NodeList. Αυτό αντιστοιχεί σε ένα δοχείο διαχειρίζεται το | NS3 | κωδικός πυρήνα που περιέχει το σύνολο των κόμβων που είναι δημιουργήθηκε σε ένα σενάριο. Ακριβώς όπως ένα σύστημα αρχείων μπορεί να έχει καταλόγους κάτω από το ρίζα, μπορεί να έχουμε τους αριθμούς κόμβου στο NodeList. Το κορδόνι `` / NodeList / 0`` αναφέρεται, επομένως, στον κόμβο μηδενικής στην NodeList οποία συνήθως σκεφτόμαστε ως «κόμβος 0”. Σε κάθε κόμβο υπάρχει μια λίστα συσκευές που έχουν εγκατασταθεί. Αυτή η λίστα εμφανίζεται δίπλα στο χώρο ονομάτων. Μπορείτε να δείτε ότι αυτό το γεγονός ίχνος προέρχεται από DeviceList/0 η οποία είναι η συσκευή μηδενικής εγκατεστημένο στον κόμβο.

Το επόμενο αλφαριθμητικό, $ns3::PointToPointNetDevice σας λέει τι είδους συσκευή είναι στη μηδενική θέση στη λίστα συσκευών για τον κόμβο μηδέν. Θυμηθείτε ότι η λειτουργία + στην αναφορά 00 σημαίνει ότι μια λειτουργία τοποθέτησης στην ουρά συνέβη στην ουρά μεταδόσεως της συσκευής. Αυτό αντικατοπτρίζεται στα τελικά τμήματα της “διαδρομής ίχνους” τα οποίο είναι TxQueue/Enqueue.

Τα υπόλοιπα τμήματα στο ίχνος πρέπει να είναι αρκετά έξυπνα. Οι αναφορές 3-4 υποδεικνύουν ότι το πακέτο είναι εμφωλιασμένο στο πρωτόκολλο point-to-point. Οι αναφορές 5-7 δείχνουν ότι το πακέτο έχει μια επικεφαλίδα IPv4 και προήλθε από τη διεύθυνση IP 10.1.1.1 και έχει προορισμό την 10.1.1.2. Οι αναφορές 8-9 δείχνουν ότι αυτό το πακέτο έχει μια επικεφαλίδα UDP και, τέλος, η αναφορά 10 δείχνει ότι το ωφέλιμο φορτίο είναι τα αναμενόμενα 1024 bytes.

Η επόμενη γραμμή στο αρχείο ίχνος δείχνει το ίδιο πακέτο που απομακρύνεται από την ουρά μετάδοσης στον ίδιο κόμβο.

Η τρίτη γραμμή στο αρχείο ίχνος δείχνει το πακέτο που λήφθηκε από τη δικτυακή συσκευή μέσω της αντήχησης του εξυπηρετητή. Αναπαράγουμε αυτό το συμβάν παρακάτω.

1
2
3
4
5
6
7
8
9
r
2.25732
/NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
  ns3::Ipv4Header (
    tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
    length: 1052 10.1.1.1 > 10.1.1.2)
    ns3::UdpHeader (
      length: 1032 49153 > 9)
      Payload (size=1024)

Παρατηρήστε ότι η λειτουργία ανίχνευσης είναι πλέον r και ο χρόνος εξομοίωσης έχει αυξηθεί σε 2.25732 δευτερόλεπτα. Αν έχετε ακολουθήσει τα βήματα του οδηγού προσεκτικά, αυτό σημαίνει ότι έχετε αφήσει το DataRate των δικτυακών συσκευών και το κανάλι Delay στις προεπιλεγμένες τιμές τους. Αυτή τη φορά θα πρέπει να είστε εξοικειωμένοι μια που το έχετε ξαναδεί σε προηγούμενη ενότητα.

Η είσοδος χώρος ονομάτων της πηγής ίχνους (αναφορά 02) έχει αλλάξει για να επισημάνει ότι το γεγονός έρχεται από τον κόμβο 1 (/NodeList/1) και η λήψη πακέτου της πηγή ίχνους (/MacRx). Θα πρέπει να είναι αρκετά εύκολο για σας να ακολουθήσετε την πρόοδο του πακέτου μέσω της τοπολογίας κοιτάζοντας τα υπόλοιπα ίχνη του αρχείου.

Οι βοηθοί συσκευών ns-3 μπορούν επίσης να χρησιμοποιηθούν για τη δημιουργία αρχείων ίχνους σε μορφή .pcap. Το αρκτικόλεξο pcap αντιστοιχεί στη σύλληψη πακέτων (packet capture) και συνήθως γράφεται με μικρά γράμματα. Είναι στην πραγματικότητα μια διεπαφή προγράμματος που περιλαμβάνει τον ορισμό του είδους αρχείου .pcap. Το πιο δημοφιλές πρόγραμμα που μπορεί να εμφανίσει αυτό το είδος αρχείου είναι το Wireshark (παλαιότερα ονομαζόταν Ethereal). Ωστόσο, υπάρχουν πολλοί αναλυτές ίχνους κίνησης που χρησιμοποιούν αυτή τη μορφή πακέτων. Ενθαρρύνουμε τους χρήστες να εκμεταλλευτούν τα πολλά διαθέσιμα εργαλεία για την ανάλυση ιχνών pcap. Σε αυτόν τον οδηγό, θα επικεντρωθούμε στην προβολή ιχνών pcap με το tcpdump.

Ο κωδικός που χρησιμοποιούμε για να ενεργοποιήσουμε την ιχνηλασία pcap είναι μιας γραμμής.

pointToPoint.EnablePcapAll ("myfirst");

Εισάγετε αυτή τη γραμμή του κώδικα μετά τον κωδικό ιχνηλασίας ASCII που μόλις προσθέσατε στο scratch/myfirst.cc. Παρατηρήστε ότι έχουμε περάσει μόνο το αλφαριθμητικό “myfirst,” και όχι “myfirst.pcap” ή κάτι παρόμοιο. Αυτό συμβαίνει επειδή η παράμετρος είναι ένα πρόθεμα, δεν είναι ένα πλήρες όνομα του αρχείου. Ο βοηθός στην ουσία θα δημιουργήσει ένα αρχείο ίχνους για κάθε συσκευή point-to-point στην εξομοίωση. Τα ονόματα των αρχείων θα χτιστούν χρησιμοποιώντας το πρόθεμα, τον αριθμό κόμβου, τον αριθμό της συσκευής και μια κατάληξη ”.pcap”.

Στο σενάριο του παραδείγματός μας, θα δούμε τελικά αρχεία με όνομα “myfirst-0-0.pcap” και “myfirst-1-0.pcap” που είναι τα ίχνη pcap για τον κόμβο 0-συσκευή 0 και κόμβο 1-συσκευή 0, αντίστοιχα.

Μόλις έχετε προσθέσει τη γραμμή του κώδικα για να ενεργοποιήσετε την ιχνηλασία pcap, μπορείτε να εκτελέσετε το σενάριο με το συνήθη τρόπο:

$ ./waf --run scratch/myfirst

Αν κοιτάξετε στον κατάλογο κορυφής της διανομής σας, θα πρέπει τώρα να βλέπετε τρία αρχεία καταγραφής: myfirst.tr είναι το αρχείο ίχνους ASCII που έχουμε εξετάσει προηγουμένως. Τα myfirst-0-0.pcap και myfirst-1-0.pcap είναι τα νέα αρχεία pcap που μόλις δημιουργήσαμε.

Ανάγνωση εξόδου με tcpdump

Το πιο εύκολο βήμα που μπορούμε κάνουμε σε αυτό το σημείο θα είναι να χρησιμοποιήσουμε το tcpdump να δούμε τα pcap αρχεία.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
reading from file myfirst-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024

tcpdump -nn -tt -r myfirst-1-0.pcap
reading from file myfirst-1-0.pcap, link-type PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024

Μπορείτε να δείτε στο dump του αρχείου myfirst-0-0.pcap (η συσκευή του πελάτη) ότι η το πακέτο αντανάκλασης στέλνεται στα 2 δευτερόλεπτα στην εξομοίωση. Αν κοιτάξετε το δεύτερο dump (myfirst-1-0.pcap) μπορείτε να δείτε ότι το πακέτο λαμβάνεται σε 2.257324 δευτερόλεπτα. Μπορείτε να δείτε το πακέτο που αντανακλάται πίσω σε 2.257324 δευτερόλεπτα στο δεύτερο dump, και, τέλος, μπορείτε να δείτε το πακέτο που παραλαμβάνεται πίσω στον πελάτη στο πρώτο dump σε 2.514648 δευτερόλεπτα.

Ανάγνωση εξόδου με το Wireshark

Εάν δεν είστε εξοικειωμένοι με το Wireshark, υπάρχει μια ιστοσελίδα από την οποία μπορείτε να κατεβάσετε τα προγράμματα και την τεκμηρίωση: http://www.wireshark.org/.

Το Wireshark είναι ένα γραφικό περιβάλλον χρήστη, το οποίο μπορεί να χρησιμοποιηθεί για την εμφάνιση αυτών των αρχείων ίχνους. Εάν έχετε διαθέσιμο το Wireshark, μπορείτε να ανοίξετε κάθε αρχείο ίχνους και να εμφανίσετε τα περιεχόμενά του σαν να είχαν συλληφθεί τα πακέτα χρησιμοποιώντας έναν οσφρηστή πακέτων (packet sniffer).