Handbuch zum ns-3-Netzwerksimulator. Kapitel 4

Handbuch zum ns-3-Netzwerksimulator. Kapitel 4
Kapitel 1,2
Kapitel 3

4 Konzeptübersicht
4.1 Schlüsselabstraktionen
4.1.1 Knoten (Node)
4.1.2 Anwendung (Application)
4.1.3 Kanal (Channel)
4.1.4 Netzgerät (Net Device)
4.1.5 Topologische Helfer
4.2 Erstes ns-3 Skript
4.2.1 Boilerplate-Code
4.2.2 Plug-in-Module
4.2.3 Namensraum ns3
4.2.4 Protokollierung
4.2.5 Hauptfunktion
4.2.6 Verwendung von topologischen Helfern
4.2.7 Verwendung der Anwendung
4.2.8 Simulator
4.2.9 Bereitstellung Ihres Szenarios
4.3 ns-3 Quellcode

Kapitel 4

Konzeptübersicht

Das Erste, was wir tun müssen, bevor wir mit dem Lernen oder Schreiben von ns-3-Code beginnen, ist, einige grundlegende Konzepte und Abstraktionen im System zu erklären. Vieles davon mag für einige offensichtlich erscheinen, aber wir empfehlen, sich Zeit zu nehmen, um diesen Abschnitt zu lesen, um sicherzustellen, dass Sie auf einer soliden Basis beginnen.

4.1 Schlüsselabstraktionen

In diesem Abschnitt werden wir einige Begriffe betrachten, die typischerweise in Netzwerken verwendet werden, aber eine bestimmte Bedeutung in ns-3 haben.

4.1.1 Knoten (Node)

Im Internet-Jargon wird ein Computergerät, das mit dem Netzwerk verbunden ist, als Host oder manchmal als Endsystem bezeichnet. Da ns-3 ein Netzwerk-Simulator und kein Internet-Simulator ist, verwenden wir absichtlich nicht den Begriff Host, da dieser eng mit dem Internet und seinen Protokollen verbunden ist. Stattdessen verwenden wir den allgemeineren Begriff, der auch von anderen Simulatoren verwendet wird und aus der Graphentheorie stammt – Knoten (node).

In ns-3 wird die grundlegende Abstraktion eines Rechenzentrums als Knoten bezeichnet. Diese Abstraktion wird in C++ durch die Klasse Node repräsentiert. Die Klasse NodeNode (Knoten) bietet Methoden zur Verwaltung der Darstellungen von Rechenzentren in Simulationen.

Sie sollten verstehen Node wie ein Computer, dem Sie Funktionalitäten hinzufügen werden. Sie werden Dinge hinzufügen wie Anwendungen, Protokollstapel und Schnittstellenkarten mit Treibern, die es dem Computer ermöglichen, nützliche Arbeit zu verrichten. Wir verwenden dasselbe grundlegende Modell in ns-3.

4.1.2 Anwendung (Application)

In der Regel wird Software in zwei breite Klassen unterteilt. Systemsoftware organisiert verschiedene Computerressourcen wie Speicher, Prozessorzyklen, Festplatte, Netzwerk usw. gemäß einem bestimmten Rechenmodell. Systemsoftware nutzt diese Ressourcen normalerweise nicht, um Aufgaben auszuführen, die dem Benutzer direkt nützen. Ein Benutzer startet in der Regel eine Anwendung, um ein bestimmtes Ziel zu erreichen, die die Ressourcen verwendet, die von der Systemsoftware verwaltet werden.

Oft wird die Grenze zwischen System- und Anwendungssoftware beim Wechsel der Berechtigungsstufen gezogen, der in den Ausnahmen des Betriebssystems erfolgt. In ns‑3 gibt es kein echtes Konzept eines Betriebssystems und daher keine Begriffe für Berechtigungsstufen oder Systemaufrufe. Wir haben jedoch die Idee Anwendung. Genauso wie im „realen Leben“ zur Durchführung von Aufgaben Anwendungsprogramme auf Computern arbeiten, arbeiten ns‑3-Anwendungen auf ns‑3-Knoten, um Simulationen in einer simulierten Welt zu steuern.

In ns‑3 ist die grundlegende Abstraktion für das Benutzerprogramm, das eine gewisse Aktivität zur Modellierung erzeugt, eine Anwendung. Diese Abstraktion wird in C++ durch die Klasse Application (Anwendung) dargestellt. Die Klasse Application bietet Methoden zur Steuerung unserer Version der Anwendungen auf Benutzerebene in Simulationen. Von den Entwicklern wird erwartet, dass sie zur Erstellung neuer Anwendungen die Klasse Application im Sinne der objektorientierten Programmierung spezialisieren. In diesem Handbuch werden wir Spezialitäten der Klasse Application verwenden, die genannt werden UdpEchoClientApplication und UdpEchoServerApplication. Wie zu erwarten war, bilden diese Anwendungen eine Sammlung von Client-/Server-Anwendungen, die zur Generierung und Echo-Simulation von Netzwerkpaketen verwendet werden.

4.1.3 Kanal (Channel)

In der realen Welt kann man einen Computer mit einem Netzwerk verbinden. Häufig werden die Umgebungen, über die Daten in diesen Netzwerken übertragen werden, als Kanäle bezeichnet. Wenn Sie ein Ethernet-Kabel an die Wandsteckdose anschließen, verbinden Sie den Computer mit dem Ethernet-Kommunikationskanal. In der simulierten Welt von ns-3 wird ein Knoten mit einem Objekt verbunden, das den Kommunikationskanal repräsentiert. Hier wird die grundlegende Abstraktion des Kommunikationsunternetzes als Kanal bezeichnet und durch die C++-Klasse Channel repräsentiert.

Klasse ChannelChannel stellt Methoden zur Verwaltung der Interaktion von Objekten im Unternetz und zur Verbindung mit ihnen zur Verfügung. Kanäle können auch von Entwicklern im Sinne der objektorientierten Programmierung spezialisiert werden. Eine Spezialisierung des Kanals kann etwas Einfaches wie einen Draht modellieren. Ein spezialisierter Kanal kann auch komplexe Dinge wie einen großen Ethernet-Switch oder einen dreidimensionalen Raum voller Hindernisse im Fall von drahtlosen Netzwerken modellieren.

In diesem Handbuch werden wir spezialisierte Versionen des Kanals verwenden, die als CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel und WifiChannelWifiChannel. CsmaChannel, zum Beispiel, modelliert eine Version des Kommunikationsunternetzes, die eine Kommunikationsumgebung mit mehreren Zugriffssteuerungen umsetzt. Dies gibt uns eine Ethernet-ähnliche Funktionalität.

4.1.4 Netzgerät (Net Device)

Früher war es so, dass, wenn Sie einen Computer mit einem Netzwerk verbinden wollten, Sie ein bestimmtes Netzwerkkabel und ein Hardwaregerät kaufen mussten, das in der PC-Terminologie als Peripheriekarte bezeichnet wird, das im Computer installiert werden musste. Wenn auf der Peripheriekarte bestimmte Netzwerkfunktionen umgesetzt wurden, wurden diese als Netzwerkinterfacekarten oder Netzwerkkarten bezeichnet. Heute werden die meisten Computer mit integriertem Netzwerkinterface-Hardware geliefert, und die Benutzer sehen sie nicht mehr als separate Geräte.

Eine Netzwerkkarte funktioniert nicht ohne einen Softwaretreiber, der die Hardware steuert. In Unix (oder Linux) wird ein Teil der Peripherie als Gerät klassifiziert. Geräte werden mit Hilfe von Gerätetreibern verwaltet, während Netzwerkgeräte (NIC) unter Verwendung von Treibern für Netzwerkgeräte verwaltet werden (network device drivers) und haben die Sammelbezeichnung Netzwerkgeräte (net devices). In Unix und Linux greifen Sie auf Netzwerkgeräte über Namen wie zum Beispiel zu eth0.

In ns‑3 umfasst die Abstraktion des Netzwerkgeräts sowohl den Softwaretreiber als auch die modellierte Hardware. Während der Simulation wird das Netzwerkgerät im Knoten 'installiert', um ihm zu ermöglichen, sich über Kanäle mit anderen Knoten zu verbinden. Wie bei einem echten Computer kann der Knoten über mehrere Geräte mit mehreren Kanälen verbunden sein. NetDevices.

Die Netzwerkabstraktion des Geräts wird in C++ durch die Klasse NetDevice. Die Klasse NetDevice stellt Methoden zur Verwaltung von Verbindungen zu den Objekten Node und Channel bereit; und kann von Entwicklern im Sinne der objektorientierten Programmierung spezialisiert werden. In diesem Handbuch werden wir mehrere spezialisierte Versionen von NetDevice verwenden, die die Namen CsmaNetDevice, PointToPointNetDevice und WifiNetDevice. So wie ein Ethernet-Netzwerkadapter für den Betrieb mit Ethernet, CsmaNetDevice für den Betrieb mit CsmaChannel, PointToPointNetDevice für den Betrieb mit PointToPointChannel, und WifiNetDevice – für den Betrieb mit WifiChannel.

4.1.5 Topologische Helfer

In einem echten Netzwerk finden Sie Host-Computer mit hinzugefügten (oder integrierten) Netzwerkkarten. In ns‑3 würden wir sagen, dass Sie Knoten mit angeschlossenen NetDevices sehen werden. In einem großen modellierten Netzwerk müssen Sie Verbindungen zwischen vielen Objekten organisieren. Node, NetDevice und Channel.

Da das Verbinden von NetDevices mit Knoten, NetDevices mit Kanälen, das Zuweisen von IP-Adressen usw. in ns‑3 eine alltägliche Aufgabe ist, stellen wir sogenannte topologische Hilfsmittel bereit, um dies so einfach wie möglich zu gestalten. Zum Beispiel erfordert das Erstellen eines NetDevice eine Vielzahl von Kernoperationen in ns‑3, das Hinzufügen einer MAC-Adresse, die Einrichtung dieses Netzwerkgeräts im Node, die Konfiguration des Protokollstacks des Knotens und anschließend das Verbinden des NetDevice mit dem Channel. Noch mehr Operationen sind erforderlich, um mehrere Geräte mit Mehrpunktkanälen zu verbinden und dann separate Netzwerke zu einem zusammenhängenden Netzwerk (Internetworks) zu verbinden. Wir bieten Hilfsobjekte für die Topologie, die diese zahlreichen Operationen zu einem benutzerfreundlichen Modell zusammenfassen.

4.2 Erstes ns-3 Skript

Wenn Sie das System wie oben vorgeschlagen installiert haben, befindet sich das ns‑3 Release im Verzeichnis mit dem Namen repos in Ihrem Home-Verzeichnis. Wechseln Sie in das Verzeichnis release

Wenn Sie kein solches Verzeichnis haben, haben Sie beim Bau der Release-Version von ns-3 kein Ausgabeverzeichnis angegeben. Führen Sie den Build wie folgt aus:
$ ./waf configure —build-profile=release —out=build/release,
$ ./waf build

Dort sollten Sie eine Verzeichnisstruktur sehen, die ungefähr wie folgt aussieht:

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

Wechseln Sie in das Verzeichnis examples/tutorial. Dort sollten Sie eine Datei mit dem Namen first.ccfinden. Dies ist ein Skript, das eine einfache Punkt-zu-Punkt-Verbindung zwischen zwei Knoten erstellt und ein Paket zwischen diesen Knoten überträgt. Lassen Sie uns dieses Skript Zeile für Zeile durchgehen, indem wir first.cc in Ihrem bevorzugten Editor öffnen.

4.2.1 Boilerplate-Code
Die erste Zeile in der Datei ist die Editor-Modus-Zeile Eclipse. Sie informiert emacs über die Formatierungsvereinbarungen (Codestil), die wir in unserem Quellcode verwenden.

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

Dies ist immer eine ziemlich umstrittene Frage, daher sollten wir klarstellen, um sie gleich aus dem Weg zu räumen. Das Projekt ns-3 hat, wie viele große Projekte, einen Codestil angenommen, dem der gesamte bereitgestellte Code entsprechen muss. Wenn Sie Ihren Code in das Projekt einbringen möchten, müssen Sie letztendlich den ns-3-Codestandard einhalten, wie er in der Datei doc/codingstd.txt beschrieben ist oder auf der Projektwebseite gezeigt wird: https://www.nsnam.org/develop/contributing-code/coding-style/.

Wir empfehlen Ihnen, sich an das Aussehen des ns-3-Codes zu gewöhnen und diesen Standard immer anzuwenden, wenn Sie mit unserem Code arbeiten. Das gesamte Entwicklerteam und die Mitwirkenden haben sich nach einiger Diskussion darauf geeinigt. Die oben angegebene emacs-Moduszeile erleichtert die korrekte Formatierung, wenn Sie den Editor emacs verwenden.

Der Simulator ns-3 wird unter Verwendung der GNU General Public Licenselizenziert. Sie werden in jeder Datei des ns-3-Distros einen entsprechenden juristischen GNU-Kopf sehen. Oft können Sie über dem GPL-Text und dem Autor, der unten angegeben ist, einen Copyright-Hinweis für eine der beteiligten Institutionen im ns-3-Projekt sehen.

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

4.2.2 Plug-in-Module

Der eigentliche Code beginnt mit einer Reihe von Include-Anweisungen (include).

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

Um unseren Nutzern von Hochsprache-Skripten zu helfen, mit der Vielzahl an Header-Dateien in unserem System umzugehen, gruppieren wir diese nach ihrer Verwendung in großen Modulen. Wir stellen eine Header-Datei bereit, die rekursiv alle in diesem Modul verwendeten Header-Dateien lädt. Anstatt zu suchen, welches genau Header Sie benötigen und möglicherweise die richtigen Abhängigkeiten zu erhalten, geben wir Ihnen die Möglichkeit, eine Gruppe von Dateien mit einer hohen Detailgenauigkeit zu laden. Dies ist nicht der effizienteste Ansatz, aber er macht das Schreiben von Skripten auf jeden Fall einfacher.

Jede der eingeschlossenen ns-3-Dateien wird in ein Verzeichnis mit dem Namen ns3 untergebracht, um während des Build-Prozesses Namenskonflikte zu vermeiden. Die Datei ns3/core-module.h entspricht dem ns-3-Modul, das Sie im Verzeichnis src/core in der von Ihnen installierten Version finden. In der Auflistung dieses Verzeichnisses finden Sie eine große Anzahl von Header-Dateien. Wenn Sie einen Build durchführen, Waf werden die öffentlichen Header-Dateien in das ns3-Verzeichnis im Unterverzeichnis build/debug

Wenn Sie kein solches Verzeichnis haben, haben Sie beim Bau der Release-Version von ns-3 kein Ausgabeverzeichnis angegeben. Führen Sie den Build wie folgt aus:
$ ./waf configure --build-profile=debug --out=build/debug
$ ./waf build
oder
$ ./waf configure --build-profile=optimized --out=build/optimized
$ ./waf build

oder build/optimized, je nach Ihrer Konfiguration. Waf Wird auch automatisch eine Header-Datei für das Modul generiert, um alle öffentlichen Header-Dateien zu laden. Da Sie diesem Handbuch natürlich konsequent folgen, haben Sie bereits

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

ausgeführt, um das Projekt für Debug-Bauten mit aktivierten Beispielen und Tests zu konfigurieren. Sie haben auch

$ ./waf

ausgeführt, um das Projekt zu erstellen. Wenn Sie jetzt also im Verzeichnis ../..//build/debug/ns3nachsehen, finden Sie dort unter anderem die Header-Dateien der vier oben genannten Module. Sie können den Inhalt dieser Dateien überprüfen und feststellen, dass sie alle öffentlichen Dateien der entsprechenden Module enthalten.

4.2.3 Namensraum ns3

Die nächste Zeile im Skript first.cc ist die Deklaration des Namensraums.

using namespace ns3;

Das ns‑3-Projekt wird im C++-Namensraum implementiert, der ns3 genannt wird. Dies gruppiert alle ns‑3-bezogenen Deklarationen in einem Sichtbarkeitsbereich außerhalb des globalen Namensraums, was, wie wir hoffen, die Integration mit anderem Code erleichtert. Die Verwendung des C++-Operators führt den ns‑3-Namensraum in den aktuellen (globalen) deklarierten Bereich ein. Das ist eine etwas seltsame Art zu sagen, dass Sie nach dieser Deklaration den ns3::Scope-Operator nicht mehr vor jedem ns‑3-Code einfügen müssen, um ihn zu verwenden. Wenn Sie mit Namensräumen nicht vertraut sind, schauen Sie in jedes beliebige C++-Lehrbuch und vergleichen Sie den Namensraum ns3 mit dem Verwendung des Namensraums std und der Deklarationen. using namespace std; in Beispielen mit dem Ausgabesystem cout und Streams.

4.2.4 Protokollierung

Die nächste Zeile des Skripts lautet:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Wir werden diese Aussage als praktischen Ort verwenden, um über unser Dokumentationssystem zu sprechen. DoxygenWenn Sie die Website des ns‑3-Projekts besuchen, finden Sie im Navigationsbereich einen Link "Dokumentation". Wenn Sie diesen Link auswählen, gelangen Sie zu unserer Dokumentationsseite. Es gibt einen Link zu "Letzte Version", die Sie zur Dokumentation der neuesten stabilen Version von ns‑3 führt. Wenn Sie den Link zu "API-Dokumentation" auswählen, gelangen Sie zur API-Dokumentationsseite von ns‑3.

Auf der linken Seite der Seite finden Sie eine grafische Darstellung der Dokumentationsstruktur. Ein guter Ausgangspunkt ist das "Modulbuch" für ns‑3 im Navigationsbaum von ns‑3. Wenn Sie das aufklappen Module, sehen Sie eine Liste der Dokumentation der ns‑3-Module. Wie oben erwähnt, hängt das Konzept eines Moduls hier direkt von den in das Modul eingebundenen Dateien ab. Das ns‑3-Logging-System wird im Abschnitt Verwendung des Logging-Moduls, die wir später in diesem Handbuch erneut besuchen werden, aber Sie können die oben genannte Aussage überprüfen, indem Sie das Modul ansehen Kern, und dann das Buch Debugging-Tools, und dann die Seite auswählen Logging. Klicken Sie auf Logging.

Jetzt sollten Sie die Dokumentation Doxygen für das Modul durchsehen. Logging. In der Makroliste oben auf der Seite sehen Sie den Eintrag für NS_LOG_COMPONENT_DEFINE. Bevor Sie den Link anklicken, sollten Sie die "Detaillierte Beschreibung" des Registrierungsmoduls lesen, um dessen Funktionsweise zu verstehen. Dazu können Sie nach unten scrollen oder "Mehr…" unter dem Diagramm auswählen.

Sobald Sie einen allgemeinen Überblick darüber haben, was passiert, fahren Sie fort und sehen Sie sich die Dokumentation zu den spezifischen NS_LOG_COMPONENT_DEFINE an. Ich werde die Dokumentation hier nicht duplizieren, aber zusammenfassend kann ich sagen, dass diese Zeile einen Registrierungsbestandteil mit dem Namen FirstScriptExample, der das Aktivieren und Deaktivieren der Konsolenregistrierung von Nachrichten über einen Namenslink ermöglicht.

4.2.5 Hauptfunktion

In den folgenden Zeilen des Skripts werden Sie sehen,

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

Dies ist einfach die Deklaration der Hauptfunktion Ihres Programms (Skripts). Wie in jedem C++-Programm müssen Sie die Hauptfunktion definieren, sie wird als erstes ausgeführt. Hier gibt es nichts Besonderes. Ihr ns-3-Skript ist einfach ein C++-Programm. Die folgende Zeile setzt die Zeitauflösung auf 1 Nanosekunde, was der Standardwert ist:

Time::SetResolution (Time::NS);

Die Zeitauflösung oder einfach Auflösung ist der kleinste Zeitwert, der verwendet werden kann (die kleinste darstellbare Differenz zwischen zwei Zeitwerten). Sie können die Auflösung genau einmal ändern. Der Mechanismus, der diese Flexibilität ermöglicht, benötigt Speicherplatz, daher geben wir, sobald die Auflösung ausdrücklich festgelegt ist, den Speicher frei, um weitere Aktualisierungen zu verhindern. (Wenn Sie die Auflösung nicht ausdrücklich festlegen, wird sie standardmäßig auf eine Nanosekunde gesetzt, und der Speicher wird zu Beginn der Simulation freigegeben.)

Die folgenden beiden Zeilen des Skripts dienen dazu, zwei Protokollierungskomponenten zu aktivieren, die in die Anwendungen EchoClient und EchoServer:

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

Wenn Sie die Dokumentation zur Logging-Komponente gelesen haben, werden Sie feststellen, dass es mehrere Detailstufen der Protokollierung gibt, die Sie für jede Komponente aktivieren können. Diese beiden Codezeilen aktivieren die Debug-Protokollierung auf der INFO-Stufe für Echo-Clients und -Server. Auf dieser Stufe wird die Anwendung während der Simulation Nachrichten beim Senden und Empfangen von Paketen ausgeben.

Nun kommen wir direkt zur Erstellung der Topologie und zum Start der Simulation. Wir setzen topologische Hilfsobjekte ein, um diese Arbeit so einfach wie möglich zu gestalten.

4.2.6 Verwendung von topologischen Helfern

Die beiden folgenden Codezeilen in unserem Skript erzeugen tatsächlich Node-Objekte ns-3, die Computer in der Simulation darstellen werden.

NodeContainer nodes;
nodes.Create(2);

Bevor wir fortfahren, lassen Sie uns die Dokumentation für die Klasse NodeContainer. Ein weiterer Weg, um zur Dokumentation dieser Klasse zu gelangen, erfolgt über den Tab Klassen auf den Seiten Doxygen. Wenn Sie Doxygen bereits geöffnet haben, scrollen Sie einfach nach oben zum oberen Ende der Seite und wählen Sie den Tab Klassen aus. Sie sollten eine neue Reihe von Tabs sehen, einer davon ist die Liste der Klassen. Unter diesem Tab sehen Sie eine Liste aller Klassen von ns-3. Scrollen Sie nach unten bis zu ns3::NodeContainer. Wenn Sie die Klasse gefunden haben, wählen Sie sie aus, um zur Dokumentation für die Klasse zu gelangen.

Wie wir uns erinnern, ist eines unserer Schlüsselkonzepte der Knoten. Er stellt den Computer dar, dem wir Dinge wie Protokollstapel, Anwendungen und Schnittstellenkarten hinzufügen möchten. Der topologische Helfer NodeContainer bietet eine bequeme Möglichkeit zum Erstellen, Verwalten und Zugreifen auf alle Objekte, Nodedie wir zur Durchführung der Simulation erstellen. Die erste Zeile oben deklariert einfach NodeContainer, die wir nodes nennen. Die zweite Zeile ruft die Methode Create für das Objekt nodes auf und fordert den Container auf, zwei Knoten zu erstellen. Wie in Doxygenbeschrieben, fordert der Container das ns-3-System auf, zwei Objekte zu erstellen Node und speichert die Zeiger auf diese Objekte intern.

Die im Skript erstellten Knoten tun noch nichts. Der nächste Schritt beim Aufbau der Topologie ist die Verbindung unserer Knoten mit dem Netzwerk. Die einfachste Form eines Netzwerks, die wir unterstützen, ist eine Punkt-zu-Punkt-Verbindung zwischen zwei Knoten. Wir werden jetzt eine solche Verbindung herstellen.

PointToPointHelper

Wir erstellen eine Punkt-zu-Punkt-Verbindung, indem wir einem uns vertrauten Muster folgen, und verwenden ein topologisches Hilfsobjekt für die Durchführung der Low-Level-Arbeiten, die zur Verbindung erforderlich sind. Erinnern wir uns daran, dass unsere beiden Schlüsselabstraktionen NetDevice und Channelim realen Leben ungefähr den Peripheriekarten und Netzwerkkabeln entsprechen. In der Regel sind diese beiden Dinge eng miteinander verbunden, und niemand kann erwarten, beispielsweise Geräte Ethernet über einen drahtlosen Kanal auszutauschen. Unsere topologischen Hilfsobjekte folgen dieser engen Beziehung, und daher werden Sie in diesem Szenario ein Objekt PointToPointHelper zum Konfigurieren und Verbinden von ns-3-Objekten verwenden. PointToPointNetDevice und PointToPointChannelDie nächsten drei Zeilen im Szenario:

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

Die erste Zeile,

PointToPointHelper pointToPoint;

erstellt im Stack eine Instanz des Objekts. PointToPointHelperAus Sicht der oberen Ebene sagt die nächste Zeile,

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

dem Objekt PointToPointHelper das zu verwendende Wert «5 Mbit/s» (fünf Megabit pro Sekunde) als «DataRate».

In spezifischerer Sicht entspricht die Zeile «DataRate» dem, was wir einen Attribut nennen. PointToPointNetDeviceWenn Sie sich ansehen, Doxygen für die Klasse ns3::PointToPointNetDevice und in der Dokumentation der Methode GetTypeId finden Sie eine Liste der Attribute, die für das Gerät definiert sind. Unter ihnen wird es das Attribut «DataRate» geben. Die meisten für Benutzer sichtbaren ns-3-Objekte haben ähnliche Listen von Attributen. Wir nutzen diesen Mechanismus für eine einfache Konfiguration der Simulation ohne erneute Kompilierung, wie Sie im nächsten Abschnitt sehen werden.

Ähnlich wie «DataRate» im PointToPointNetDevice finden Sie das Attribut «Delay», das mit PointToPointChannel verbunden ist. Die finale Zeile,

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

sagen PointToPointHelper verwendet den Wert «2 ms» (zwei Millisekunden) als Wert für die Ausbreitungsverzögerung über den Punkt-zu-Punkt-Kanal, den er anschließend erstellt.

NetDeviceContainer

Bis jetzt haben wir im Szenario ein NodeContainer, das zwei Knoten enthält. Wir haben ein PointToPointHelper, das bereit ist, um Objekte PointToPointNetDevices zu erstellen und sie mithilfe des Objekts PointToPointChannel zu verbinden. So wie wir ein topologisches Hilfsobjekt NodeContainer verwendet haben, um Knoten zu erstellen, werden wir PointToPointHelper bitten, die mit der Erstellung, Konfiguration und Installation unserer Geräte verbundenen Arbeiten für uns zu erledigen. Wir benötigen eine Liste aller erstellten Objekte. NetDevice, deshalb verwenden wir NetDeviceContainer zum Speichern, so wie wir NodeContainer zum Speichern der von uns erstellten Knoten verwendet haben. Die folgenden beiden Codezeilen

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

vollenden die Konfiguration der Geräte und des Kanals. Die erste Zeile deklariert den oben genannten Gerätekontainer, und die zweite führt die Hauptarbeit aus. Die Methode Installieren des Objekts PointToPointHelper das gleiche NodeContainer als Parameter. Innerhalb NetDeviceContainer für jeden Knoten, der sich in NodeContainer erstellt wird (für die Punkt-zu-Punkt-Verbindung muss es genau zwei geben) PointToPointNetDevice geschaffen und im Gerätekontainer gespeichert. PointToPointChannel wird erstellt, und es sind zwei PointToPointNetDevices. Nach der Erstellung der Objekte werden die in PointToPointHelper, gespeicherten Attribute verwendet, um die entsprechenden Attribute in den erstellten Objekten zu initialisieren.

Nach dem Aufruf pointToPoint.Install (nodes) haben wir zwei Knoten, jeder mit einem installierten "Punkt-zu-Punkt"-Netzwerkgerät und einem "Punkt-zu-Punkt"-Kanal zwischen ihnen. Beide Geräte werden so konfiguriert, dass sie Daten mit einer Geschwindigkeit von fünf Megabit pro Sekunde mit einer Übertragungslatenz von zwei Millisekunden über den Kanal übertragen.

InternetStackHelper

Jetzt sind unsere Knoten und Geräte konfiguriert, aber auf unseren Knoten sind keine Protokollstacks installiert. Die folgenden beiden Codezeilen kümmern sich darum.

InternetStackHelper stack;
stack.Install (nodes);

InternetStackHelper stellt einen topologischen Helfer für Internetstacks dar, ähnlich wie PointToPointHelper für Punkt-zu-Punkt-Netzwerkgeräte. Die Methode Installieren nimmt NodeContainer als Parameter. Bei der Ausführung installiert sie den Internetstack (TCP, UDP, IP usw.) auf jedem Knoten des Containers.

Ipv4AddressHelper

Dann müssen wir unsere Geräte mit IP-Adressen verknüpfen. Wir stellen einen topologischen Helfer zur Verwaltung der IP-Adressverteilung bereit. Die einzige für den Benutzer sichtbare API besteht darin, die Basis-IP-Adresse und die Netzwerkmaske festzulegen, die bei der tatsächlichen Adressverteilung verwendet werden (dies geschieht auf niedrigerer Ebene innerhalb des Helfers). Die folgenden beiden Codezeilen in unserem Beispielszenario first.cc,

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

Deklarieren ein Hilfsobjekt der Adresse und sagen ihm, dass es anfangen soll, IP-Adressen aus dem Netzwerk 10.1.1.0 zuzuweisen, wobei die Bitmaske 255.255.255.0 verwendet wird. Standardmäßig beginnen die zugewiesenen Adressen mit eins und erhöhen sich monoton, daher wird die erste aus dieser Basis zugewiesene Adresse 10.1.1.1 sein, dann 10.1.1.2 usw. In der Realität speichert das System ns-3 auf niedriger Ebene alle zugewiesenen IP-Adressen und erzeugt einen fatalen Fehler, wenn Sie versehentlich eine Situation schaffen, in der dieselbe Adresse zweimal generiert wird (übrigens ist es schwierig, diesen Fehler zu debuggen).

Die nächste Codezeile,

Ipv4InterfaceContainer interfaces = address.Assign (devices);

führt die tatsächliche Zuweisung der Adresse durch. In ns-3 stellen wir eine Verbindung zwischen der IP-Adresse und dem Gerät her, indem wir das Objekt Ipv4Interfaceverwenden. So wie wir manchmal eine Liste von Netzwerkteilen benötigen, die der Assistent für die spätere Verwendung erstellt hat, benötigen wir manchmal eine Liste von Objekten, die Ipv4Interface. Ipv4InterfaceContainer diese Funktionalität bereitstellt.

Wir haben ein Punkt-zu-Punkt-Netzwerk aufgebaut, mit installierten Stacks und zugewiesenen IP-Adressen. Jetzt benötigen wir in jedem Knoten Anwendungen zur Generierung von Verkehr.

4.2.7 Verwendung der Anwendung

Eine weitere grundlegende Abstraktion des Systems ns-3 ist die Anwendung (Anwendung). In diesem Szenario verwenden wir zwei Spezialisierungen der Basisklasse Anwendung ns-3, die UdpEchoServerApplication und UdpEchoClientApplication. Wie in den vorherigen Fällen verwenden wir Hilfsobjekte zur Konfiguration und Verwaltung der Basisklassenobjekte. Hier verwenden wir UdpEchoServerHelper und UdpEchoClientHelperObjekte, um unser Leben einfacher zu machen.

UdpEchoServerHelper

Die folgenden Codezeilen in unserem Beispielskript first.cc werden zur Konfiguration der UDP-Echo-Serveranwendung auf einem der zuvor erstellten Knoten verwendet.

UdpEchoServerHelper echoServer (9);

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

Die erste Zeile des obigen Codeabschnitts erstellt UdpEchoServerHelper. Wie gewohnt handelt es sich hierbei nicht um eine Anwendung an sich, sondern um ein Objekt, das uns hilft, echte Anwendungen zu erstellen. Eine unserer Vereinbarungen ist es, die erforderlichen Attribute an den Konstruktor des Hilfsobjekts weiterzugeben. In diesem Fall kann der Assistent nichts Nützliches tun, wenn ihm die Portnummer, an der der Server auf Pakete wartet, nicht zur Verfügung gestellt wird; diese Nummer muss auch dem Client bekannt sein. In diesem Fall übergeben wir die Portnummer an den Konstruktor des Assistenten. Der Konstruktor führt seinerseits einfach aus SetAttribute mit dem übergebenen Wert. Später können Sie bei Bedarf mit SetAttribute einen anderen Wert für das Attribut „Port“ festlegen.

Ähnlich wie viele andere Hilfsobjekte hat das Objekt UdpEchoServerHelper eine Methode Installieren. Die Ausführung dieser Methode führt tatsächlich dazu, dass eine grundlegende Echo-Server-Anwendung erstellt und an einen Knoten gebunden wird. Interessanterweise verwendet die Methode Installieren das gleiche NodeContainter einen Parameter, genau wie andere Installieren Methoden, die wir gesehen haben.

Die implizite C++-Umwandlung, die hier arbeitet, nimmt das Ergebnis der Methode node.Get (1) (die einen Smart Pointer auf das Knotenobjekt – Ptr) zurückgibt und verwendet ihn im Konstruktor für das anonyme Objekt NodeContainer, das dann an die Methode Installierenübergeben wird. Wenn Sie im C++-Code nicht bestimmen können, mit welcher Signatur die Methode kompiliert und ausgeführt wird, suchen Sie nach den impliziten Umwandlungen.

Nun sehen wir, dass echoServer.Install vorbereitet ist, die Anwendung UdpEchoServerApplication auf dem in NodeContainergefundenen Knoten zu installieren, den wir zur Verwaltung unserer Knoten verwenden, Knoten mit Index 1. Die Methode Installieren gibt den Container zurück, der Zeiger auf alle Anwendungen enthält (in diesem Fall eine, da wir ein anonymes NodeContainer, das einen Knoten enthält, übergeben haben), erstellt vom Assistenten.

Die Anwendungen müssen den Zeitpunkt für den Start der Verkehrsgenerierung angeben „start“ und ggf. auch die Zeit angeben, wann sie gestoppt werden soll „stop“. Wir geben beide Parameter an. Diese Zeiten werden mit den Methoden ApplicationContainer Start und Stopfestgelegt. Diese Methoden nehmen Parameter vom Typ Zeit. In diesem Fall verwenden wir eine explizite C++-Umwandlungssequenz, um C++ double 1.0 und es in ein Objekt tns‑3 Time umwandeln, das das Objekt Seconds verwendet, um in Sekunden zu konvertieren. Denken Sie daran, dass die Umwandlungsregeln vom Autor des Modells kontrolliert werden können und C++ eigene Regeln hat, sodass Sie nicht immer darauf vertrauen können, dass die Parameter so umgewandelt werden, wie Sie es erwarten. Zwei Zeilen,

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

führt dazu, dass die Echo-Serveranwendung (automatisch) nach einer Sekunde nach Beginn der Simulation gestartet wird und nach zehn Sekunden Simulation stoppt (deaktiviert wird). Da wir ein Simulationsereignis (Ereignis zum Stoppen der Anwendung) deklariert haben, das in zehn Sekunden ausgeführt wird, wird mindestens zehn Sekunden Netzwerkbetrieb simuliert.

UdpEchoClientHelper

Die Clientanwendung echo wird in einer Weise konfiguriert, die praktisch identisch mit der des Servers ist. Es gibt ein grundlegendes Objekt UdpEchoClientApplication, das von
UdpEchoClientHelper.

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

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

Für den Echo-Client müssen jedoch fünf verschiedene Attribute gesetzt werden. Die ersten beiden Attribute werden beim Erstellen gesetzt. UdpEchoClientHelperWir übergeben die Parameter, die verwendet werden (innerhalb des Helfers), um die Attribute festzulegen. "RemoteAddress" und "RemotePort" in Übereinstimmung mit unserem Abkommen zur Übertragung der erforderlichen Parameter an den Konstruktor des Helfers.

Erinnern wir uns, dass wir verwendet haben Ipv4InterfaceContainer , um die IP-Adressen zu verfolgen, die wir unseren Geräten zugewiesen haben. Das Null-Interface im Schnittstellencontainer entspricht der IP-Adresse des Nullknotens im Knotencontainer. Das erste Interface im Schnittstellencontainer entspricht der IP-Adresse des ersten Knotens im Knotencontainer. Daher erstellen wir in der ersten Codezeile (oben) den Helfer und teilen ihm mit, dass die Remote-Adresse des Clients die IP-Adresse ist, die dem Knoten zugewiesen ist, auf dem sich der Server befindet. Wir geben auch an, dass die Pakete an den neunten Port gesendet werden sollen.

Das Attribut „MaxPackets“ informiert den Client über die maximale Anzahl von Paketen, die wir während der Simulation senden können. Das Attribut „Interval“ gibt dem Client an, wie lange er zwischen den Paketen warten soll, und das Attribut „PacketSize“ informiert den Client darüber, wie groß die Nutzlast des Pakets sein soll. Mit dieser Kombination von Attributen sagen wir dem Client, dass er ein 1024-Byte-Paket senden soll.

Wie im Fall des Echo-Servers setzen wir beim Echo-Client die Attribute Start und Stop, aber hier starten wir den Client eine Sekunde nach dem Einschalten des Servers (zwei Sekunden nach Beginn der Simulation).

4.2.8 Simulator

In diesem Schritt müssen wir die Simulation starten. Dies geschieht mit der globalen Funktion Simulator::Run.

Simulator::Run ();

Als wir zuvor die Methoden aufgerufen haben,

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

haben wir tatsächlich Ereignisse im Simulator für 1,0 Sekunden, 2,0 Sekunden und zwei Ereignisse für 10,0 Sekunden geplant. Nach dem Aufruf Simulator::Run, wird das System beginnen, die Liste der geplanten Ereignisse zu durchlaufen und sie auszuführen. Zuerst wird es das Ereignis nach 1,0 Sekunden auslösen, was die Anwendung des Echo-Servers aktiviert (dieses Ereignis kann wiederum viele andere Ereignisse planen). Dann wird es das Ereignis auslösen, das für t = 2,0 Sekunden geplant ist, was die Anwendung des Echo-Clients startet. Auch dieses Ereignis kann viele weitere Ereignisse planen. Die Umsetzung des Startereignisses im Echo-Client wird die Phase der Datenübertragung in der Simulation einleiten, indem sie ein Paket an den Server sendet.

Der Akt des Sendens eines Pakets an den Server wird eine Kette von Ereignissen auslösen, die im Hintergrund automatisch geplant werden und die Mechanik des Sendens von Echo-Signalen gemäß den Synchronisationsparametern umsetzen, die wir im Skript festgelegt haben.

Insgesamt, da wir nur ein Paket senden (denken wir daran, dass das Attribut MaxPackets auf eins gesetzt wurde), wird die Kette von Ereignissen, die durch diese einzige Client-Echo-Anfrage initiiert wurde, enden, und die Simulation wird in den Wartemodus gehen. Sobald dies geschieht, werden die verbleibenden geplanten Ereignisse die Ereignisse Stop für den Server und den Client sein. Wenn diese Ereignisse ausgeführt werden, wird es keine weiteren Ereignisse zur Verarbeitung geben und Simulator::Run die Kontrolle zurückgeben. Die Simulation ist abgeschlossen.

Nun bleibt nur noch das Aufräumen. Dies geschieht durch den Aufruf der globalen Funktion Simulator::Destroy. Da Funktionen von Helfern (oder Low-Level-Code von ns-3) aufgerufen wurden, die so organisiert sind, dass im Simulator Hooks eingefügt werden, um alle erstellten Objekte zu löschen. Sie müssen keine dieser Objekte selbst verfolgen – alles, was Sie tun müssen, ist, zu deaktivieren. Simulator::Destroy Die ns-3-System erledigt diese schwierige Arbeit für Sie. Die verbleibenden Zeilen unseres ersten ns-3-Skripts, first.cc, tun genau das:

Simulator::Destroy ();
return 0;
}

Wann stoppt der Simulator?

ns-3 ist ein diskreter Ereignissimulator (DE). In einem solchen Simulator ist jedes Ereignis mit der Zeit seiner Ausführung verbunden, und die Simulation läuft weiter, indem sie Ereignisse in der Reihenfolge ihrer Entstehung während der Simulation verarbeitet. Ereignisse können die Planung zukünftiger Ereignisse auslösen (zum Beispiel kann ein Timer sich selbst neu planen, um in der nächsten Zeiteinheit abzuschließen).

Anfangsereignisse werden normalerweise von einem Objekt ausgelöst, z. B. wird IPv6 die Dienste im Netzwerk, Nachbardanfragen usw. planen. Die Anwendung plant das erste Paketversandereignis usw. Wenn ein Ereignis verarbeitet wird, kann es null, ein oder mehrere Ereignisse generieren. Während der Simulation treten Ereignisse auf, indem sie einfach enden oder neue erzeugen. Die Simulation stoppt automatisch, wenn die Ereigniswarteschlange leer ist oder ein spezielles Ereignis entdeckt wird. Stop. Das Ereignis Stop wird durch die Funktion Simulator::Stop (Stoppe die Zeit).

Es gibt einen typischen Fall, in dem Simulator::Stop absolut notwendig ist, um die Simulation zu beenden: wenn es selbsttragende Ereignisse gibt. Selbsttragende (oder wiederkehrende) Ereignisse sind Ereignisse, die sich immer selbst neu planen. Folglich halten sie die Ereigniswarteschlange immer nicht leer. Es gibt viele Protokolle und Module, die wiederkehrende Ereignisse enthalten, wie z. B.:

• FlowMonitor – periodische Überprüfung auf verlorene Pakete;

• RIPng – periodische Übertragung von Routertabelle-Updates;

• usw.

In solchen Fällen Simulator::Stop ist notwendig, um die Simulation korrekt zu stoppen. Außerdem, wenn ns-3 im Emulationsmodus ist, wird RealtimeSimulator verwendet, um die Simulation mit den Uhren des Computers zu synchronisieren, und Simulator::Stop ist notwendig, um den Prozess zu stoppen.

Viele der im Lehrbuch enthaltenen Simulationsprogramme machen keinen Aufruf. Simulator::Stop Offensichtlich, da sie automatisch mit dem Erschöpfen der Ereignisse in der Warteschlange beendet werden. Diese Programme werden jedoch auch den Aufruf Simulator::Stop annehmen. Zum Beispiel plant der folgende zusätzliche Befehl im ersten Programmbildschirm eine explizite Beendigung in Sekunde 11:

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

Das obige wird das Verhalten dieses Programms tatsächlich nicht ändern, da diese spezifische Simulation natürlich nach 10 Sekunden endet. Aber wenn Sie die Stopptime im obigen Befehl von 11 Sekunden auf 1 Sekunde ändern würden, würden Sie feststellen, dass die Simulation stoppt, bevor eine Ausgabe auf dem Bildschirm erscheint (da die Ausgabe etwa 2 Sekunden Simulationszeit benötigt).

Es ist wichtig, Simulator::Stop vor dem Aufruf von Simulator::Run aufzurufen; andernfalls kann Simulator::Run niemals die Kontrolle an das Hauptprogramm zurückgeben, um die Beendigung durchzuführen!

4.2.9 Bereitstellung Ihres Szenarios

Wir haben das Erstellen Ihrer einfachen Skripte trivial gemacht. Alles, was Sie tun müssen, ist, Ihr Skript in das Verzeichnis scratch zu legen, und es wird automatisch zusammengebaut, wenn Sie starten Waf. Lass es uns versuchen. Gehen Sie zurück in das Hauptverzeichnis und kopieren Sie examples/tutorial/first.cc in das Verzeichnis scratch

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

Jetzt bauen Sie Ihr erstes Beispielskript mit waf:

$ ./waf

Sie sollten Meldungen sehen, dass Ihr erstes Beispiel erfolgreich erstellt wurde.

Waf: Betrete Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
[614/708] cxx: scratch/myfirst.cc -> build/debug/scratch/myfirst_3.o
[706/708] cxx_link: build/debug/scratch/myfirst_3.o -> build/debug/scratch/myfirst
Waf: Verlasse Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' erfolgreich abgeschlossen (2.357s)

Jetzt können Sie das Beispiel ausführen (beachten Sie, dass, wenn Sie Ihr Programm im Verzeichnis scratch bauen, Sie es auch ausführen müssen aus scratch):

$ ./waf --run scratch/myfirst

Sie sollten eine ähnliche Ausgabe sehen:

Waf: Betrete 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 abgeschlossen (0.418s) 1024 Bytes an 10.1.1.2 gesendet
1024 Bytes von 10.1.1.1 empfangen
1024 Bytes von 10.1.1.2 empfangen

Hier sehen Sie, dass das Build-System prüft, ob die Datei erstellt wurde, und sie dann ausführt. Sie sehen den Komponenten-Log auf dem Echo-Client, der anzeigt, dass er ein 1024-Byte-Paket an den Echo-Server 10.1.1.2 gesendet hat. Sie sehen auch den Logging-Komponenten auf dem Echo-Server, der sagt, dass er 1024 Byte von 10.1.1.1 erhalten hat. Der Echo-Server gibt das Paket stillschweigend zurück, und im Log des Echo-Clients sehen Sie, dass er sein Paket vom Server zurückerhalten hat.

4.3 ns-3 Quellcode

Jetzt, wo Sie einige der ns-3-Hilfsprogramme verwendet haben, können Sie sich den Quellcode ansehen, der diese Funktionalität implementiert. Den aktuellsten Code finden Sie auf unserem Webserver unter folgendem Link: https://gitlab.com/nsnam/ns-3-dev.git. Dort sehen Sie eine Zusammenfassungsseite von Mercurial für unseren ns-3-Entwicklungsbaum. Oben auf der Seite finden Sie einige Links,

summary | shortlog | changelog | graph | tags | files

Gehen Sie weiter und wählen Sie den Link zu den Dateien. So wird die oberste Ebene der meisten unserer Repositories aussehen:

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

Unsere Beispielskripte befinden sich im Verzeichnis examples. Wenn Sie auf die Beispiele klicken, sehen Sie eine Liste von Unterverzeichnissen. Eine der Dateien im Unterverzeichnis tutorial — first.cc. Wenn Sie auf klicken, first.cc sehen Sie den Code, den Sie gerade studiert haben.

Der Quellcode befindet sich hauptsächlich im Verzeichnis src. Sie können den Quellcode einsehen, indem Sie auf den Verzeichnisnamen klicken oder auf den Link Dateien rechts neben dem Verzeichnisnamen. Wenn Sie auf das Verzeichnis src klicken, erhalten Sie eine Liste der Unterverzeichnisse von src. Wenn Sie dann auf das Unterverzeichnis core klicken, finden Sie eine Liste von Dateien. Die erste Datei, die Sie sehen werden (zum Zeitpunkt des Verfassens dieses Leitfadens) — abort.h. Wenn Sie auf den Link abort.h, werden Sie zur Quelldatei für abort.h, die nützliche Makros zum Verlassen von Skripten enthält, wenn abnormale Bedingungen festgestellt werden. Der Quellcode für die Hilfsprogramme, die wir in diesem Kapitel verwendet haben, finden Sie im Verzeichnis src/Applications/helper. Zögern Sie nicht, im Verzeichnisbaum zu stöbern, um zu verstehen, was wo ist und den Stil von ns-3 zu durchdringen.

Quelle: habr.com

Zuverlässiges Hosting für Websites mit DDoS-Schutz kaufen, VPS VDS Server 🔥 Zuverlässiges Hosting für Websites mit DDoS-Schutz kaufen, VPS VDS Server - ProHoster