🥇 Tutorial for the ns-3 network simulator. Chapter 3 | ProHoster

🥇 Tutorial for the ns-3 network simulator. Chapter 3 | ProHoster
chapters 1, 2
Chapter 3

4 Overview of the Concept
4.1 Key Abstractions
4.1.1 Node
4.1.2 Application
4.1.3 Channel
4.1.4 Net Device
4.1.5 Topological Helpers
4.2 First ns-3 Script
4.2.1 Boilerplate Code
4.2.2 Plug-in Modules
4.2.3 Namespace ns3
4.2.4 Logging
4.2.5 Main Function
4.2.6 Using Topological Helpers
4.2.7 Using Application
4.2.8 Simulator
4.2.9 Building Your Scenario
4.3 ns-3 Source Code

Chapter 4

Overview of the Concept

The first thing we need to do before we start studying or writing ns-3 code is to explain several key concepts and abstractions within the system. Much of this may seem obvious to some, but we recommend taking the time to read this section to ensure you're starting on a solid foundation.

4.1 Key Abstractions

In this section, we will look at some terms that are commonly used in networking but have specific meanings in ns-3.

4.1.1 Node

In internet jargon, a computer device that connects to a network is called a host or sometimes an end system. Since ns-3 is a network simulator rather than an internet simulator, we intentionally avoid using the term host, as it is closely associated with the internet and its protocols. Instead, we use a more general term, also employed by other simulators, which originates in graph theory — node (node).

In ns-3, the basic abstraction of a computing device is called a node. This abstraction is represented in C++ by the Node class. The class NodeNode (node) provides methods for managing the representations of computing devices in simulations.

You should think of it Node as a computer to which you will add functionalities. You will add things like applications, protocol stacks, and peripheral cards with drivers that allow the computer to perform useful tasks. We use the same basic model in ns-3.

4.1.2 Application

Generally, computer software is divided into two broad classes. System software organizes various computer resources such as memory, CPU cycles, disk, network, etc., according to a certain computational model. System software typically does not utilize these resources for tasks that provide direct benefits to the user. To achieve a specific goal, the user usually launches an application that accesses and uses resources managed by the system software.

Often, the dividing line between system and application software is drawn at the level of privilege changes that occur in operating system traps. In ns-3, there is no real concept of an operating system and, consequently, no notions of privilege levels or system calls. However, we do have the concept of an application. Just like in the 'real world', to perform tasks, software applications run on computers; ns-3 applications run on ns-3 nodes to manage simulations in the simulated world.

In ns-3, the base abstraction for a user program that generates some activity for modeling is an application. This abstraction is represented in C++ by the Application class. The Application class provides methods for managing the representations of our version of user-level applications in simulations. Developers are expected to specialize the Application class in terms of object-oriented programming to create new applications. In this guide, we will use specializations of the Application class called UdpEchoClientApplication and UdpEchoServerApplication. As expected, these applications form a set of client/server applications used for generating and echo-simulating network packets.

4.1.3 Channel

In the real world, you can connect a computer to a network. The environments through which data is transmitted in these networks are often referred to as channels. When you connect an Ethernet cable to a wall socket, you are connecting the computer to the Ethernet communication channel. In the simulated world of ns-3, a node connects to an object representing the communication channel. Here, the main abstraction of the communication subnet is called a channel and is represented in C++ by the Channel class.

Class ChannelChannel provides methods for managing the interaction of subnet objects and connecting nodes to them. Channels can also be specialized by developers in the context of object-oriented programming. Channel specialization can model something as simple as a wire. A specialized channel can also model complex items such as a large Ethernet switch or a three-dimensional space filled with obstacles in the case of wireless networks.

In this guide, we will use specialized versions of the channel called CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel and WifiChannelWifiChannel. CsmaChannel, for example, models a version of the communication subnet that implements a multiple access communication environment with carrier sensing. This gives us Ethernet-like functionality.

4.1.4 Net Device

Previously, if you wanted to connect a computer to a network, you had to buy a specific network cable and a hardware device, called (in PC terminology) a peripheral card, which needed to be installed in the computer. If certain network functions were implemented on the peripheral card, they were referred to as network interface cards or network adapters. Today, most computers come with integrated network interface hardware, and users do not see them as separate devices.

A network adapter will not work without a software driver that controls its hardware. In Unix (or Linux), part of the peripheral hardware is classified as a device. Devices are managed by device drivers, and network devices (NICs) are controlled using network device drivers (network device drivers) and are collectively referred to as network devices (net devices). In Unix and Linux, you refer to network devices by names such as eth0.

In ns-3, the abstraction of a network device encompasses both the software driver and the modeled hardware. During simulation, a network device is 'installed' in a node to allow it to connect with other nodes via channels. Just like in a real computer, a node can be connected to multiple channels through several devices. NetDevices.

The network device abstraction is represented in C++ by the class NetDevice. The class NetDevice provides methods for managing connections with Node and Channel objects, and can be specialized by developers in terms of object-oriented programming. In this guide, we will use several specialized versions of NetDevice called CsmaNetDevice, PointToPointNetDevice and WifiNetDevice. Just as an Ethernet network adapter is designed to work with the Ethernet, CsmaNetDevice is intended to work with CsmaChannel, PointToPointNetDevice is intended to work with PointToPointChannel, and WifiNetDevice — designed to work with WifiChannel.

4.1.5 Topological Helpers

In a real network, you would find host computers with added (or built-in) network cards. In ns-3, we would say that you will see nodes with connected NetDevices. In a large simulated network, you would need to organize connections between many objects. Node, NetDevice and Channel.

Since connecting NetDevices to nodes, NetDevices to channels, assigning IP addresses, etc., in ns-3 is a common task, we provide so-called topology helpers to make this as simple as possible. For instance, creating a NetDevice requires multiple ns-3 core operations, adding a MAC address, setting this network device in a Node, configuring the node's protocol stack, and then connecting the NetDevice to a Channel. Even more operations are necessary to connect several devices to multipoint channels and then merge separate networks into an integrated network (Internetworks). We provide topology helper objects that conveniently combine these many operations into an easy-to-use model.

4.2 First ns-3 Script

If you set up the system as suggested above, you will have the ns-3 release in a directory named repos in your home directory. Navigate to the directory release

If you do not have such a directory, it means you did not specify the output directory when building the release version of ns-3. Build it like this:
$ ./waf configure --build-profile=release --out=build/release,
$ ./waf build

there you should see a directory structure similar to the following:

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

Navigate to the directory examples/tutorial. You should see a file named first.cc. This script will create a simple point-to-point connection between two nodes and transmit one packet between them. Let's look at this script line by line by opening first.cc in your favorite editor.

4.2.1 Boilerplate Code
The first line in the file is the editor mode line Eclipse. It tells emacs about the formatting conventions (coding style) we will use in our source code.

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

This is always a somewhat contentious issue, so we should clarify it to clear it up right away. The ns-3 project, like most large projects, has adopted a coding style that all provided code must adhere to. If you want to contribute your code to the project, you will eventually have to conform to the ns-3 coding standard as outlined in the file doc/codingstd.txt or shown on the project's web page: https://www.nsnam.org/develop/contributing-code/coding-style/.

We recommend that you become familiar with the appearance of ns-3 code and apply this standard whenever you work with our code. The entire development team and contributors agreed to this after some grumbling. The emacs mode line provided above simplifies correct formatting if you are using the emacs editor.

The ns-3 simulator is licensed under GNU General Public License. You will see the appropriate legal GNU header in each file of the ns-3 distribution. Often, you may see a copyright notice for one of the participating institutions in the ns-3 project above the GPL text and the author shown below.

/* 
* 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 Modules

The actual code begins with a series of include statements (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"

To assist our high-level script users in managing the large number of header files present in the system, we group them according to their usage into big modules. We provide a single header file that will recursively load all the header files used within the module. Instead of searching for the exact header you need and possibly getting the right list of dependencies, we give you the ability to load a group of files with a high degree of detail. This may not be the most efficient approach, but it certainly makes scripting much easier.

Each of the included ns-3 files is placed in a directory named ns3 (build subdirectory) to avoid filename conflicts during the build process. The file ns3/core-module.h corresponds to the ns-3 module that you will find in the directory src/core in the release you have installed. In the listing of this directory, you will find a large number of header files. When you perform a build, , described below. Most users will work with it places the public header files in the ns3 directory under the subdirectory build/debug

If you do not have such a directory, it means you did not specify the output directory when building the release version of ns-3. Build it like this:
$ ./waf configure --build-profile=debug --out=build/debug
$ ./waf build
or
$ ./waf configure --build-profile=optimized --out=build/optimized
$ ./waf build

or build/optimized, depending on your configuration. , described below. Most users will work with It will also automatically generate an include file for the module to load all of the public header files. Since you are, of course, diligently following this guide, you have already done

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

to set up the project for building debug builds that include examples and tests. You have also done

$ ./waf

to build the project. So now, when you look in the directory ../../build/debug/ns3, you will find, among others, the header files of the four modules shown above. You can take a look at the contents of these files and find that they include all public files used by the corresponding modules.

4.2.3 Namespace ns3

The next line in the script first.cc is the namespace declaration.

using namespace ns3;

The ns-3 project is implemented in a C++ namespace called ns3. This groups all ns-3 related declarations within a scope outside of the global namespace, which we hope will assist in integration with other code. Using the C++ operator brings the ns-3 namespace into the current (global) declarative region. This is a fancy way of saying that after this declaration, you will not need to use the ns3:: scope resolution operator before any ns-3 code to utilize it. If you are not familiar with namespaces, refer to almost any C++ textbook and compare the ns3 namespace with using the std namespace and declarations. using namespace std; in examples working with the output operator cout and streams.

4.2.4 Logging

The next line of the script is as follows:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

We will use this statement as a convenient place to discuss our documentation system. DoxygenIf you look at the ns-3 project website, you will find a link titled 'Documentation' on the navigation panel. If you choose this link, you will end up on our documentation page. There is a link for 'Latest Release' that will take you to the documentation for the latest stable version of ns-3. If you click on the 'API Documentation' link, you will reach the ns-3 API documentation page.

On the left side of the page, you will find a graphical representation of the documentation structure. A good starting point is the 'Modules' book in the ns-3 navigation tree. If you expand Modules, you will see a list of ns-3 module documentation. As discussed above, the concept of a module here is directly related to the files included in the module above. The ns-3 logging subsystem is discussed in the section Using the Logging Module, so we will return to it later in this guide, but you can learn about the statement above by looking at the module Core, then opening the book Debugging tools, and selecting the page Logging. Click on Logging.

Now you should view the documentation Doxygen for the module. LoggingIn the list of macros at the top of the page, you will see an entry for NS_LOG_COMPONENT_DEFINE. Before following the link, be sure to check the 'Detailed Description' of the logging module to understand its functionality as a whole. You can either scroll down or choose 'More...' under the diagram.

Once you have a general understanding of what is happening, go ahead and check the documentation on the specific NS_LOG_COMPONENT_DEFINE. I won't duplicate the documentation here, but to summarize, this line declares a logging component named FirstScriptExample, which allows you to enable and disable console log message registration by linking to the name.

4.2.5 Main Function

In the following lines of the script, you will see

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

This is just the declaration of the main function of your program (script). As in any C++ program, you need to define the main function; it runs first. There’s nothing special about it. Your ns-3 script is simply a C++ program. The next line sets the time resolution to 1 nanosecond, which is the default value:

Time::SetResolution (Time::NS);

Time resolution, or simply resolution, is the smallest time value that can be used (the smallest representable difference between two time values). You can change the resolution exactly once. The mechanism that provides this flexibility consumes memory, so once the resolution is explicitly set, we release the memory to prevent further updates. (If you don't explicitly set the resolution, it defaults to one nanosecond, and the memory will be released at the start of the simulation.)

The next two lines of the script are used to enable two logging components that are built into the applications EchoClient and EchoServer:

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

If you have read the documentation for the Logging component, you will see that there are several levels of detail you can enable for logging in each component. These two lines of code implement debug-level logging at the INFO level for echo clients and servers. At this level, the application will print messages while simulating during the sending and receiving of packets.

Now we will move directly to the creation of the topology and launching the simulation. We will utilize topology helper objects to make this task as easy as possible.

4.2.6 Using Topological Helpers

The next two lines of code in our script will actually create ns-3 Node objects that will represent computers in the simulation.

NodeContainer nodes;
nodes.Create(2);

Before we continue, let's find the documentation for the class NodeContainer. Another way to access the documentation for this class is through the Classes tab on the Doxygen. If you already have Doxygen open, just scroll to the top of the page and select the Classes tab. You should see a new set of tabs, one of which is the list of classes. Under this tab, you will see a list of all ns-3 classes. Scroll down to ns3::NodeContainer. When you find the class, select it to access the documentation for that class.

As we recall, one of our key abstractions is the node. It represents a computer to which we are going to add things like protocol stacks, applications, and interface cards. The topology helper NodeContainer provides a convenient way to create, manage, and access any objects Node, that we create for running the simulation. The first line above simply declares NodeContainer, which we call nodes. The second line calls the Create method for the nodes object and asks the container to create two nodes. As described in Doxygen, the container requests the creation of two objects in the ns-3 system Node and stores pointers to those objects inside.

The nodes created in the script do nothing yet. The next step in building the topology is to connect our nodes to the network. The simplest form of network we support is a point-to-point link between two nodes. We will now create such a connection.

PointToPointHelper

We create a point-to-point connection by following a familiar pattern, using a topological helper object to perform the low-level work necessary for the connection. Let’s recall that our two key abstractions NetDevice and Channel. In the real world, these terms roughly correspond to peripheral cards and network cables. Generally, these two things are closely linked, and no one can rely on exchange, for example, devices Ethernet over a wireless channel. Our topological helpers follow this close relationship, and therefore in this scenario, you will use one object PointToPointHelper to configure and connect ns-3 objects PointToPointNetDevice and PointToPointChannel. The next three lines in the scenario:

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

The first line,

PointToPointHelper pointToPoint;

creates an instance of the object in the stack. PointToPointHelperFrom a high-level perspective, the next line,

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

tells the object PointToPointHelper to use the value "5 Mbps" (five megabits per second) as the "DataRate».

From a more specific perspective, the line "DataRate" corresponds to what we call an attribute PointToPointNetDevice. If you look at Doxygen the class ns3::PointToPointNetDevice and in the documentation for the method GetTypeId you will find a list of attributes defined for the device. Among them will be the attribute "DataRate". Most user-visible ns-3 objects have similar lists of attributes. We use this mechanism for easy simulation configuration without recompilation, as you will see in the next section.

Similar to "DataRate" in PointToPointNetDevice, you will find the attribute "Delay" associated with PointToPointChannel. The final line,

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

says PointToPointHelper uses the value "2 ms" (two milliseconds) as the propagation delay value across the point-to-point channel that it subsequently creates.

NetDeviceContainer

At this point in the scenario, we have NodeContainer, which contains two nodes. We have PointToPointHelper, which is prepared to create the objects PointToPointNetDevices and connect them using a PointToPointChannel object. Just as we used a NodeContainer topological helper object to create nodes, we will ask PointToPointHelper to do the work related to creating, configuring, and setting up our devices. We will need a list of all created objects. NetDevice, so we use NetDeviceContainer for their storage just like we used NodeContainer for storing the nodes we created. The following two lines of code,

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

complete the setup of the devices and the channel. The first line declares the device container mentioned above, and the second performs the main function. The method Install of the facility PointToPointHelper accepts NodeContainer as a parameter. Inside NetDeviceContainer for each node present in NodeContainer is created (for point-to-point communication there should be exactly two) PointToPointNetDevice is created and stored in the device container. PointToPointChannel is created, and to it two PointToPointNetDevices. After the creation of the objects, the attributes stored in PointToPointHelper, are used to initialize the corresponding attributes in the created objects.

After calling pointToPoint.Install(nodes) we will have two nodes, each with a 'point-to-point' network device installed and one 'point-to-point' channel between them. Both devices will be configured to transmit data at a speed of five megabits per second with a transmission delay of two milliseconds.

InternetStackHelper

Now we have the nodes and devices set up, but no protocol stacks are installed on our nodes. The following two lines of code will take care of this.

InternetStackHelper stack;
stack.Install(nodes);

InternetStackHelper represents a topological helper for internet stacks, similar to PointToPointHelper for point-to-point network devices. The method Install takes a NodeContainer as a parameter. When executed, it will install the Internet stack (TCP, UDP, IP, etc.) on each node in the container.

Ipv4AddressHelper

Next, we need to link our devices with IP addresses. We provide a topological helper to manage the distribution of IP addresses. The only visible API to the user is the setting of a base IP address and subnet mask to use during the actual address distribution (this is done at a lower level within the helper). The following two lines of code in our script example first.cc,

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

declare a helper address object and tell it to start allocating IP addresses from the network 10.1.1.0, using the subnet mask 255.255.255.0 to determine allocations. By default, the allocated addresses will start from one and increase monotonically, so the first address allocated from this base will be 10.1.1.1, followed by 10.1.1.2, and so on. In reality, at a low level, the ns-3 system keeps track of all allocated IP addresses and generates a fatal error if you accidentally create a situation where the same address is generated twice (by the way, this error is hard to debug).

The next line of code,

Ipv4InterfaceContainer interfaces = address.Assign(devices);

performs the actual assignment of the address. In ns-3, we establish a connection between the IP address and the device using the object Ipv4Interface. Just as we sometimes need a list of network devices created by the helper for future use, we sometimes need a list of objects Ipv4Interface. Ipv4InterfaceContainer provides this functionality.

We have built a point-to-point network, with installed stacks and assigned IP addresses. Now we need applications on each node to generate traffic.

4.2.7 Using Application

Another of the core abstractions of the ns-3 system is Application in this scenario. We use two specializations of the base class Application ns-3 called UdpEchoServerApplication and UdpEchoClientApplication. As with previous cases, we use helper objects to configure and manage the base objects. Here we use UdpEchoServerHelper and UdpEchoClientHelperobjects to make our life easier.

UdpEchoServerHelper

The following lines of code in our example script first.cc are used to set up the UDP echo server application on one of the nodes we created earlier.

UdpEchoServerHelper echoServer(9);

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

The first line of code in the above snippet creates UdpEchoServerHelperAs usual, this is not an application by itself, but an object that helps us create real applications. One of our agreements is to pass the necessary attributes to the constructor of the helper object. In this case, the helper cannot do anything useful unless it is provided with the port number on which the server will wait for packets; this number must also be known to the client. Here, we pass the port number to the helper's constructor. The constructor, in turn, simply executes SetAttribute with the provided value. Later, if desired, you can use SetAttribute to set a different value for the 'Port' attribute.

Like many other helper objects, the object UdpEchoServerHelper has a method Install. Executing this method effectively creates a basic echo server application and binds it to the node. Interestingly, the method Install accepts NodeContainer also takes a parameter, just like other Install methods we have seen.

The implicit C++ conversion here takes the result of the method node.Get(1) (which returns a smart pointer to the node object - Ptr) and uses it in the constructor for the anonymous object NodeContainer, which is then passed to the method Install. If you can't determine in C++ code which method signature gets compiled and executed, look for implicit conversions.

Now we see that echoServer.Install is going to install the application UdpEchoServerApplication on the node found in NodeContainer, which we use to manage our nodes, the node with index 1. The method Install will return a container that holds pointers to all applications (in this case, one, since we passed an anonymous NodeContainer, containing one node) created by the helper.

Applications need to specify the moment to start generating traffic 'start' and may additionally need to specify when to stop it 'stop'.We provide both parameters. These times are set using the methods ApplicationContainer Start and Stop. These methods take parameters of type Time. In this case, we use an explicit sequence of C++ conversions to take the C++ double 1.0 and convert it into a ts‑3 Time object that uses the Seconds object for translation into seconds. Remember that conversion rules may be controlled by the model author, and C++ has its own rules, so you cannot always expect parameters to be converted as you anticipated. Two lines,

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

will result in the echo server application starting (automatically turning on) one second after the simulation begins and stopping (turning off) ten seconds into the simulation. Since we declared a simulation event (the application stop event) that will execute after ten seconds, at least ten seconds of network operation will be simulated.

UdpEchoClientHelper

The client application echo is configured in a manner quite similar to the server. There is a base object UdpEchoClientApplication, which is managed by
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));

However, for the echo client we need to set five different attributes. The first two attributes are set during creation UdpEchoClientHelper. We pass the parameters that are used (within the helper) to set the attributes "RemoteAddress" and "RemotePort" according to our agreement on transferring the necessary parameters to the helper's constructor.

Let’s recall that we used Ipv4InterfaceContainer to track the IP addresses we assigned to our devices. The zero interface in the interface container corresponds to the IP address of the zero node in the node container. The first interface in the interface container corresponds to the IP address of the first node in the node container. So, in the first line of code (at the top), we create the helper and tell it that the client’s remote address will be the IP address assigned to the node where the server is located. We also state that packets should be sent to port nine.

"MaxPackets" attribute informs the client of the maximum number of packets we can send during simulation. The "Interval" attribute tells the client how long to wait between packets, and the "PacketSize" attribute informs the client how large the packet payload should be. With this combination of attributes, we instruct the client to send a single 1024-byte packet.

As with the echo server, we set the attributes for the echo client, Start and Stopbut here we start the client one second after the server is turned on (two seconds after the simulation begins).

4.2.8 Simulator

At this stage, we need to start the simulation. This is done using the global function Simulator::Run.

Simulator::Run ();

When we previously called the methods,

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

we effectively scheduled events in the simulator at 1.0 seconds, 2.0 seconds, and two events at 10.0 seconds. After calling Simulator::Run, the system will begin processing the list of scheduled events and executing them. It will first launch the event at 1.0 seconds, which activates the echo server application (this event can, in turn, schedule many other events). Then it will launch the event scheduled for t = 2.0 seconds, which will start the echo client application. Again, this event may schedule many more events. The implementation of the start event in the echo client will initiate the data transfer simulation phase by sending a packet to the server.

The act of sending the packet to the server will trigger a chain of events that will be automatically scheduled behind the scenes and will implement the mechanics of sending echo signal packets according to the synchronization parameters we set in the script.

As a result, since we are sending only one packet (remember, the attribute MaxPackets was set to one), the chain of events initiated by this single client echo request will finish, and the simulation will enter a wait state. Once this occurs, the remaining scheduled events will be the events Stop for the server and client. When those events are executed, there will be no further events left to process and Simulator::Run it will return control. The simulation is complete.

All that's left is to clean up. This is done by calling the global function Simulator::DestroySince helper functions (or low-level ns-3 code) were called, which are organized to insert hooks into the simulator for the destruction of all objects that were created. You do not need to track any of these objects manually — all you needed to do was call Simulator::Destroy and exit. The ns-3 system will do this difficult job for you. The remaining lines of our first ns-3 script, first.cc, do exactly that:

Simulator::Destroy ();
return 0;
}

When will the simulator stop?

ns-3 is a discrete event simulator (DE). In such a simulator, each event is associated with the time of its execution, and the simulation continues by processing events in the order they occur throughout the simulation. Events can lead to the scheduling of future events (for example, a timer may reschedule itself to finish counting in the next interval).

Initial events are usually triggered by an object, for example, IPv6 will schedule service discovery on the network, neighbor requests, etc. The application schedules the first packet sending event, and so on. When an event is processed, it can generate zero, one, or multiple events. As the simulation runs, events simply conclude or spawn new ones. The simulation will stop automatically if the event queue becomes empty or a special event is detected. StopAn event Stop is generated by the function Simulator::Stop (stop time).

There is a typical case where Simulator::Stop is absolutely necessary to stop the simulation: when there are self-sustaining events. Self-sustaining (or recurring) events are events that always reschedule themselves. As a result, they always keep the event queue non-empty. There are many protocols and modules that contain recurring events, for example:

• FlowMonitor — periodic check for lost packets;

• RIPng — periodic broadcasting of routing table updates;

• etc.

In such cases Simulator::Stop is necessary to correctly stop the simulation. Moreover, when ns-3 is in emulation mode, RealtimeSimulator is used to synchronize the simulation clocks with the machine clocks, and Simulator::Stop is necessary to stop the process.

Many of the simulation programs in the tutorial do not call Simulator::Stop this is evident as they automatically finish with the queue events being exhausted. However, these programs will also call Simulator::Stop. For example, the following additional statement in the first program will schedule an explicit stop at the 11th second:

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

The above will not actually change the behavior of this program since this specific simulation naturally ends after 10 seconds. But if you changed the stop time in the above statement from 11 seconds to 1 second, you would notice that the simulation stops before any output reaches the screen (as the output occurs approximately 2 seconds into the simulation time).

It is important to call Simulator::Stop before calling Simulator::Run; otherwise, Simulator::Run may never return control to the main program to execute the stop!

4.2.9 Building Your Scenario

We made creating your simple scripts trivial. All you need to do is place your script in the scratch directory, and it will be automatically built when you run , described below. Most users will work with. Let's try. Navigate back to the top-level directory and copy examples/tutorial/first.cc to the directory scratch

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

Now build your first script example using , so instead of the above command, the following will work::

$ ./waf

You should see messages indicating that your first example was successfully created.

Waf: Entering directory `/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: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (2.357s)

Now you can run the example (note that if you build your program in the scratch directory, you should also run it from scratch):

$ ./waf --run scratch/myfirst

You should see output similar to this:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s) Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Here you can see that the build system checks that the file was compiled and then runs it. You see an entry on the echo client indicating that it sent a 1024-byte packet to the echo server 10.1.1.2. You also see a log entry on the echo server stating that it received 1024 bytes from 10.1.1.1. The echo server silently echoes the packet back, and you can see in the echo client log that it received its packet back from the server.

4.3 ns-3 Source Code

Now that you have used some of the ns-3 helpers, you can take a look at some source codes that implement this functionality. The latest code can be viewed on our web server at the following link: https://gitlab.com/nsnam/ns-3-dev.git. There you will see a Mercurial summary page for our ns-3 development tree. At the top of the page, you will find several links,

summary | shortlog | changelog | graph | tags | files

Go ahead and select the link to files. Here’s what the top level of most of our repositories will look like:

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

Our example scripts are located in the directory examples. If you click on examples, you will see a list of subdirectories. One of the files in the subdirectory tutorial — first.cc. If you click on that, first.cc you will see the code that you just learned about.

The source code is primarily located in the main directory srcYou can view the source code by clicking on the directory name or by clicking on the files link to the right of the directory name. If you click on the src directory, you will see a list of subdirectories within src. If you then click on the core subdirectory, you will find a list of files. The first file you will see (at the time of writing this guide) is abort.h. If you click on the link abort.h, you will be taken to the source file for abort.h, which contains useful macros for exiting scripts when abnormal conditions are detected. The source code for the helpers we used in this chapter can be found in the directory src/Applications/helper. Feel free to explore the directory tree to understand what is where and get familiar with the ns-3 programming style.

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster