Tutorial zum ns-3 Netzwerk Simulator. Kapitel 5

Tutorial zum ns-3 Netzwerk Simulator. Kapitel 5
Kapitel 1,2
Kapitel 3
Kapitel 4

5 Konfiguration
5.1 Verwendung des Protokollierungsmoduls
5.1.1 Übersicht über das Protokollieren
5.1.2 Aktivierung der Protokollierung
5.1.3 Hinzufügen der Protokollierung zu Ihrem Code
5.2 Verwendung von Befehlszeilenargumenten
5.2.1 Überschreiben von Standardattributwerten
5.2.2 Erfassen Ihrer eigenen Befehle
5.3 Verwendung des Nachverfolgungssystems
5.3.1 ASCII-Nachverfolgung
Parsen von ASCII-Nachverfolgungen
5.3.2 PCAP-Nachverfolgung

Kapitel 5

Einstellungen

5.1 Verwendung des Protokollierungsmoduls

Wir haben bereits kurz das Protokollierungsmodul ns-3 betrachtet, während wir das Skript durchgegangen sind first.cc. In diesem Kapitel werden wir uns eingehender mit den Anwendungsmöglichkeiten des Protokollierungsuntermoduls befassen.

5.1.1 Übersicht über das Protokollieren

Viele große Systeme unterstützen irgendein Mittel zur Protokollierung von Nachrichten, und ns-3 ist da keine Ausnahme. In einigen Fällen werden nur Fehlermeldungen in die "Operator-Konsole" (die normalerweise stderr in Unix-basierten Systemen ist) geschrieben. In anderen Systemen können auch Warnmeldungen und detailliertere Informationen ausgegeben werden. In manchen Fällen werden Protokollierungswerkzeuge verwendet, um Debugging-Nachrichten auszugeben, die die Ausgabe schnell "vernebeln" können.

Der in ns-3 verwendete Ansatz geht davon aus, dass all diese Informationsstufen nützlich sind, und wir bieten einen selektiven, mehrstufigen Ansatz zur Protokollierung von Nachrichten. Die Protokollierung kann vollständig deaktiviert oder für einzelne Komponenten oder im globalen Maßstab aktiviert werden. Hierfür gibt es anpassbare Informationsstufen. Das Protokollierungsmodul ns-3 bietet einen relativ einfachen Weg, um nützliche Informationen aus Ihrer Simulation zu erhalten.

Sie sollten verstehen, dass wir einen allgemeinen Mechanismus - die Nachverfolgung - bereitstellen, um Daten aus Ihren Modellen zu extrahieren, der bevorzugt für die Ausgabe während der Modellierung verwendet werden sollte (für weitere Informationen zu unserem Nachverfolgungssystem siehe Abschnitt 5.3 des Handbuchs). Die Protokollierung sollte das bevorzugte Mittel zur Gewinnung von Debugging-Informationen, Warnungen, Fehlermeldungen oder zum schnellen Ausgeben von Nachrichten aus Ihren Skripten oder Modellen zu jedem Zeitpunkt sein.

Derzeit sind im System sieben Stufen (Typen) von Protokollnachrichten in aufsteigender Informationsdichte definiert.

  • LOG_ERROR — Protokollierung von Fehlermeldungen (verbundener Makro: NS_LOG_ERROR);
  • LOG_WARN — Registrierung von Warnmeldungen (verwandter Makro: NS_LOG_WARN);
  • LOG_DEBUG — Registrierung relativ seltener spezieller Debugmeldungen (verwandter Makro: NS_LOG_DEBUG);
  • LOG_INFO — Registrierung von Informationsmeldungen über den Programmablauf (verwandter Makro: NS_LOG_INFO);
  • LOG_FUNCTION — Registrierung von Meldungen, die jede aufgerufene Funktion beschreiben (zwei verwandte Makros: NS_LOG_FUNCTION, verwendet für Memberfunktionen, und NS_LOG_FUNCTION_NOARGS, verwendet für statische Funktionen);
  • LOG_LOGIC — Registrierung von Meldungen, die den logischen Ablauf innerhalb einer Funktion beschreiben (verwandter Makro: NS_LOG_LOGIC);
  • LOG_ALL — Registrierung von allem oben genannten (kein verwandter Makro).
    Für jeden Typ (LOG_TYPE) gibt es auch den Typ LOG_LEVEL_TYPE, der, wenn er verwendet wird, zusätzlich zu seiner eigenen Ebene alle übergeordneten Ebenen registrieren kann. (Daraus folgt, dass LOG_ERROR und LOG_LEVEL_ERROR sowie LOG_ALL und LOG_LEVEL_ALL funktional äquivalent sind.) Zum Beispiel erlaubt die Aktivierung von LOG_INFO nur die von dem Makro NS_LOG_INFO bereitgestellten Meldungen, während bei Aktivierung von LOG_LEVEL_INFO auch Meldungen von den Makros NS_LOG_DEBUG, NS_LOG_WARN und NS_LOG_ERROR einbezogen werden.

Wir bieten auch ein bedingungsloses Logging-Makro an, das immer angezeigt wird, unabhängig von der Protokollierungsebene oder Komponentenwahl.

  • NS_LOG_UNCOND — bedingungslose Registrierung der zugehörigen Meldung (ohne zugehörige Protokollierungsebene).

Jede Ebene kann einzeln oder kumulativ angefordert werden. Die Protokollierung kann über die Umgebungsvariable sh-NW NS_LOG oder durch Registrierung eines Aufrufs der Systemfunktion konfiguriert werden. Wie zuvor gezeigt, verfügt das Protokollierungssystem über eine Doxygen-Dokumentation, und jetzt ist der richtige Zeitpunkt, sie zu überprüfen, falls Sie dies noch nicht getan haben.

Jetzt, da Sie die Dokumentation sehr gründlich gelesen haben, lassen Sie uns dieses Wissen nutzen, um interessante Informationen aus dem Beispielskript zu gewinnen. scratch/myfirst.cc, das Sie bereits kompiliert haben.

5.1.2 Aktivierung der Protokollierung

Lassen Sie uns die NS_LOG-Umgebungsvariable verwenden, um einige weitere Protokolle zu starten, aber zuerst, nur um sich zu orientieren, führen Sie das letzte Skript aus, wie Sie es zuvor gemacht haben,

$ ./waf --run scratch/myfirst

Sie sollten die bereits vertraute Ausgabe des ersten Beispielprogramms ns-3 sehen.

$ Waf: Betritt Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Verlasse Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build` 'build'
Erfolgreich beendet (0.413s)
1024 Bytes an 10.1.1.2 gesendet
1024 Bytes von 10.1.1.1 empfangen
1024 Bytes von 10.1.1.2 empfangen

Es stellt sich heraus, dass die "gesendeten" und "empfangenen" Nachrichten, die Sie oben sehen, tatsächlich registrierte Nachrichten von UdpEchoClientApplication und UdpEchoServerApplication. Zum Beispiel können wir das Client-Anwendungsprotokoll anfragen, indem wir den Protokollierungsgrad über die Umgebungsvariable NS_LOG einstellen.

Ab diesem Moment nehme ich an, dass Sie eine sh-ähnliche Shell verwenden, die die Syntax "VARIABLE = value" verwendet. Wenn Sie eine csh-ähnliche Shell verwenden, müssen Sie meine Beispiele in die Syntax "setenv VARIABLE value" umwandeln, die von diesen Shells benötigt wird.

Im Moment reagiert die UDP-Echo-Client-Anwendung auf die folgende Zeile im scratch/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

Dies aktiviert den Protokollierungsgrad LOG_LEVEL_INFO. Wenn wir einen Protokollierungsgrad-Flag übergeben, aktivieren wir tatsächlich diesen Grad und alle niedrigeren Grade. In diesem Fall haben wir NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN und NS_LOG_ERROR aktiviert. Wir können den Protokollierungsgrad erhöhen und mehr Informationen erhalten, ohne das Skript zu ändern und neu zu kompilieren, indem wir die Umgebungsvariable NS_LOG wie folgt setzen:

$ export NS_LOG=UdpEchoClientApplication=level_all

So setzen wir den folgenden Wert der NS_LOG-Variable in der sh-Shell fest,

UdpEchoClientApplication=level_all

Die linke Seite der Zuweisung ist der Name des zu protokollierenden Komponenten, den wir anpassen möchten, und die rechte Seite ist das Flag, das wir dafür anwenden möchten. In diesem Fall werden wir alle Debugging-Stufen in der Anwendung aktivieren. Wenn Sie das Skript mit so festgelegtem NS_LOG ausführen, wird die ns-3-Protokollierungsanwendung die Änderungen übernehmen und Sie sollten die folgende Ausgabe sehen:

Waf: Betritt Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Verlasse Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' erfolgreich beendet (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
1024 Bytes an 10.1.1.2 gesendet
1024 Bytes von 10.1.1.1 empfangen
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
1024 Bytes von 10.1.1.2 empfangen
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Zusätzliche Debugging-Informationen, die von der Anwendung bereitgestellt werden, entsprechen jetzt dem Niveau NS_LOG_FUNCTION. Es zeigt jeden Fall des Funktionsaufrufs während der Ausführung des Skripts. In der Regel sollten in den Methodenfunktionen (mindestens) die folgenden verwendet werden.NS_LOG_FUNCTION (this). Verwenden Sie NS_LOG_FUNCTION_NOARGS ()
nur in statischen Funktionen. Beachten Sie jedoch, dass im ns-3-System keine Anforderungen zur Unterstützung von Logging-Funktionalität existieren. Die Entscheidung, wie viele Informationen protokolliert werden, liegt im Ermessen des Modellentwicklers. Bei Echo-Anwendungen stehen umfangreiche Ausgaben für das Protokollieren zur Verfügung.

Jetzt können Sie das Protokoll der Funktionsaufrufe einsehen, die von der Anwendung gemacht wurden. Wenn Sie genau hinschauen, werden Sie einen Doppelpunkt zwischen der Zeile UdpEchoClientApplication und dem Methodennamen sehen, wo Sie möglicherweise den C++-Gültigkeitsoperator erwartet hätten (::). Dies wurde absichtlich so gestaltet.

Tatsächlich handelt es sich nicht um den Klassennamen, sondern um den Namen des Log-Komponenten. Wenn eine Übereinstimmung zwischen der Quell-Datei und der Klasse besteht, ist dies normalerweise der Klassenname, aber Sie müssen verstehen, dass es sich tatsächlich nicht um den Klassennamen handelt und dort ein Doppelpunkt anstelle eines doppelten Doppelpunkts steht. Dies ist eine Möglichkeit, Ihnen zu helfen, konzeptionell den Log-Komponenten-Namen vom Klassennamen zu trennen.

Dennoch kann es in bestimmten Fällen schwierig sein zu bestimmen, welche Methode tatsächlich die Log-Nachricht erzeugt. Wenn Sie sich den obigen Text ansehen, werden Sie sich fragen, woher die Zeile "Received 1024 bytes from 10.1.1.2" stammt. Sie können dieses Problem lösen, indem Sie die Ebene prefix_func in der Umweltvariable NS_LOG setzen. Versuchen Sie Folgendes:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Beachten Sie, dass die Anführungszeichen notwendig sind, da das vertikale Strichsymbol, das wir zur Kennzeichnung der ODER-Operation verwenden, auch ein Pipeline-Verbindungszeichen in Unix ist. Jetzt, wenn Sie das Skript ausführen, werden Sie sehen, dass das Logging-System garantiert, dass jede Nachricht aus diesem Protokoll ein Präfix mit dem Namen der Komponente hat.

Waf: Verzeichnis betreten `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Verzeichnis verlassen `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' wurde erfolgreich abgeschlossen (0.417s)\nUdpEchoClientApplication:UdpEchoClient()\nUdpEchoClientApplication:SetDataSize(1024)\nUdpEchoClientApplication:StartApplication()\nUdpEchoClientApplication:ScheduleTransmit()\nUdpEchoClientApplication:Send()\nUdpEchoClientApplication:Send(): 1024 Bytes an 10.1.1.2 gesendet\n1024 Bytes von 10.1.1.1 empfangen\nUdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)\nUdpEchoClientApplication:HandleRead(): 1024 Bytes von 10.1.1.2 empfangen\nUdpEchoClientApplication:StopApplication()\nUdpEchoClientApplication:DoDispose()\nUdpEchoClientApplication:~UdpEchoClient()

Jetzt können Sie sehen, dass alle Nachrichten, die vom UDP-Echo-Client-Anwendung empfangen werden, als solche identifiziert werden. Die Nachricht „Received 1024 bytes from 10.1.1.2» ist jetzt eindeutig als von der Echo-Client-Anwendung kommend definiert. Die verbleibende Nachricht sollte aus der UDP-Echo-Server-Anwendung stammen. Wir können diese Komponente aktivieren, indem wir eine Liste der durch Doppelpunkt getrennten Komponenten in der Umgebungsvariablen NS_LOG eingeben.

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

Warnung: Im obigen Textbeispiel müssen Sie das Zeilenumbruchzeichen nach dem Doppelpunkt (:) entfernen, es wird zur Formatierung des Dokuments verwendet. Wenn Sie jetzt das Skript ausführen, werden Sie alle Protokollmeldungen von der Client- und Server-Echo-Anwendung sehen. Sie werden feststellen, dass dies beim Debuggen sehr nützlich sein kann.

Waf: Verzeichnis betreten `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Verzeichnis verlassen `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' wurde erfolgreich abgeschlossen (0.406s)\nUdpEchoServerApplication:UdpEchoServer()\nUdpEchoClientApplication:UdpEchoClient()\nUdpEchoClientApplication:SetDataSize(1024)\nUdpEchoServerApplication:StartApplication()\nUdpEchoClientApplication:StartApplication()\nUdpEchoClientApplication:ScheduleTransmit()\nUdpEchoClientApplication:Send()\nUdpEchoClientApplication:Send(): 1024 Bytes an 10.1.1.2 gesendet\nUdpEchoServerApplication:HandleRead(): 1024 Bytes von 10.1.1.1 empfangen\nUdpEchoServerApplication:HandleRead(): Paket wird wiederholt\nUdpEchoClientApplication:HandleRead(0x624920, 0x625160)\nUdpEchoClientApplication:HandleRead(): 1024 Bytes von 10.1.1.2 empfangen\nUdpEchoServerApplication:StopApplication()\nUdpEchoClientApplication:StopApplication()\nUdpEchoClientApplication:DoDispose()\nUdpEchoServerApplication:DoDispose()\nUdpEchoClientApplication:~UdpEchoClient()\nUdpEchoServerApplication:~UdpEchoServer()

Es ist auch manchmal nützlich, die Simulationszeit zu sehen, zu der die Protokollnachricht erstellt wurde. Sie können dies tun, indem Sie das Prädikat hinzufügen oder prefix_time:

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

Wieder müssen Sie oben das Zeilenumbruchzeichen entfernen. Wenn Sie jetzt das Skript ausführen, sollten Sie die folgende Ausgabe sehen:

Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' betreten
Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' verlassen
'build' erfolgreich abgeschlossen (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(): 1024 Bytes an 10.1.1.2 gesendet
2.00369s UdpEchoServerApplication:HandleRead(): 1024 Bytes von 10.1.1.1 empfangen
2.00369s UdpEchoServerApplication:HandleRead(): Paket wird zurückgeschickt
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): 1024 Bytes von 10.1.1.2 empfangen
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Bitte beachten Sie, dass der Konstruktor für UdpEchoServer während der Simulation bei 0 Sekunden aufgerufen wurde. Das geschieht tatsächlich vor dem Beginn der Simulation, aber diese Zeit wird als null Sekunden angezeigt. Dasselbe gilt für die Meldung des Konstruktors UdpEchoClient.

Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' betreten
Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' verlassen
'build' erfolgreich abgeschlossen (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(): 1024 Bytes an 10.1.1.2 gesendet
2.00369s UdpEchoServerApplication:HandleRead(): 1024 Bytes von 10.1.1.1 empfangen
2.00369s UdpEchoServerApplication:HandleRead(): Paket wird zurückgeschickt
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): 1024 Bytes von 10.1.1.2 empfangen
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Erinnern wir uns daran, dass das Skript scratch\/first.cc die Echo-Serveranwendung eine Sekunde vor Beginn der Simulation gestartet hat. Jetzt können Sie sehen, dass die Methode StartApplication des Servers tatsächlich in der ersten Sekunde aufgerufen wird. Sie können auch bemerken, dass der Echo-Client in der zweiten Sekunde der Simulation gestartet wird, wie wir es im Skript angefordert haben.

Jetzt können Sie den Verlauf der Simulation verfolgen, indem Sie den Aufruf ScheduleTransmit im Client sehen, der den Sendebefehl HandleRead in der Echo-Serveranwendung auslöst. Beachten Sie, dass die verstrichene Zeit zum Senden des Pakets über die Punkt-zu-Punkt-Verbindung 3,69 Millisekunden beträgt. Deutlich ist, dass der Echo-Server die Meldung registriert, dass er auf das Paket geantwortet hat, und dann, nach einer Verzögerung des Kanals, sehen Sie, dass der Echo-Client das Echo-Paket in seiner Methode HandleRead erhält.

In dieser Simulation findet vieles unbemerkt von Ihnen statt. Aber Sie können den gesamten Prozess ganz einfach verfolgen, indem Sie alle Logging-Komponenten im System aktivieren. Versuchen Sie, den folgenden Wert in der Variablen NS_LOG festzulegen,

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

Das Sternchen oben ist ein Platzhalter für die Logging-Komponente. Dies aktiviert alle Protokolle in allen Komponenten, die in der Simulation verwendet werden. Ich werde die Ausgabe hier nicht reproduzieren (zum Zeitpunkt des Schreibens erzeugt es 1265 Zeilen Ausgabe für ein Echo-Paket), aber Sie können diese Informationen in eine Datei umleiten und sie in Ihrem bevorzugten Editor ansehen.

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

Ich persönlich benutze diese äußerst ausführliche Version des Protokollierens, wenn ich ein Problem habe und keine Ahnung habe, wo es schiefgegangen ist. Ich kann den Code recht einfach verfolgen, ohne Haltepunkte zu setzen und Schritt für Schritt im Debugger zu arbeiten. Ich kann einfach die Ausgabe in meinem bevorzugten Editor bearbeiten und nach dem suchen, was ich erwarte, und sehen, dass etwas Unerwartetes passiert. Wenn ich eine allgemeine Vorstellung davon habe, was schiefgeht, gehe ich zum Debugger über, um das Problem genauer zu untersuchen. Diese Art der Ausgabe kann besonders nützlich sein, wenn Ihr Skript etwas völlig Unerwartetes macht. Wenn Sie nur den Debugger verwenden, können Sie unerwartete Wendungen völlig übersehen. Das Protokollieren macht solche Wendungen offensichtlich.

5.1.3 Hinzufügen der Protokollierung zu Ihrem Code

Sie können neue Einträge in Ihre Simulationen hinzufügen, indem Sie den Protokollkomponenten-Log aus mehreren Makros aufrufen. Lassen Sie uns dies im Skript tun, myfirst.cc, das wir im "sauberen" Verzeichnis haben. Erinnern wir uns, dass wir in diesem Skript die Protokollierungskomponente definiert haben:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Ihnen ist bekannt, dass Sie das Protokollieren aller Nachrichten dieser Komponente aktivieren können, indem Sie die Umgebungsvariable NS_LOG auf verschiedenen Ebenen einstellen. Lassen Sie uns fortfahren und einige Einträge in das Skript hinzufügen. Das Makro, das zum Protokollieren von Nachrichten der Informationsstufe verwendet wird, ist NS_LOG_INFO. Lassen Sie uns eine Nachricht hinzufügen (gleich bevor wir die Knoten erstellen), die Ihnen sagt, dass das Skript sich im Prozess des Topologieaufbaus befindet ("Creating Topology"). Dies geschieht im folgenden Codefragment,
Öffnen Sie scratch/myfirst.cc in Ihrem bevorzugten Editor und fügen Sie die Zeile hinzu,
NS_LOG_INFO ("Creating Topology");
direkt vor den Zeilen,

NodeContainer nodes;
nodes.Create(2);

Kompilieren Sie nun das Skript mit waf, und setzen Sie die NS_LOG-Variable zurück, um den Protokollierungsstream, den wir zuvor aktiviert haben, zu deaktivieren:

$ ./waf
$ export NS_LOG=
Jetzt, wenn Sie das Skript ausführen,

$ ./waf --run scratch/myfirst FirstScriptExample werden Sie die neue Nachricht nicht sehen, da die zugehörige Protokollierungskomponente (FirstScriptExample) nicht aktiviert wurde. Um Ihre Nachricht zu sehen, müssen Sie die Protokollierungskomponente aktivieren

mit einem Niveau von mindestens NS_LOG_INFO. Wenn Sie nur diesen spezifischen Protokollierungsgrad sehen möchten, können Sie ihn so aktivieren,\n$ export NS_LOG=FirstScriptExample=info

Wenn Sie das Skript jetzt ausführen, sehen Sie die neue Nachricht „Topology erstellen“

Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' betreten
Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' verlassen
'build' erfolgreich abgeschlossen (0.404s)
Topology erstellen
1024 Bytes an 10.1.1.2 gesendet
1024 Bytes von 10.1.1.1 erhalten
1024 Bytes von 10.1.1.2 erhalten

5.2 Verwendung von Befehlszeilenargumenten

5.2.1 Überschreiben von Standardattributwerten

Eine andere Möglichkeit, das Verhalten von ns‑3-Skripten zu ändern, ohne sie zu bearbeiten und erneut zu kompilieren, besteht darin, Befehlszeilenargumente zu verwenden. Wir bieten einen Mechanismus zum Parsen von Befehlszeilenargumenten und zur automatischen Einstellung lokaler und globaler Variablen basierend auf den Ergebnissen.

Der erste Schritt bei der Verwendung des Befehlszeilenargument-Systems besteht darin, einen Parser für die Befehlszeile zu deklarieren. Dies ist recht einfach zu machen (in Ihrem Hauptprogramm), wie im folgenden Code:

int
main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv);
...
}

Dieses einfache zweizeilige Fragment ist tatsächlich sehr nützlich. Es öffnet die Tür zu globalen ns‑3-Variablen und zum Attributsystem. Lassen Sie uns zwei Zeilen Code zu Beginn der Hauptfunktion des Skripts hinzufügen. scratch/myfirst.cc. Fahren wir fort, kompilieren das Skript und führen es aus, wobei wir beim Ausführen wie folgt um Hilfe bitten:

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

Dieser Befehl fordert dazu auf, Waf das Skript scratch\/myfirst auszuführen und ihm das Befehlszeilenargument --PrintHelp. Die Anführungszeichen sind notwendig, um zu zeigen, für welches Programm das Argument gedacht ist. Der Befehlszeilenparser erkennt das Argument --PrintHelp und gibt eine Antwort aus,

Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' betreten
Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' verlassen
'build' erfolgreich abgeschlossen (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--PrintHelp: Diese Hilfemeldung ausgeben.
--PrintGroups: Die Liste der Gruppen ausgeben.
--PrintTypeIds: Alle TypeIds ausgeben.
--PrintGroup=[group]: Alle TypeIds der Gruppe ausgeben.
--PrintAttributes=[typeid]: Alle Attribute des typeid ausgeben.
--PrintGlobals: Die Liste der Globals ausgeben.

Nun betrachten wir die Option --PrintAttributes. Wir haben bereits das ns‑3-Attributsystem erwähnt, als wir das Skript first.cc untersucht haben. Wir haben die folgenden Codezeilen gesehen,

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

und gesagt, dass DataRate es sich tatsächlich um ein Attribut handelt. PointToPointNetDeviceLassen Sie uns den Befehlszeilenparser verwenden, um die Attribute anzuzeigen. PointToPointNetDeviceDie Hilfeliste sagt uns, dass wir TypeIdangeben müssen. Dies ist der Name der Klasse, zu der die interessierenden Attribute gehören. In unserem Fall wird dies ns3::PointToPointNetDevice. Lassen Sie uns weiter voranschreiten, geben Sie ein,

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

Das System wird alle Attribute dieses Netzwerkgerättyps ausdrucken. Sie werden sehen, dass unter den Attributen in der Liste vorhanden ist,

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
Die Standarddatenrate für Punkt-zu-Punkt-Verbindungen

Dieser Wert wird standardmäßig in das System übernommen, wenn ein Objekt erstellt wird. PointToPointNetDevice. Wir werden diesen Standardwert mit dem Parameter Attribut in PointToPointHelper oben verwenden. Lassen Sie uns die Standardwerte für Punkt-zu-Punkt-Geräte und -Kanäle nutzen. Dazu entfernen wir die Aufrufe von SetDeviceAttribute und SetChannelAttribute aus myfirst.cc, die wir im leeren Verzeichnis haben.

Ihr Skript sollte jetzt einfach deklarieren PointToPointHelper und keine Installationsoperationen ausführen, wie im folgenden Beispiel gezeigt,

...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...

Fahren Sie fort und erstellen Sie ein neues Skript mit Waf (./waf) und lassen Sie uns zurückkehren und einige Protokolle aus der UDP-Echo-Anwendung des Servers aktivieren und den Zeitpräfix hinzufügen.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

Wenn Sie das Skript ausführen, sollten Sie die folgende Ausgabe sehen:

Waf: Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build' betreten
Waf: Verlasse das Verzeichnis `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' erfolgreich abgeschlossen (0.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
1024 Bytes gesendet an 10.1.1.2
2.25732s 1024 Bytes empfangen von 10.1.1.1
2.25732s Paket wird zurückgesendet
1024 Bytes empfangen von 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Erinnern wir uns, dass beim letzten Mal, als wir die Simulationszeit betrachteten, der Zeitpunkt, zu dem das Echo-Server-Paket empfangen wurde, bei 2,00369 Sekunden lag.

2.00369s UdpEchoServerApplication:HandleRead(): 1024 Bytes empfangen von 10.1.1.1

Jetzt erhält es das Paket bei 2.25732 Sekunden. Das liegt daran, dass wir die Datenübertragungsrate des PointToPointNetDevice von fünf Megabit pro Sekunde auf den Standardwert von 32768 Bit pro Sekunde zurückgesetzt haben. Wenn wir die neue DataRate über die Befehlszeile eingebracht hätten, könnten wir unsere Simulation wieder beschleunigen. Wir werden dies wie folgt tun, gemäß der in der Hilfeklasse implizierten Formel:

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

Daher wird der Standardwert des Attributs DataRate auf fünf Megabit pro Sekunde zurückgesetzt. Sind Sie überrascht von dem Ergebnis? Es stellt sich heraus, dass wir auch die Kanalverzögerung entsprechend der Lichtgeschwindigkeit setzen müssen, um das ursprüngliche Verhalten des Skripts wiederherzustellen. Wir können das System der Befehlszeile auffordern, die Kanalattribute auszugeben, genau wie wir es für das Netzwerkgerät getan haben:

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

Wir stellen fest, dass das Attribut der Kanalverzögerung wie folgt gesetzt ist:

--ns3::PointToPointChannel::Delay=[0ns]:
Übertragungsverzögerung durch den Kanal

Dann können wir über das System der Befehlszeile beide diese Standardwerte setzen,

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

In diesem Fall stellen wir die Zeit wieder her, die wir hatten, als wir DataRate und Delay im Skript explizit gesetzt haben:

Waf: Betreten des Verzeichnisses `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Verlassen des Verzeichnisses `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'bauen' erfolgreich abgeschlossen (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
1024 Bytes an 10.1.1.2 gesendet
2.00369s 1024 Bytes von 10.1.1.1 empfangen
2.00369s Paket wird zurückgegeben
1024 Bytes von 10.1.1.2 empfangen
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Bitte beachten Sie, dass das Paket nach 2,00369 Sekunden erneut vom Server empfangen wurde. Tatsächlich könnten wir auf diese Weise jeden der im Skript verwendeten Attribute setzen. Insbesondere könnten wir Werte für die Attribute MaxPackets festlegen, die von eins abweichen. UdpEchoClient.

Wie würden Sie das verwenden? Probieren Sie es aus. Denken Sie daran, dass Sie den Ort, an dem wir den Standardwert des Attributs überschreiben und ausdrücklich setzen, kommentieren müssen MaxPackets im Skript. Danach sollten Sie das Skript neu kompilieren. Sie können auch über die Befehlszeile Hilfe zur Syntax des Setzens eines neuen Standardwertes des Attributs erhalten. Wenn Sie das herausgefunden haben, können Sie die Anzahl der in der Befehlszeile angezeigten Pakete steuern. Da wir pflichtbewusste Menschen sind, sollte unsere Befehlszeile etwa so aussehen:

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

Die naheliegende Frage, die sich an dieser Stelle stellt, ist, wie wir über das Vorhandensein all dieser Attribute Bescheid wissen. Wiederum bietet das System der Befehlszeile in dieser Hinsicht eine Hilfefunktion. Wenn wir Hilfe über die Befehlszeile anfordern, sollten wir sehen:

$ .\/waf --run "scratch\/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Liste der Globals drucken.
--PrintGroups: Liste der Gruppen drucken.
--PrintGroup=[group]: Alle TypeIds der Gruppe drucken.
--PrintTypeIds: Alle TypeIds drucken.
--PrintAttributes=[typeid]: Alle Attribute des typeid drucken.
--PrintHelp: Diese Hilfenachricht drucken.

Wenn Sie das Argument „PrintGroups“ wählen, sollten Sie eine Liste aller registrierten Gruppen sehen. TypeId. Die Gruppennamen stimmen mit den Modulnamen im Quellverzeichnis überein (obwohl sie mit einem Großbuchstaben beginnen). Das Drucken aller Informationen auf einmal wäre zu umfangreich, daher steht ein zusätzlicher Filter zur Verfügung, um Informationen nach Gruppen zu drucken. Konzentrieren wir uns erneut auf das Modul „Punkt-zu-Punkt“:

.\/waf --run "scratch\/myfirst --PrintGroup=PointToPoint"
TypeIds in der Gruppe PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

Hier finden Sie verfügbare TypeId-Namen zur Suche nach Attributen, zum Beispiel in
--PrintAttributes = ns3 :: PointToPointChannel, wie oben gezeigt.

Eine weitere Möglichkeit, mehr über die Attribute zu erfahren, ist über Doxygen ns-3. Dort gibt es eine Seite, die alle im Simulator registrierten Attribute auflistet.

5.2.2 Erfassen Ihrer eigenen Befehle

Sie können auch über das Befehlszeilensystem Ihre eigenen Hooks hinzufügen. Dies ist ziemlich einfach mit der Methode des Befehlszeilenparsers. AddValue.
Lassen Sie uns diese Möglichkeit nutzen, um die Anzahl der Pakete, die angezeigt werden sollen, auf eine ganz andere Weise anzugeben. Lassen Sie uns eine lokale Variable mit dem Namen nPackets in die Funktion main. Wir setzen es auf eins, um unserem vorherigen Standardverhalten zu entsprechen. Um dem Befehlszeilenparser zu erlauben, diesen Wert zu ändern, müssen wir diesen Wert im Parser erfassen. Das tun wir, indem wir einen Aufruf hinzufügen AddValue. Gehen Sie und ändern Sie das Skript scratch/myfirst.cc , so dass es mit dem folgenden Code beginnt:

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Anzahl der Pakete, die ausgegeben werden sollen", nPackets);
cmd.Parse (argc, argv);
...

Scrollen Sie nach unten zu dem Punkt im Skript, an dem wir das Attribut MaxPackets setzen und ändern Sie es so, dass es auf die Variable nPackets anstelle der Konstante 1 eingestellt ist, wie unten gezeigt.

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

Jetzt sollten Sie, wenn Sie das Skript ausführen und das Argument —PrintHelp angeben, das neue Benutzerargument auf dem Hilfedisplay sehen. Geben Sie ein,

$ ./waf --run "scratch/myfirst --PrintHelp"
Waf: Betritt das Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Verlässt das Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' erfolgreich abgeschlossen (0.403s)
--PrintHelp: Diese Hilfsnachricht anzeigen.
--PrintGroups: Die Liste der Gruppen anzeigen.
--PrintTypeIds: Alle TypeIds anzeigen.
--PrintGroup=[group]: Alle TypeIds der Gruppe anzeigen.
--PrintAttributes=[typeid]: Alle Attribute von typeid anzeigen.
--PrintGlobals: Die Liste der Globals anzeigen.
Benutzerargumente:
--nPackets: Anzahl der zu sendenden Pakete

Wenn Sie die Anzahl der zu übertragenden Pakete ändern möchten, können Sie das tun, indem Sie das Argument -nPackets in der Befehlszeile setzen.

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

Jetzt sollten Sie sehen

Waf: Betritt das Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Verlässt das Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' erfolgreich abgeschlossen (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
1024 Bytes an 10.1.1.2 gesendet
2.25732s 1024 Bytes von 10.1.1.1 empfangen
2.25732s Paket zurückgesendet
1024 Bytes von 10.1.1.2 empfangen
1024 Bytes an 10.1.1.2 gesendet
3.25732s 1024 Bytes von 10.1.1.1 empfangen
3.25732s Paket zurückgesendet
1024 Bytes von 10.1.1.2 empfangen
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Sie haben jetzt zwei Pakete gesendet. Ganz einfach, oder?
Sie können sehen, dass Sie als Benutzer von ns-3 das System der Befehlszeilenargumente verwenden können, um globale Werte und Attribute zu steuern. Wenn Sie Modellentwickler sind, können Sie neue Attribute zu Ihren Objekten hinzufügen, die dann automatisch über das Befehlszeilensystem für Ihre Benutzer konfigurierbar sind. Wenn Sie Skriptersteller sind, können Sie Ihrer Skripte neue Variablen hinzufügen und sie nahtlos in das Befehlszeilensystem integrieren.

5.3 Verwendung des Nachverfolgungssystems

Der Sinn des Modellierens ist es, Ausgaben zur weiteren Analyse zu erzeugen, und das ns-3-Trace-System ist der Hauptmechanismus dafür. Da ns-3 ein Programm in C++ ist, können die Standardwerkzeuge zur Erzeugung von Ausgaben in C++ verwendet werden:

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

Sie können sogar das Logging-Modul verwenden, um ein wenig Struktur in Ihre Lösung zu bringen. Es sind viele Probleme bekannt, die durch einen solchen Ansatz entstehen, und um diese Probleme zu lösen, haben wir ein allgemeines Ereignistracing-Unterssystem bereitgestellt.

Hauptziele des ns-3-Trace-Systems sind:

  • Für grundlegende Aufgaben sollte das Trace-System es dem Benutzer ermöglichen, standardmäßige Tracings für beliebte Quellen zu generieren und auszuwählen, welche Objekte Tracing erzeugen.

  • Gelegenheitsbenutzer sollten in der Lage sein, das Trace-System zu erweitern, um das generierte Ausgabeformat zu ändern oder neue Trace-Quellen einzufügen, ohne den Kern des Simulators zu modifizieren;

  • Erfahrene Benutzer können den Kern des Simulators modifizieren, um neue Trace-Quellen und -Empfänger hinzuzufügen. Das ns-3 Trace-System basiert auf den Prinzipien unabhängiger Trace-Quellen und -Empfänger sowie einem einheitlichen Mechanismus zum Anschließen von Quellen an Verbraucher.

Das ns-3 Trace-System basiert auf den Prinzipien unabhängiger Trace-Quellen und -Empfänger sowie einem einheitlichen Mechanismus zum Anschließen von Quellen an Empfänger. Trace-Quellen sind Objekte, die Ereignisse signalisieren können, die während der Simulation auftreten, und den Zugriff auf interessante grundlegende Daten ermöglichen. Zum Beispiel kann eine Trace-Quelle anzeigen, wann ein Netzwerkgerät ein Paket empfangen hat, und den Zugang zum Inhalt des Pakets für interessierte Trace-Empfänger ermöglichen.

Trace-Quellen sind an sich nutzlos, wenn sie nicht mit anderen Teilen des Codes „verbunden“ sind, die tatsächlich etwas Nützliches mit den vom Empfänger bereitgestellten Informationen machen. Trace-Empfänger sind Verbraucher von Ereignissen und Daten, die von Trace-Quellen bereitgestellt werden. Zum Beispiel könnte man einen Trace-Empfänger erstellen, der (bei Verbindung mit der Trace-Quelle des vorherigen Beispiels) interessante Teile des empfangenen Pakets ausgibt.

Ein vernünftiger Grund für eine solche klare Trennung besteht darin, Benutzern zu ermöglichen, neue Arten von Empfängern an vorhandene Trace-Quellen anzuschließen, ohne den Kern des Simulators bearbeiten und neu kompilieren zu müssen. Somit kann der Benutzer im obigen Beispiel einen neuen Trace-Empfänger in seinem Skript definieren und ihn nur durch Bearbeitung seines benutzerdefinierten Skripts mit einer bestehenden Trace-Quelle verbinden, die im Simulation-Kern definiert ist.

In diesem Handbuch werden wir einige vordefinierte Quellen und Senken durchgehen und zeigen, wie man sie mit minimalem Aufwand für den Benutzer einrichtet. Siehe das ns-3-Handbuch oder die Anweisungsabschnitte für Informationen zur erweiterten Konfiguration der Nachverfolgung, einschließlich der Erweiterung des Nachverfolgungsnamensraums und der Erstellung neuer Nachverfolgungsquellen.

5.3.1 ASCII-Nachverfolgung

ns-3 bietet Hilfsfunktionen, die ein niedrigschwelliges Nachverfolgungssystem bereitstellen, um Ihnen bei den Details zu helfen, wenn Sie einfache Paketnachverfolgungen einrichten. Wenn Sie diese Funktion aktivieren, sehen Sie die Ausgaben in ASCII-Dateien. Für diejenigen, die mit der Ausgabe von ns-2 vertraut sind, ist dieser Nachverfolgungstyp ähnlich. out.tr, das von zahlreichen Skripten generiert wurde.

Lassen Sie uns zur Sache kommen und einige Ergebnisse der ASCII-Nachverfolgung in unser Skript scratch/myfirst.cc einfügen. Direkt vor dem Aufruf von Simulator::Run(), fügen Sie die folgenden Codezeilen hinzu:
AsciiTraceHelper ascii;

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

Wie in vielen anderen Idiomen von ns-3 verwendet dieser Code ein Hilfsobjekt zur Erstellung von ASCII-Nachverfolgungen. Die zweite Zeile enthält zwei geschachtelte Methodenaufrufe. Die „innere“ Methode CreateFileStream() verwendet das Idiom des anonymen Objekts, um ein File-Stream-Objekt im Stack (ohne Objektname) zu erstellen und es an die aufrufende Methode zu übergeben. In Zukunft werden wir dieses Thema vertiefen, aber alles, was Sie in diesem Stadium wissen müssen, ist, dass Sie ein Objekt erstellen, das eine Datei mit dem Namen myfirst.tr darstellt, und es an ns-3 übergeben. Wir überlassen ns-3 die Pflege des von Ihnen geschaffenen Objekts während seiner gesamten Lebensdauer, in der die Probleme, die durch das wenig bekannte (absichtliche) Restrisiko im Zusammenhang mit den Kopierkonstruktoren von Stream-Objekten in C++ verursacht werden, gelöst werden.

Der externe Aufruf EnableAsciiAll() teilt dem Hilfsobjekt mit, dass Sie in Ihrer Simulation die ASCII-Nachverfolgung für alle Punkt-zu-Punkt-Verbindungen aktivieren möchten und dass Sie möchten, dass die (angegebenen) Nachverfolgungsempfänger Informationen über den Paketverkehr im ASCII-Format aufzeichnen.

Für diejenigen, die mit ns-2 vertraut sind, entsprechen die nachverfolgten Ereignisse den bekannten Nachverfolgungspunkten, die die Ereignisse „+“, „-“, „d“ und „r“ protokollieren.
Jetzt können Sie das Skript kompilieren und es über die Befehlszeile ausführen:

$ ./waf --run scratch/myfirst

Wie oft haben Sie zuvor mehrere Nachrichten von Waf gesehen und dann die Meldung „'build' finished successfully“ (Build erfolgreich abgeschlossen) mit einer bestimmten Anzahl von Nachrichten der laufenden Anwendung?

Während der Ausführung wird das Programm eine Datei mit dem Namen erstellen myfirst.tr. Aufgrund der Besonderheiten der Funktionsweise Waf, wird die Datei standardmäßig nicht im lokalen Verzeichnis, sondern im Hauptverzeichnis des Repositories erstellt. Wenn Sie den Pfad ändern möchten, in dem die Traces gespeichert werden, können Sie den Waf-Parameter verwenden --cwd. Das haben wir nicht getan, um die ASCII-Trace-Datei myfirst.tr in Ihrem bevorzugten Editor anzusehen, müssen wir ins Hauptverzeichnis unseres Repositories wechseln.

Parsen von ASCII-Nachverfolgungen

Dort gibt es eine Fülle von Informationen in ziemlich dichter Form, aber das Erste, auf das Sie achten sollten, ist, dass die Datei aus einzelnen Zeilen besteht. Dies wird deutlich, wenn Sie das Anzeige-Fenster etwas aufziehen.

Jede Zeile in der Datei entspricht einem Trace-Ereignis. In diesem Fall verfolgen wir Ereignisse in der Übertragungswarteschlange, die in jedem Punkt-zu-Punkt-Netzwerkgerät in der Simulation vorhanden ist. Die Übertragungswarteschlange ist die Warteschlange, durch die jedes Paket für den Punkt-zu-Punkt-Kanal gehen muss. Beachten Sie, dass jede Zeile in der Trace-Datei mit einem einzelnen Symbol beginnt (und ein Leerzeichen danach hat). Dieses Symbol hat folgende Bedeutung:

+: Eine Einreihoperation im Gerät trat in der Warteschlange auf;
-: Eine Extraktionsoperation im Gerät trat in der Warteschlange auf;
d: Das Paket wurde verworfen, in der Regel weil die Warteschlange voll ist;
r: Das Paket wurde vom Netzwerkgerät empfangen.

Lassen Sie uns die erste Zeile in der Trace-Datei genauer betrachten. Ich werde sie in Teile zerlegen (mit Einrückungen zur Veranschaulichung) und mit der Zeilennummer links:

0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4   Point-to-Point Protokoll: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 Protokoll 17 offset 0 flags [none]
8     Länge: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      Länge: 1032 49153 > 9)
11      Payload (Größe=1024)

Der erste Abschnitt dieses erweiterten Trace-Ereignisses (Zeile 0) ist die Operation. Hier haben wir das Zeichen +, was einer Einreihoperation für die Übertragung entspricht. Der zweite Abschnitt (Zeile 1) ist die Simulationszeit, ausgedrückt in Sekunden. Sie können sich erinnern, dass wir darum gebeten haben UdpEchoClientApplication Die Paketübertragung beginnt in zwei Sekunden. Hier sehen wir die Bestätigung, dass dies tatsächlich geschieht.

Im folgenden Abschnitt der Trace (beginnend bei Zeile 2) wird angezeigt, welche Trace-Quelle dieses Ereignis hervorgebracht hat (der Namespace Trace wird angegeben). Sie können sich den Trace-Namespace ähnlich wie das Dateisystem vorstellen. Der Root des Namespace ist NodeList. Dies entspricht dem Container, der hauptsächlich im ns-3-Code verwaltet wird. Er enthält alle Knoten, die im Skript erstellt werden. So wie ein Dateisystem im Root Verzeichnisse haben kann, können wir NodeList mehrere Knoten haben. Daher verweist die Zeile /NodeList/0 auf den Nullknoten im NodeList, den wir normalerweise als "Knoten 0" verstehen. Jeder Knoten hat eine Liste von Geräten, die installiert wurden. Diese Liste befindet sich als Nächstes im Namespace. Sie können sehen, dass dieses Trace-Ereignis von DeviceList/0, dem Nullgerät, das im Knoten installiert ist, ausgeht.

Die nächste Unterzeile, $ns3::PointToPointNetDevice, gibt an, welches Gerät sich an der Nullposition: der Gerätschaften im Nullknoten befindet. Erinnern Sie sich, dass die Operation + in Zeile 0 bedeutete, dass ein Element in die Übertragungswarteschlange des Geräts hinzugefügt wurde. Dies spiegelt sich in den letzten Segmenten des "Trace-Pfads" wider: TxQueue/Enqueue.

Die restlichen Abschnitte der Trace sollten intuitiv verständlich sein. Die Zeilen 3-4 geben an, dass das Paket im Punkt-zu-Punkt-Protokoll gekapselt ist. Die Zeilen 5-7 zeigen, dass das Paket einen IP4-Version-Header hat und von der IP-Adresse 10.1.1.1 kommt und bestimmt ist für 10.1.1.2. Die Zeilen 8-9 zeigen, dass dieses Paket einen UDP-Header hat und schließlich zeigt Zeile 10, dass die Nutzlast die erwarteten 1024 Bytes hat.

Die nächste Zeile in der Trace-Datei zeigt, dass dasselbe Paket aus der Übertragungswarteschlange auf demselben Knoten entnommen wurde.

Die dritte Zeile in der Trace-Datei zeigt, dass das Paket vom Netzwerkgerät auf dem Knoten mit dem Echo-Server empfangen wurde. Ich habe dieses Ereignis unten rekonstruiert.

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

Bitte beachten Sie, dass die Trace-Operation jetzt r ist und die Simulationszeit auf 2,25732 Sekunden erhöht wurde. Wenn Sie die Anweisungen im Tutorial sorgfältig befolgt haben, bedeutet das, dass Sie die DataRate der Netzwerkgeräte und die Kanalverzögerung auf deren Standardwerte belassen haben. Diese Zeit sollte Ihnen bekannt vorkommen, da Sie sie bereits im vorherigen Abschnitt gesehen haben.

Der Namespace des Trace-Quells (Zeile 2) wurde geändert, um widerzuspiegeln, dass dieses Ereignis von Knoten 1 kommt (/NodeList/1) и пакет принят источником трассировки (/MacRx). Es sollte Ihnen relativ einfach fallen, den Paketverkehr durch die Topologie zu verfolgen, indem Sie die verbleibenden Informationen in der Trace-Datei einsehen.

5.3.2 PCAP-Nachverfolgung

Die ns-3-Geräteassistenten können auch verwendet werden, um Trace-Dateien im .pcap-Format zu erstellen. Das Akronym pcap wird normalerweise in Kleinbuchstaben geschrieben und steht für Paketermittlung und ist tatsächlich eine API, die die Definition des .pcap-Dateiformats umfasst. Das beliebteste Programm, das dieses Format lesen und anzeigen kann, ist Wireshark (früher genannt Ethereal). Es gibt jedoch viele Traffic-Trace-Analyzer, die dieses Paketformat verwenden. Wir empfehlen den Benutzern, verschiedene Tools zu verwenden, die für die Analyse von pcap-Traces verfügbar sind. In diesem Handbuch konzentrieren wir uns auf die Anzeige von pcap-Traces mit tcpdump.

Das Aktivieren der pcap-Tracierung erfolgt mit einer einzigen Codezeile.

pointToPoint.EnablePcapAll ("myfirst");

Fügen Sie diese Codezeile nach dem ASCII-Trace-Code ein, den wir gerade hinzugefügt haben. scratch/myfirst.ccBitte beachten Sie, dass wir nur den String „myfirst“ übergeben haben, nicht „myfirst.pcap“ oder etwas Ähnliches. Das liegt daran, dass der Parameter ein Präfix und kein vollständiger Dateiname ist. Während der Simulation erstellt der Assistent tatsächlich für jedes Punkt-zu-Punkt-Gerät eine Trace-Datei. Die Dateinamen werden unter Verwendung des Präfixes, der Knotennummer, der Gerätnummer und des Suffixes „pcap».

In unserem Beispiel-Skript werden wir schließlich Dateien mit den Namen „myfirst-0-0.pcap» und „myfirst-1-0.pcap“ sehen, die pcap-Traces für Knoten 0-Gerät 0 und Knoten 1-Gerät 0 sind. Nachdem Sie die Codezeile zum Aktivieren der pcap-Tracierung hinzugefügt haben, können Sie das Skript wie gewohnt ausführen:

$ ./waf --run scratch/myfirst

Wenn Sie im obersten Verzeichnis Ihrer Distribution nachsehen, sollten Sie drei Dateien sehen: die ASCII-Trace-Datei, die wir zuvor untersucht haben, die Dateien myfirst.tr, den wir zuvor studiert haben, Dateien myfirst-0-0.pcap und myfirst-1-0.pcap — neue pcap-Dateien, die wir gerade erstellt haben.

Lesen der Ausgabe mit tcpdump

Aktuell ist es am einfachsten, pcap-Dateien mit tcpdump anzusehen.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
liest von der Datei myfirst-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, Länge 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, Länge 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
liest von der Datei myfirst-1-0.pcap, link-type PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, Länge 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, Länge 1024

Im Dump myfirst-0-0.pcap (Clientgerät) können Sie sehen, dass das Echo-Paket nach 2 Sekunden Simulation gesendet wird. Wenn Sie sich den zweiten Dump ansehen (myfirst-1-0.pcap), sehen Sie, dass das Paket im Moment 2,257324 Sekunden empfangen wird. Sie werden im zweiten Dump sehen, dass das Paket im Moment 2,257324 Sekunden zurückgegeben wird, und schließlich, dass das Paket im ersten Dump im Moment 2,514648 Sekunden wieder vom Client empfangen wurde.

Lesen der Ausgabe mit Wireshark

Wenn Sie mit Wiresharknicht vertraut sind, gibt es eine Website, von der Sie Programme und Dokumentation herunterladen können: http://www.wireshark.org/. Wireshark — dies ist eine grafische Benutzeroberfläche, die verwendet werden kann, um diese Trace-Dateien anzuzeigen. Wenn Sie Wireshark haben, können Sie jede der Trace-Dateien öffnen und den Inhalt anzeigen, als ob Sie Pakete mit einem Paket-Analyzer erfasst hätten.

Quelle: habr.com

60GB SSD 8Gb DDR4