Tutorial zum ns-3 Netzwerk-Simulator. Kapitel 4

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

4 Überblick über das Konzept
4.1 Schlüsselabstraktionen
4.1.1 Knoten
4.1.2 Anwendung
4.1.3 Kanal
4.1.4 Netzgerät
4.1.5 Topologische Hilfsmittel
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 topologischer Hilfsmittel
4.2.7 Verwendung der Anwendung
4.2.8 Simulator
4.2.9 Aufbau Ihres Szenarios
4.3 ns-3 Quellcode

Kapitel 4

Überblick über das Konzept

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

4.1 Schlüsselabstraktionen

In diesem Abschnitt werden einige Begriffe behandelt, die allgemein im Netzwerk verwendet werden, aber eine spezifische Bedeutung in ns-3 haben.

4.1.1 Knoten

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

In ns-3 wird die grundlegende Abstraktion eines Rechenressourcengerätes als Knoten bezeichnet. Diese Abstraktion wird in C++ durch die Klasse Node dargestellt. Die Klasse NodeNode (Knoten) bietet Methoden zur Verwaltung der Repräsentationen von Rechenressourcengeräten in Simulationen.

Sie sollten verstehen, Knoten wie ein Computer, dem Sie Funktionalitäten hinzufügen werden. Sie fügen Elemente wie Anwendungen, Protokollstacks und Peripheriekarten mit Treibern hinzu, die es dem Computer ermöglichen, nützliche Aufgaben zu erfüllen. Wir verwenden dasselbe Grundmodell in ns-3.

4.1.2 Anwendung

In der Regel wird Software in zwei Hauptkategorien unterteilt. Systemsoftware organisiert verschiedene Computerressourcen wie Speicher, CPU-Zyklen, Festplatte, Netzwerk usw. entsprechend einem bestimmten Rechenmodell. Systemsoftware nutzt diese Ressourcen normalerweise nicht zur Ausführung von Aufgaben, die dem Benutzer unmittelbaren Nutzen bringen. Um ein bestimmtes Ziel zu erreichen, startet der Benutzer normalerweise eine Anwendung, die die von der Systemsoftware verwalteten Ressourcen abruft und nutzt.

Oft wird die Grenze zwischen Systemsoftware und Anwendungssoftware durch den Wechsel der Berechtigungsstufen gezogen, der bei Betriebssystemaufrufen erfolgt. In ns‑3 gibt es kein echtes Konzept eines Betriebssystems und dementsprechend keine Begriffe für Berechtigungsstufen oder Systemaufrufe. Wir haben jedoch die Idee einer Anwendung. Ähnlich wie in der „realen Welt“ arbeiten Anwendungsprogramme auf Computern zur Ausführung von Aufgaben, arbeiten ns‑3-Anwendungen auf ns‑3-Knoten zur Steuerung von Simulationen in einer simulierten Welt.

In ns-3 ist die grundlegende Abstraktion für ein Benutzerprogramm, das aktivität zur Simulation erzeugt, die Anwendung. Diese Abstraktion wird in C++ durch die Klasse Application (Anwendung) dargestellt. Die Klasse Application bietet Methoden zur Verwaltung der Darstellungen unserer Version von Anwendungen auf Benutzerebene in Simulationen. Von Entwicklern wird erwartet, dass sie die Klasse Application im Sinne der objektorientierten Programmierung spezialisieren, um neue Anwendungen zu erstellen. In diesem Handbuch verwenden wir die Spezialisierungen der Klasse Application, die genannt werden UdpEchoClientApplication und UdpEchoServerApplication. Wie zu erwarten war, bilden diese Anwendungen eine Reihe von Client/Server-Anwendungen, die zur Erzeugung und Echo-Simulation von Netzwerkpaketen verwendet werden.

4.1.3 Kanal

In der realen Welt können Computer mit einem Netzwerk verbunden werden. Oft werden die Umgebungen, durch die Daten in diesen Netzwerken übertragen werden, als Kanäle bezeichnet. Wenn Sie ein Ethernet-Kabel an eine 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 Hauptabstraktion des Kommunikationsunternetzes als Kanal bezeichnet und in C++ durch die Klasse Channel dargestellt.

Klasse ChannelChannel bietet Methoden zur Verwaltung der Interaktion von Objekten im Unternetz und zur Verbindung von Knoten damit. Kanäle können auch von Entwicklern im Sinne der objektorientierten Programmierung spezialisiert werden. Die Spezialisierung eines Kanals kann etwas Einfaches wie ein Kabel 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 verwenden wir spezialisierte Versionen des Kanals, die den Namen CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel und WifiChannelWifiChannel. CsmaChannel, zum Beispiel modelliert es eine Version eines Kommunikationsnetzwerks, das eine Kommunikationsumgebung mit mehreren Zugriffsberechtigungen und Trägersignalsteuerung implementiert. Das bietet uns eine Ethernet-ähnliche Funktionalität.

4.1.4 Netzgerät

Früher war es so, dass man, um einen Computer mit einem Netzwerk zu verbinden, ein bestimmtes Netzwerkkabel und ein Hardwaregerät kaufen musste, das in der PC-Terminologie als Peripheriekarte bekannt war, die im Computer installiert werden musste. Wenn auf der Peripheriekarte bestimmte Netzwerkfunktionen implementiert waren, wurden sie als Netzwerkinterfacekarten oder Netzwerkkarten bezeichnet. Heute werden die meisten Computer mit integriertem Netzwerkinterface-Equipment ausgeliefert, sodass die Benutzer diese nicht mehr als separate Geräte wahrnehmen.

Eine Netzwerkkarte funktioniert nicht ohne einen Treiber, der ihre Hardware steuert. In Unix (oder Linux) wird ein Teil der Peripheriegeräte als Gerät klassifiziert. Geräte werden über Gerätetreiber verwaltet, und Netzwerkgeräte (NICs) werden unter Verwendung von Netzwerktreibern verwaltet (Netzwerktreiber) und werden insgesamt als Netzwerkgeräte bezeichnet (Netzwerkgeräte). In Unix und Linux greifen Sie auf Netzwerkgeräte über Namen zu, wie zum Beispiel eth0.

In ns-3 umfasst die Abstraktion des Netzwerkgeräts sowohl den Softwaretreiber als auch die simulierte Hardware. Bei der Simulation wird das Netzwerkgerät im Knoten „installiert“, um ihm die Kommunikation mit anderen Knoten über Kanäle zu ermöglichen. Ähnlich wie bei einem echten Computer kann der Knoten über mehrere Geräte mit mehreren Kanälen verbunden sein. Netzwerkgeräte.

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

4.1.5 Topologische Hilfsmittel

In einem realen Netzwerk finden Sie Host-Computer mit hinzugefügten (oder integrierten) Netzwerkkarten. In ns-3 würden wir sagen, dass Sie Knoten mit verbundenen NetDevices sehen werden. In einem großen simulierten Netzwerk müssen Sie Verbindungen zwischen zahlreichen Objekten organisieren. Knoten, NetDevice und Kanal.

Da das Anschließen von NetDevices an Knoten, NetDevices an Kanäle, die Zuweisung von IP-Adressen usw. in ns-3 eine häufige Aufgabe ist, stellen wir sogenannte topologische Hilfsfunktionen zur Verfügung, um dies so einfach wie möglich zu machen. Zum Beispiel erfordert die Erstellung eines NetDevice viele Kernoperationen von ns-3: Sie müssen eine MAC-Adresse hinzufügen, dieses Netzwerkgerät einem Node zuweisen, den Protokollstapel des Knotens konfigurieren und dann das NetDevice an den Kanal anschließen. Noch mehr Operationen sind erforderlich, um mehrere Geräte an Mehrpunktkanäle anzuschließen und dann einzelne Netzwerke zu einem vereinten Netzwerk (Internetworks) zu verbinden. Wir bieten Hilfsobjekte für die Topologie an, die diese zahlreichen Operationen für Ihre Benutzerfreundlichkeit in ein einfach zu bedienendes Modell integrieren.

4.2 Erstes ns-3 Skript

Wenn Sie das System wie oben vorgeschlagen installiert haben, finden Sie die ns-3-Version im Verzeichnis namens repos in Ihrem Home-Verzeichnis. Wechseln Sie in das Verzeichnis release

Wenn Sie ein solches Verzeichnis nicht haben, haben Sie beim Erstellen der Release-Version von ns-3 das Ausgabeverzeichnis nicht 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 herstellt und ein Paket zwischen den Knoten überträgt. Lassen Sie uns dieses Skript Zeile für Zeile betrachten, indem wir first.cc in Ihrem bevorzugten Editor öffnen.

4.2.1 Boilerplate-Code
Die erste Zeile in der Datei ist die Mode-Zeile emacs. Sie informiert emacs über die Formatierungsbedingungen (Codierungsstil), die wir in unserem Quellcode verwenden werden.

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

Das ist immer eine recht umstrittene Frage, daher sollten wir klarstellen, um sie sofort aus dem Weg zu räumen. Das ns‑3-Projekt hat, wie die meisten größeren Projekte, einen Kodierungsstil 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-Kodierungsstandard einhalten, wie im Dokument doc/codingstd.txt oder auf der Projektwebsite beschrieben: https://www.nsnam.org/develop/contributing-code/coding-style/.

Wir empfehlen Ihnen, sich mit dem Aussehen des ns‑3-Codes vertraut zu machen und diesen Standard anzuwenden, wann immer Sie mit unserem Code arbeiten. Das gesamte Entwicklerteam und die Mitwirkenden haben nach einiger Diskussion zugestimmt. Die oben angegebene Emacs-Moduszeile vereinfacht das korrekte Formatieren, wenn Sie den Emacs-Editor verwenden.

Der ns‑3-Simulator wird unter der GNU General Public Licenselizenziert. Sie finden den entsprechenden rechtlichen GNU-Hinweis in jeder Datei des ns‑3-Distribution. Oft können Sie eine Urheberrechtsmitteilung für eine der beteiligten Institutionen im ns‑3-Projekt über dem GPL-Text und den unten angegebenen Autoren 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 Code beginnt mit einer Reihe von Include-Direktiven (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 Benutzern von Hochsprachen-Skripten zu helfen, die mit einer Vielzahl von Header-Dateien in der Systemumgebung umgehen müssen, gruppieren wir diese nach deren Verwendung in umfassende Module. Wir bieten eine einzige Header-Datei an, die alle innerhalb dieses Moduls verwendeten Header-Dateien rekursiv lädt. Anstatt zu suchen, welche Header-Datei Sie benötigen und möglicherweise die richtige Liste der Abhängigkeiten zu erhalten, ermöglichen wir Ihnen das Laden einer Gruppe von Dateien mit einem hohen Maß an Detailliertheit. Dies ist zwar nicht der effizienteste Ansatz, macht jedoch das Schreiben von Skripten erheblich einfacher.

Jede der inkludierten ns‑3-Dateien wird in einem Verzeichnis mit dem Namen ns3 abgelegt (unterverzeichnis der Erstellung), 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 der von Ihnen installierten Version finden werden. In der Auflistung dieses Verzeichnisses finden Sie eine Vielzahl von Header-Dateien. Wenn Sie den Build ausführen, WAF legt öffentliche Header-Dateien im Verzeichnis ns3 im Unterverzeichnis ab build/debug

Wenn Sie ein solches Verzeichnis nicht haben, haben Sie beim Erstellen der Release-Version von ns-3 das Ausgabeverzeichnis nicht 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, abhängig von Ihrer Konfiguration. WAF wird außerdem automatisch eine Header-Datei für den Zugriff auf alle öffentlichen Header-Dateien generieren. Da Sie selbstverständlich dieser Anleitung strikt folgen, haben Sie bereits

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

um das Projekt für Debug-Bauten mit Beispielen und Tests einzurichten. Außerdem haben Sie

$ ./waf

um das Projekt zu compilieren. Wenn Sie jetzt das Verzeichnis ../..//build/debug/ns3, durchsuchen, werden Sie unter anderem die Header-Dateien der vier oben genannten Module finden. Sie können den Inhalt dieser Dateien prü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 Namensraumdeklaration.

using namespace ns3;

Das ns-3-Projekt ist im C++-Namensraum ns3 implementiert. Dies gruppiert alle ns-3-bezogenen Deklarationen in einem Sichtbereich außerhalb des globalen Namensraums, was hoffentlich die Integration mit anderem Code erleichtert. Die Verwendung des C++-Operators bringt den ns-3-Namensraum in den aktuellen (globalen) Deklarationsbereich. Dies ist eine etwas umständliche Art zu sagen, dass Sie nach dieser Deklaration den ns3::Operator nicht mehr für den gesamten ns-3-Code verwenden müssen. Wenn Sie mit Namensräumen nicht vertraut sind, werfen Sie einen Blick in fast jedes C++-Lehrbuch und vergleichen Sie den ns3-Namensraum mit der Verwendung des std-Namensraums und Deklarationen. using namespace std; in den Beispielen für die Verwendung des Ausgaboperators 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 nutzen, um unser Dokumentationssystem zu besprechen. Doxygen. Wenn Sie die Website des ns‑3-Projekts besuchen, finden Sie einen Link zur "Dokumentation" in der Navigationsleiste. Wenn Sie auf diesen Link klicken, gelangen Sie zu unserer Dokumentationsseite. Dort gibt es einen Link zur "Letzten Version", der Sie zur Dokumentation der neuesten stabilen ns‑3-Version führt. Wenn Sie den Link zur "API Dokumentation" auswählen, gelangen Sie zur ns‑3 API-Dokumentationsseite.

Auf der linken Seite der Seite finden Sie eine grafische Darstellung der Dokumentationsstruktur. Ein guter Ausgangspunkt ist das "Buch" Module ns‑3 im ns‑3 Navigationsbaum. Wenn Sie diesen Abschnitt aufklappen, Module, sehen Sie eine Liste der ns‑3 Modul-Dokumentationen. Wie bereits erwähnt, steht das Konzept des Moduls hier in direktem Zusammenhang mit den im Modul enthaltenen Dateien. Das ns‑3 Logging-System wird im Abschnitt "Verwendung des Logging-Moduls"diskutiert, daher werden wir später in diesem Handbuch darauf zurückkommen. Sie können jedoch durch den oben genannten Abschnitt lernen, indem Sie das Modul Kernwählen und dann das Buch Debugging-Toolsöffnen und die Seite auswählen, Logging. Klicken Sie auf Logging.

Nun sollten Sie die Dokumentation Doxygen für das Modul Logging. In der Makrosliste oben auf der Seite finden Sie den Eintrag für NS_LOG_COMPONENT_DEFINE. Bevor Sie dem Link folgen, sollten Sie auf jeden Fall die „Detaillierte Beschreibung“ des Protokollmoduls ansehen, um seine Funktionsweise im Allgemeinen 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 vor sich geht, fahren Sie fort und schauen Sie sich die Dokumentation zu den spezifischen NS_LOG_COMPONENT_DEFINE an. Ich werde die Dokumentation hier nicht wiederholen, aber zusammenfassend gesagt, erklärt diese Zeile einen Protokollkomponenten mit dem Namen FirstScriptExample, der es erlaubt, die Konsolenprotokollierung von Nachrichten über den Namen zu aktivieren und zu deaktivieren.

4.2.5 Hauptfunktion

In den folgenden Zeilen des Skripts sehen Sie,

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

Das ist einfach die Deklaration der Hauptfunktion Ihres Programms (Skripts). Wie in jedem C++-Programm müssen Sie die Hauptfunktion definieren, die zuerst ausgeführt wird. Hier gibt es nichts Besonderes. Ihr ns-3-Skript ist einfach ein C++-Programm. Die nächste 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 genutzt werden kann (die kleinste darstellbare Differenz zwischen zwei Zeitwerten). Sie können die Auflösung genau einmal ändern. Der Mechanismus, der diese Flexibilität gewährleistet, benötigt Speicher, daher geben wir den Speicher frei, sobald die Auflösung explizit festgelegt wird, um weitere Aktualisierungen zu verhindern. (Wenn Sie die Auflösung nicht explizit festlegen, beträgt der Standardwert eine Nanosekunde, und der Speicher wird zu Beginn der Simulation freigegeben.)

Die folgenden zwei Zeilen des Skripts werden verwendet, um zwei Komponenten der Protokollierung zu aktivieren, die in die Anwendungen integriert sind. EchoClient und EchoServer:

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

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

Jetzt kommen wir direkt zur Erstellung der Topologie und zur Ausführung der Simulation. Wir werden topologische Hilfsobjekte verwenden, um diese Aufgabe so einfach wie möglich zu gestalten.

4.2.6 Verwendung topologischer Hilfsmittel

Die nächsten beiden Zeilen in unserem Skript erstellen tatsächlich ns-3 Node-Objekte, die Computer in der Simulation darstellen.

NodeContainer nodes;
nodes.Create(2);

Bevor wir fortfahren, lassen Sie uns die Dokumentation für die Klasse NodeContainerfinden. Eine andere Möglichkeit, zur Dokumentation dieser Klasse zu gelangen, ist über den Reiter Classes auf den Seiten. Doxygen. Wenn Sie Doxygen bereits geöffnet haben, scrollen Sie einfach nach oben zur oberen Seite und wählen Sie die Registerkarte Klassen. Sie sollten eine neue Reihe von Registerkarten sehen, wovon eine die Klassenliste ist. Unter dieser Registerkarte sehen Sie eine Liste aller ns‑3-Klassen. Scrollen Sie nach unten, bis ns3 :: NodeContainer. Wenn Sie die Klasse gefunden haben, wählen Sie sie aus, um zur Dokumentation für diese Klasse zu gelangen.

Wie wir uns erinnern, ist eine unserer wichtigsten Abstraktionen der Knoten. Er stellt den Computer dar, zu dem wir Dinge wie Protokollstacks, Anwendungen und Peripheriekarten hinzufügen möchten. Der topologische Assistent NodeContainer bietet eine benutzerfreundliche Möglichkeit, alle Objekte zu erstellen, zu verwalten und darauf zuzugreifen, Knoten, die wir zur Ausführung der Simulation erstellen. Die erste Zeile oben deklariert NodeContainer, die wir nodes nennen. Die zweite Zeile ruft die Methode Create für das Objekt nodes auf und bittet den Container, zwei Knoten zu erstellen. Wie beschrieben in Doxygen, fordert der Container das System ns‑3 auf, zwei Objekte zu erstellen, Knoten und speichert die Zeiger auf diese Objekte intern.

Die im Skript erstellten Knoten tun zunächst nichts. Der nächste Schritt beim Aufbau der Topologie besteht darin, unsere Knoten mit dem Netzwerk zu verbinden. Die einfachste Form des Netzwerks, die wir unterstützen, ist eine Punkt-zu-Punkt-Verbindung zwischen zwei Knoten. Wir werden jetzt eine solche Verbindung erstellen.

PointToPointHelper

Wir erstellen die Punkt-zu-Punkt-Verbindung, indem wir einem uns vertrauten Muster folgen und ein topologisches Hilfsobjekt verwenden, um die erforderliche Low-Level-Arbeit für die Verbindung zu erledigen. Denken wir daran, dass unsere beiden Schlüsselabstraktionen NetDevice und Kanal. In der realen Welt entsprechen diese Begriffe in etwa Peripheriekarten und Netzwerkkabeln. Diese beiden Dinge sind in der Regel eng miteinander verbunden, und niemand kann erwarten, dass beispielsweise Geräte Ethernet über eine drahtlose Verbindung kommunizieren. Unsere topologischen Hilfsobjekte folgen dieser engen Verbindung, und deshalb werden Sie in diesem Szenario ein Objekt verwenden PointToPointHelper zum Konfigurieren und Verbinden von ns-3 Objekten PointToPointNetDevice und PointToPointChannel. Die nächsten drei Zeilen im Skript:

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

Die erste Zeile,

PointToPointHelper pointToPoint;

erstellt im Stack eine Instanz des Objekts PointToPointHelper. Auf der obersten Ebene sieht die folgende Zeile aus,

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

sagt dem Objekt PointToPointHelper den Wert „5 Mbit/s“ (fünf Megabit pro Sekunde) als „DataRate».

Aus spezifischerer Sicht entspricht die Zeile „DataRate“ dem, was wir als Attribut bezeichnen PointToPointNetDevice. Wenn Sie sich Doxygen für die Klasse ns3::PointToPointNetDevice und in der Dokumentation zur Methode GetTypeId werden Sie eine Liste der Attribute finden, die für das Gerät definiert sind. Unter diesen wird ein Attribut „DataRate“ sein. Die meisten für den Benutzer sichtbaren ns-3 Objekte haben ähnliche Attributlisten. Wir verwenden diesen Mechanismus für eine einfache Anpassung der Simulation ohne erneute Kompilierung, wie Sie im folgenden Abschnitt sehen werden.

Ähnlich wie „DataRate“ im PointToPointNetDevice finden Sie das Attribut „Delay“, das mit dem 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 Verbreitungsverzögerung über den Punkt-zu-Punkt-Kanal, den er anschließend erstellt.

NetDeviceContainer

Bis jetzt haben wir in unserem Szenario NodeContainer, das zwei Knoten enthält. Wir haben PointToPointHelper, das bereit ist, Objekte zu erstellen PointToPointNetDevices und deren Verbindung über das Objekt PointToPointChannel. So wie wir den Hilfsobjekt NodeContainer zur Erstellung der Knoten verwendet haben, werden wir PointToPointHelper für uns die Arbeiten zur Erstellung, Konfiguration und Installation unserer Geräte durchführen lassen. Wir benötigen eine Übersicht aller erstellten Objekte, NetDevice, weshalb wir NetDeviceContainer zum Speichern nutzen, ebenso wie wir NodeContainer zum Speichern unserer erstellten Knoten verwendet haben. Die folgenden zwei Codezeilen,

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

vollenden die Einrichtung der Geräte und des Kanals. Die erste Zeile deklariert den bereits erwähnten Gerätekontainer, während die zweite die Hauptarbeit verrichtet. Die Methode Installieren des Objekts PointToPointHelper nimmt NodeContainer als Parameter. Innerhalb von NetDeviceContainer wird für jeden Knoten, der sich in NodeContainer befindet (für die Punkt-zu-Punkt-Verbindung müssen es genau zwei sein), PointToPointNetDevice erstellt und im Gerätekontainer gespeichert. PointToPointChannel wird erstellt, und es werden zwei PointToPointNetDevices. Nach der Erstellung der Objekte werden die in PointToPointHelpergespeicherten Attribute verwendet, um die entsprechenden Attribute in den erstellten Objekten zu initialisieren.

Nach dem Aufruf von pointToPoint.Install (nodes) Wir werden zwei Knoten haben, 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 und einer Übertragungsverzögerung von zwei Millisekunden ü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 Internet-Stacks dar, ähnlich wie PointToPointHelper für Punkt-zu-Punkt-Netzwerkgeräte. Die Methode Installieren nimmt NodeContainer als Parameter. Bei der Ausführung wird der Internet-Stack (TCP, UDP, IP usw.) auf jedem Knoten im Container installiert.

Ipv4AddressHelper

Dann müssen wir unsere Geräte mit IP-Adressen verknüpfen. Wir bieten einen Topologie-Helfer zur Verwaltung der Verteilung von IP-Adressen an. Die einzige für den Benutzer sichtbare API ist die Festlegung der Basis-IP-Adresse und der Subnetzmaske, die bei der tatsächlichen Verteilung der Adressen verwendet wird (dies geschieht auf einer niedrigeren Ebene innerhalb des Helfers). Die folgenden zwei Codezeilen in unserem Skriptbeispiel: first.cc,

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

deklarieren ein Hilfsobjekt für Adressen und geben an, dass es mit der Zuweisung von IP-Adressen aus dem Netzwerk 10.1.1.0 beginnen soll, wobei die Bitmaske 255.255.255.0 verwendet wird. Standardmäßig beginnen die zugewiesenen Adressen bei eins und erhöhen sich monoton, sodass die erste von dieser Basis zugewiesene Adresse 10.1.1.1 sein wird, gefolgt von 10.1.1.2 usw. In der Realität speichert das System ns-3 auf niedriger Ebene alle zugewiesenen IP-Adressen und generiert einen schwerwiegenden Fehler, wenn Sie versehentlich eine Situation geschaffen haben, in der dieselbe Adresse zweimal generiert wird (übrigens ist dieser Fehler schwer zu debuggen).

Die nächste Codezeile:

Ipv4InterfaceContainer interfaces = address.Assign (devices);

führt die tatsächliche Adresszuweisung aus. In ns‑3 stellen wir die Verbindung zwischen der IP-Adresse und dem Gerät über das Objekt Ipv4Interfaceher. Genau wie wir manchmal eine Liste der Netzgeräte benötigen, die vom Assistenten für die spätere Verwendung erstellt wurden, benötigen wir manchmal eine Liste der Objekte Ipv4Interface. Ipv4InterfaceContainer die diese Funktionalität bereitstellt.

Wir haben ein Punkt-zu-Punkt-Netzwerk mit installierten Stacks und zugewiesenen IP-Adressen aufgebaut. Jetzt benötigen wir in jedem Knoten Anwendungen, um Traffic zu generieren.

4.2.7 Verwendung der Anwendung

Eine weitere der grundlegenden Abstraktionen im System ns‑3 ist Application (Anwendung). In diesem Szenario verwenden wir zwei Spezialisierungen der Basisklasse Application ns‑3, die genannt werden UdpEchoServerApplication und UdpEchoClientApplication. Wie in den vorherigen Fällen verwenden wir Hilfsobjekte zur Konfiguration und Verwaltung der Basiskomponenten. Hier verwenden wir UdpEchoServerHelper und UdpEchoClientHelperObjekte, um unser Leben zu erleichtern.

UdpEchoServerHelper

Die folgenden Codezeilen in unserem Beispielskript first.cc werden verwendet, um die UDP-Echo-Serveranwendung auf einem der Knoten, die wir zuvor erstellt haben, einzurichten.

UdpEchoServerHelper echoServer (9);

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

Die erste Codezeile im obigen Fragment erstellt UdpEchoServerHelper. Wie üblich handelt es sich nicht um eine eigenständige Anwendung, sondern um ein Objekt, das uns hilft, reale Anwendungen zu entwickeln. Eine unserer Vereinbarungen besteht darin, die erforderlichen Attribute an den Konstruktor des Hilfsobjekts zu übermitteln. In diesem Fall kann der Hilfsobjekt nichts Nützliches tun, wenn ihm keine Portnummer zur Verfügung gestellt wird, an dem der Server auf Pakete warten wird; diese Nummer muss auch dem Client bekannt sein. Daher übergeben wir die Portnummer an den Konstruktor des Hilfsobjekts. Der Konstruktor selbst führt einfach SetAttribute mit dem übergebenen Wert aus. Später können Sie, falls gewünscht, mit SetAttribute einen anderen Wert für das Attribut "Port" festlegen.

Wie viele andere Hilfsobjekte hat auch 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 hat die Methode Installieren nimmt NodeContainer als Parameter genauso wie andere Installieren Methoden, die wir gesehen haben.

Die implizite Umwandlung in C++, die hier funktioniert, nimmt das Ergebnis der Methode node.Get (1) (der einen smarten Zeiger auf das Knotenobjekt zurückgibt — Ptr) und verwendet ihn im Konstruktor für das anonyme Objekt NodeContainer, das dann an die Methode übergeben wird Installieren. Wenn Sie im C++-Code nicht feststellen können, welche Signatur eine Methode kompiliert und ausgeführt wird, suchen Sie nach impliziten Umwandlungen.

Jetzt sehen wir, dass echoServer.Install die Anwendung installieren wird UdpEchoServerApplication auf dem gefundenen in NodeContainer, den wir verwenden, um unsere Knoten zu steuern, Knoten mit dem 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) erstellt von dem Helfer.

Die Anwendungen müssen den Zeitpunkt des Startens der Traffic-Generierung angeben "start" und möglicherweise zusätzlich die Zeit angeben, wann diese gestoppt werden soll "stop". Wir stellen beide Parameter zur Verfügung. Diese Zeiten werden mit den Methoden ApplicationContainer Start und Stop. Diese Methoden akzeptieren Parameter vom Typ Zeit. In diesem Fall verwenden wir eine explizite Sequenz von C++-Umwandlungen, um C++ double 1,0 und es in ein tns‑3-Zeitobjekt umwandeln, das das Objekt Seconds zur Umrechnung in Sekunden verwendet. Beachten Sie, dass die Umwandlungsregeln vom Modellautor steuert werden können und C++ seine eigenen Regeln hat, daher können Sie nicht immer darauf zählen, dass die Parameter wie erwartet umgewandelt werden. Zwei Zeichenfolgen,

serverApps.Start (Seconds (1,0));
serverApps.Stop (Seconds (10,0));

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

UdpEchoClientHelper

Client-Anwendung echo wird auf eine Weise konfiguriert, die praktisch der des Servers entspricht. Es gibt ein Basiselement UdpEchoClientApplication, das verwaltet wird 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 wir jedoch fünf verschiedene Attribute festlegen. Die ersten beiden Attribute werden während der Erstellung gesetzt. UdpEchoClientHelperWir übergeben die Parameter, die (innerhalb des Assistenzsystems) zur Festlegung der Attribute verwendet werden. „RemoteAddress“ und „RemotePort“ entsprechend unserer Vereinbarung zur Übertragung der erforderlichen Parameter an den Assistenten-Konstruktor.

Erinnern wir uns daran, dass wir Ipv4InterfaceContainer verwendet haben, um die IP-Adressen zu verfolgen, die unseren Geräten zugewiesen wurden. Das Null-Interface im Interface-Container entspricht der IP-Adresse des Null-Knotens im Knoten-Container. Das erste Interface im Interface-Container entspricht der IP-Adresse des ersten Knotens im Knoten-Container. Also, in der ersten Zeile des Codes (oben) erstellen wir den Assistenten und teilen ihm mit, dass die Remote-Adresse des Clients die IP-Adresse ist, die dem Knoten zugewiesen wurde, auf dem der Server läuft. Wir geben auch an, dass die Pakete an den neunten Port gesendet werden sollen.

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

Wie beim Echo-Server setzen wir die Attribute für den Echo-Client. 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

An dieser Stelle müssen wir die Simulation starten. Dies geschieht mithilfe der globalen Funktion Simulator::Run.

Simulator::Run ();

Als wir zuvor 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, das System beginnt, die Liste der geplanten Ereignisse zu durchlaufen und sie auszuführen. Zuerst wird es ein Ereignis nach 1,0 Sekunden auslösen, was die Echo-Server-Anwendung aktiviert (dieses Ereignis kann wiederum viele andere Ereignisse planen). Dann wird es das für t = 2,0 Sekunden geplante Ereignis initiieren, das die Echo-Client-Anwendung startet. Auch dieses Ereignis kann viele weitere Ereignisse planen. Die Implementierung des Startereignisses im Echo-Client wird die Phase der Datensimulation 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 automatisch im Hintergrund geplant werden und die Mechanik des Sendens von Echo-Signalen gemäß den Synchronisationsparametern implementieren, die wir im Skript festgelegt haben.

Letztendlich, da wir nur ein Paket senden (denken wir daran, dass das Attribut MaxPackets auf eins gesetzt war), wird die durch diese einzige Client-Echo-Anfrage eingeleitete Ereigniskette enden und die Simulation in den Wartemodus übergehen. Sobald dies geschieht, werden die verbleibenden geplanten Ereignisse die Ereignisse sein Stop für den Server und den Client. Sobald diese Ereignisse eintreten, werden keine weiteren Ereignisse zur Verarbeitung mehr vorhanden sein und Simulator::Run über die Kontrolle zurückgegeben. Die Modellierung ist abgeschlossen.

Nun bleibt nur noch, aufzuräumen. Dies erfolgt durch den Aufruf einer globalen Funktion Simulator::Destroy. Da Hilfsfunktionen (oder niedrigstufiger ns-3-Code) aufgerufen wurden, die so organisiert sind, dass im Simulator Hooks zum Zerstören aller erstellten Objekte eingefügt werden, müssen Sie keine dieser Objekte selbst verfolgen — alles, was Sie tun mussten, war Simulator::Destroy und das Programm zu beenden. Das ns-3-System erledigt diese mühsame Arbeit für Sie. Die verbleibenden Zeilen unseres ersten ns-3-Skripts, first.cc, tun genau dies:

Simulator::Destroy ();
rückgabe 0;
}

Wann wird der Simulator gestoppt?

ns-3 ist ein Simulationsprogramm für diskrete Ereignisse (DE). In einem solchen Simulator ist jedes Ereignis mit der Zeit seiner Ausführung verbunden, und die Simulation wird fortgesetzt, indem die Ereignisse in der Reihenfolge ihres Auftretens während der Simulation verarbeitet werden. Ereignisse können zukünftige Ereignisse planen (z. B. kann ein Timer sich selbst umplanen, um den nächsten Intervall abzuschließen).

Ereignisse werden typischerweise durch ein Objekt initiiert, wie zum Beispiel IPv6, das die Erkennung von Diensten im Netzwerk, Nachbquire-Anfragen usw. plant. Die Anwendung plant das erste Paketversandereignis usw. Wenn ein Ereignis bearbeitet wird, kann es null, ein oder mehrere Ereignisse generieren. Im Verlauf der Simulation treten Ereignisse auf, die einfach enden oder neue erzeugen. Die Simulation stoppt automatisch, wenn die Ereigniswarteschlange leer ist oder ein spezielles Ereignis erkannt wird. StopEreignis Stop wird durch die Funktion Simulator::Stop (Zeit stoppen).

Ein typischer Fall, in dem Simulator::Stop unbedingt erforderlich ist, um die Simulation zu stoppen, ist, wenn selbstunterhaltende Ereignisse vorliegen. Selbstunterhaltende (oder wiederkehrende) Ereignisse sind solche, die immer neu geplant werden. Infolgedessen halten sie die Ereigniswarteschlange immer nicht leer. Es gibt viele Protokolle und Module, die wiederkehrende Ereignisse enthalten, zum Beispiel:

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

• RIPng — periodische Übertragung von Routing-Tabellenaktualisierungen;

• usw.

In solchen Fällen Simulator::Stop ist notwendig, um die Simulation korrekt zu stoppen. Darüber hinaus wird, wenn ns‑3 im Emulationsmodus ist, der RealtimeSimulator verwendet, um die Simulationsuhren mit den Maschinenuhren zu synchronisieren, und Simulator::Stop ist erforderlich, um den Prozess zu beenden.

Viele der Simulationsprogramme im Lehrbuch rufen nicht Simulator::Stop explizit auf, da sie automatisch mit dem Erschöpfen der Ereignisse in der Warteschlange enden. Diese Programme werden jedoch auch den Aufruf von Simulator::Stop annehmen. Zum Beispiel plant der folgende zusätzliche Befehl im ersten Programmmuster eine explizite Beendigung nach 11 Sekunden:

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

Die obige Anweisung ändert das Verhalten dieses Programms nicht, da diese spezifische Simulation natürlich nach 10 Sekunden endet. Wenn Sie jedoch die Zeit für das Stoppen in der obigen Anweisung von 11 Sekunden auf 1 Sekunde geändert haben, würden Sie bemerken, dass die Simulation stoppt, bevor irgendeine Ausgabe auf dem Bildschirm erscheint (da die Ausgabe etwa 2 Sekunden Simulationszeit benötigt).

Es ist wichtig, Simulator::Stop vor Simulator::Run aufzurufen; andernfalls kann Simulator::Run die Kontrolle über das Hauptprogramm zur Durchführung der Stoppfunktion möglicherweise nie zurückgeben!

4.2.9 Aufbau Ihres Szenarios

Wir haben die Erstellung Ihrer einfachen Skripte ganz einfach gemacht. Alles, was Sie tun müssen, ist, Ihr Skript in das Verzeichnis scratch zu legen, und es wird automatisch erstellt, wenn Sie ausführen WAF. Lassen Sie es uns versuchen. Kehren Sie in das übergeordnete Verzeichnis zurück und kopieren Sie examples/tutorial/first.cc in das Verzeichnis scratch

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

Jetzt kompilieren Sie Ihr erstes Beispielszenario mit waf:

$ ./waf

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

Waf: Betritt 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: Verließt Verzeichnis `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' wurde erfolgreich abgeschlossen (2,357s)

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

$ ./waf --run scratch/myfirst

Sie sollten eine ähnliche 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 beendet (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 überprüft, ob die Datei kompiliert wurde, und sie dann ausführt. Sie sehen den Eintrag des Clients im Echo-Center, der anzeigt, dass er ein 1024-Byte-Paket an den Echo-Server 10.1.1.2 gesendet hat. Sie sehen auch den Protokolleintrag auf dem Echo-Server, der angibt, dass er 1024 Bytes von 10.1.1.1 empfangen hat. Der Echo-Server sendet das Paket stillschweigend zurück, und Sie sehen im Protokoll des Echo-Clients, dass er sein Paket vom Server zurückerhalten hat.

4.3 ns-3 Quellcode

Jetzt, da Sie einige der Helfer von ns‑3 verwendet haben, können Sie einen Blick auf den Quellcode werfen, der diese Funktionalität implementiert. Der aktuellste Code kann auf unserem Webserver unter folgendem Link eingesehen werden: https://gitlab.com/nsnam/ns-3-dev.git. Dort sehen Sie eine Zusammenfassungsseite von Mercurial für unser Entwicklungsbaum von ns‑3. Am oberen Rand der Seite finden Sie einige Links,

Zusammenfassung | Kurzprotokoll | Änderungsprotokoll | Graph | Tags | Dateien

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

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

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 darauf klicken, first.cc sehen Sie den Code, den Sie gerade gelernt 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 von Unterverzeichnissen des src. Wenn Sie dann auf das Unterverzeichnis core klicken, finden Sie eine Liste von Dateien. Die erste Datei, die Sie sehen werden (zum Zeitpunkt der Erstellung dieses Handbuchs) — abort.h. Wenn Sie auf den Link klicken abort.h, werden Sie zur Quelldatei für abort.h, die nützliche Makros enthält, um aus Skripten auszutreten, wenn abnormalen Bedingungen erkannt werden. Der Quellcode für die Hilfsdienste, die wir in diesem Kapitel verwendet haben, befindet sich im Verzeichnis src/Applications/helper. Zögern Sie nicht, im Verzeichnisbaum zu stöbern, um zu verstehen, wo was ist, und sich im Programmstil von ns-3 zurechtzufinden.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster