ns-3 network simulator tutorial. Chapter 4

ns-3 network simulator tutorial. Chapter 4
chapter 1,2
chapter 3

4 Concept overview
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-ins
4.2.3 ns3 namespace
4.2.4 Logging
4.2.5 Main function
4.2.6 Using topology helpers
4.2.7 Using Application
4.2.8 Simulator
4.2.9 Building your script
4.3ns-3 Source Code

Chapter 4

Concept overview

The first thing we need to do before we start learning or writing ns-3 code is to explain a few basic concepts and abstractions in the system. Much of this may seem obvious to some, but we recommend taking the time to read this section to make sure you're starting on a solid footing.

4.1 Key abstractions

In this section, we'll look at some terms that are commonly used on the web but have a specific meaning 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. Due to the fact that ns-3 is a network simulator and not an Internet simulator, we deliberately do not use the term host, as it is closely related to the Internet and its protocols. Instead, we use a more general term, also used by other simulators, which has its origins in graph theory - node (urge).

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

You must understand Node like a computer to which you add functionality. You'll add things like applications, protocol stacks, and peripheral cards with drivers that enable the computer to do useful work. 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, processor cycles, disk, network, etc. according to some computational model. System software typically does not use these resources to perform tasks that provide direct benefit to the user. The user, in order to achieve a certain goal, usually launches an application that receives and uses resources controlled by the system software.

Often the dividing line between system software and application software is drawn in privilege level changes that occur in operating system traps. ns-3 has no real concept of an operating system and therefore no concept of privilege levels or system calls. We do, however, have an idea for an app. Just as in the "real world" software applications run on computers to perform tasks, ns-3 applications run on ns-3 nodes to run simulations in the simulated world.

In ns-3, the base abstraction for the user program that generates some activity for simulation is the application. This abstraction is represented in C++ by the Application class. The Application class provides methods for manipulating the views of our version of user-level applications in simulations. Developers are expected to specialize the Application class in the sense of object-oriented programming in order to create new applications. In this tutorial, we will be using the Application class specializations called UdpEchoClientApplication ΠΈ UdpEchoServerApplication. As you might expect, these applications comprise a set of client/server applications used to generate and echo simulated network packets.

4.1.3 Channel

In the real world, you can connect a computer to a network. Often the media over which data is transmitted in these networks are called channels. When you plug an Ethernet cable into a wall outlet, you are connecting your computer to an Ethernet link. In the simulated ns-3 world, a node connects to an object representing a communication channel. Here the basic abstraction of a 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 hosts to them. Channels can also be specialized by developers in the sense of object-oriented programming. A channel specialization can model something as simple as a wire. A dedicated channel can also model complex things like a large Ethernet switch or a XNUMXD space full of obstacles in the case of wireless networks.

We will be using specialized versions of the channel in this tutorial called CsmaChannelCsmaChannel, PointToPointChannelPointToPointChannel ΠΈ WifiChannelWifiChannel. CsmaChannel, for example, models a version of a communications subnet that implements a carrier sense multiple access communications environment. This gives us Ethernet-like functionality.

4.1.4 Net Device

It used to be that 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, that you had to install in the computer. If the peripheral board implemented some network functions, they were called network interface boards or network cards. Most computers today come with network interface hardware integrated and are not seen as separate devices by users.

The network card will not work without a software driver that controls its hardware. On Unix (or Linux), a piece of peripheral equipment is classified as a device. Devices are managed using device drivers, and network devices (NICs) are managed using network device drivers (network device drivers) and are collectively called network devices (net devices). On Unix and Linux, you refer to network devices by names such as eth0.

In ns-3, the network device abstraction covers both the driver software and the hardware being simulated. In the simulation, a network device is "mounted" in a node to allow it to communicate with other nodes via channels. As in a real computer, a node can be connected to multiple channels through multiple devices NetDevices.

The network device abstraction is represented in C++ by the class NetDevice... Class NetDevice provides methods for managing connections to Node and Channel objects; and can be specialized by developers in the sense of object-oriented programming. In this tutorial, we will use several specialized versions of NetDevice called CsmaNetDevice, PointToPointNetDevice ΠΈ WifiNetDevice. Just like an Ethernet network adapter is designed to work with a network Ethernet, CsmaNetDevice designed to work with CsmaChannel, PointToPointNetDevice designed to work with PointToPointChannel, WifiNetDevice - Designed to work with wifi channel.

4.1.5 Topological helpers

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

Because connecting NetDevices to nodes, NetDevices to channels, assigning IP addresses, etc. in ns-3 are a common task, to make this as easy as possible, we provide so-called topology helpers. For example, to create a NetDevice, you need to perform a lot of ns-3 kernel operations, add a MAC address, install this network device in Node, configure the host's protocol stack, and then connect the NetDevice to a Channel. More operations will be required to connect multiple devices to multipoint links and then connect the individual networks into a single network (Internetworks). We provide topology helper objects that combine these multiple operations into an easy-to-use model for your convenience.

4.2 First ns-3 script

If you installed the system as suggested above, you will have the ns-3 release in a directory called repos in your home directory. Go to directory release

If you do not have such a directory, then 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

Go to directory examples/tutorial. You should see a file there named first.cc. This is a script that will create a simple point-to-point connection between two nodes and send one packet between the nodes. 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 emacs. It tells emacs about the formatting conventions (coding style) we use in our source code.

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

This is always quite a contentious issue, so we need to be clear to get it out of the way right away. The ns-3 project, like most major projects, has adopted a coding style that all submitted code must conform to. If you want to contribute your code to the project, you will eventually have to conform to the ns-3 coding standard as described in the file doc/codingstd.txt or shown on the project web page: https://www.nsnam.org/develop/contributing-code/coding-style/.

We recommend that you get used to the look and feel of ns-3 code and apply this standard whenever you work with our code. The entire development team and contributors agreed with this after some grumbling. The emacs mode line above makes it easy to format properly if you're using the emacs editor.

The ns-3 simulator is licensed using GNU General Public License. You will see the corresponding GNU legal header in every file in the ns-3 distribution. Often you will 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-ins

The code itself begins with a series of include statements (includes).

#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 help our high-level scripting users deal with the large number of header files present in the system, we group them according to their use into large modules. We provide one header file that will recursively load all the header files used in a given module. Instead of looking for exactly which header you need and possibly getting the right list of dependencies, we give you the ability to upload a group of files in great detail. It's not the most efficient approach, but it certainly makes scripting a lot easier.

Each of the ns-3 include files is placed in a directory named ns3 (build subdirectory) to avoid file name conflicts during the build process. File ns3/core-module.h corresponds to the ns-3 module that you find in the directory src/core in the release you installed. In the listing of this directory, you will find a large number of header files. When you make a build, waf places the public header files in the ns3 directory in a subdirectory build/debug

If you do not have such a directory, then 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. waf will also automatically generate a module include file to load all public header files. Since you are, of course, strictly following this guide, you have already done

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

to set up the project to run debug builds that include examples and tests. you also did

$ ./waf

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

4.2.3 ns3 namespace

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

using namespace ns3;

The ns-3 project is implemented in a C++ namespace called ns3. This groups all ns-3-related declarations in scope outside of the global namespace, which we hope will help with integration with other code. Using the C++ statement introduces the ns-3 namespace into the current (global) declarative region. It's a fancy way of saying that after this declaration, you won't need to type the ns3::scope permission operator before any ns-3 code to use it. If you're unfamiliar with namespaces, take a look at almost any C++ textbook and compare the ns3 namespace with the std namespace and the declaration using namespace std; in examples of working with the output operator cout and streams.

4.2.4 Logging

The next line of the script is,

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

We will use this statement as a convenient place to discuss our documentation system. Doxygen. If you look at the ns-3 project website, you will find a "Documentation" link in the navigation bar. If you choose this link, you will be taken to our documentation page. There is a link to "Latest Release" which will take you to the documentation for the latest stable version of ns-3. If you select the "API Documentation" link, you will be taken to 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 place to start is in the Modules ns-3 "book" in the ns-3 navigation tree. If you reveal Modules, you will see a list of documentation for ns-3 modules. 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 (logging, logging) is discussed in the section Using the logging module, so we'll come back to it later in this tutorial, but you can learn about the above statement by looking at the module Coreand then opening the book Debugging tools, and then selecting a page Logging. Click on Logging.

You should now review the documentation Doxygen for module Logging. In the list of macros at the top of the page, you will see an entry for NS_LOG_COMPONENT_DEFINE. Before clicking on the link, be sure to look at the β€œDetailed Description” of the registration module to understand how it works in general. To do this you can scroll down or select "More..." under the chart.

Once you have a general idea of ​​what's going on, go ahead and look at the documentation for the specific NS_LOG_COMPONENT_DEFINE. I won't duplicate the documentation here, but to summarize, this line declares a registration bean called FirstScriptExample, which allows you to enable or disable console message logging by name reference.

4.2.5 Main function

In the following lines of the script, you will see,

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

This is just a declaration of the main function of your program (script). As with any C++ program, you need to define a main function, which is executed first. There is nothing special here. Your ns-3 script is just a C++ program. The following line sets the time resolution to 1 nanosecond, which is the default:

Time::SetResolution (Time::NS);

Time resolution, or simply resolution, is the smallest value of time that can be used (the smallest representable difference between two times). You can change the resolution exactly once. The mechanism that provides this flexibility consumes memory, so once the permission is set explicitly, we free the memory, preventing further updates. (If you don't explicitly set the resolution, it will default to one nanosecond and the memory will be freed when the simulation starts.)

The next two lines of the script are used to enable two logging components that are built into applications echoclient ΠΈ 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 logging/granularity that you can enable on each component. These two lines of code enable debug logging to the INFO level for echo clients and servers. At this level, the application will print messages during the simulation when sending and receiving packets.

Now we'll get straight to the point of creating the topology and running the simulation. We use topological helper objects to make this job as easy as possible.

4.2.6 Using topology helpers

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

NodeContainer nodes;
nodes.Create (2);

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

As we remember, one of our key abstractions is the node. It represents the computer to which we are going to add things like protocol stacks, applications, and peripheral cards. Topological Assistant NodeContainer provides a convenient way to create, manage and access any objects Node, which we create to run the simulation. The first line above simply declares NodeContainer, which we call nodes. The second line calls the Create method on the nodes object and asks the container to create two nodes. As described in Doxygen, the container asks the ns-3 system to create two objects Node and stores pointers to these objects inside.

The nodes created in the script do nothing yet. The next step in building the topology is connecting 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 following a familiar pattern, using a topological helper object to do the low-level work required for the connection. Recall that our two key abstractions NetDevice ΠΈ Channel. In the real world, these terms roughly correspond to peripheral cards and network cables. As a rule, these two things are closely related to each other, and no one can expect to exchange, for example, devices Ethernet over a wireless channel. Our topology helpers follow this close relationship and therefore you will use one object in this scenario. PointToPointHelper for configuring and connecting ns‑3 objects PointToPointNetDevice ΠΈ PointToPointChannel. The next three lines in the script:

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

First line,

PointToPointHelper pointToPoint;

creates an object instance on the stack PointToPointHelper. From a top level perspective, the next line,

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

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

More specifically, the string "DataRate" corresponds to what we call an attribute PointToPointNetDevice. If you look at Doxygen for 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 attribute lists. We use this mechanism to easily set up a simulation without recompilation, as you will see in the next section.

Like "datarate' in the PointToPointNetDevice, you will find the 'Delay' attribute associated with the PointToPointChannel. final line,

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

says PointToPointHelper use the value "2 ms" (two milliseconds) as the propagation delay value for the point-to-point link it subsequently creates.

NetDeviceContainer

At the moment we have in the script NodeContainerA that contains two nodes. We have PointToPointHelper, which is prepared to create objects PointToPointNetDevices and connecting them using a PointToPointChannel object. Just as we used the NodeContainer topology helper to create nodes, we will ask PointToPointHelper perform work for us related to the creation, configuration and installation of our devices. We need a list of all created objects NetDevice, so we use NetDeviceContainer to store them just like we used to NodeContainer to store the nodes we created. The next two lines of code,

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

complete the device and channel setup. The first line declares the device container mentioned above, and the second line does the main work. Method install object PointToPointHelper takes NodeContainer as a parameter. Inside NetDeviceContainer for each node located in NodeContainer is created (for a point-to-point connection, there must be exactly two of them) PointToPointNetDevice is created and stored in the device container. PointToPointChannel is created and two are attached to it PointToPointNetDevices. After the objects were created, the attributes stored in PointToPointHelper, are used to initialize the corresponding attributes in the created objects.

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

InternetStackHelper

We now have nodes and devices configured, but no protocol stacks installed on our nodes. The next two lines of code will take care of that.

InternetStackHelper stack;
stack.Install (nodes);

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

IPv4AddressHelper

Then we need to associate our devices with IP addresses. We provide a topology assistant to manage the allocation of IP addresses. The only API visible to the user is setting the base IP address and netmask to use when doing the actual address allocation (this is done at a lower level within the helper). The next two lines of code in our sample script first.cc,

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

declare an address helper object and tell it to start allocating IP addresses from the 10.1.1.0 network, using the bitmask 255.255.255.0 to determine. By default, allocated addresses will start at one and increase monotonically, so the first address allocated from this base will be 10.1.1.1, then 10.1.1.2, and so on. In reality, at a low level, the ns-3 system remembers 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 difficult to debug).

Next line of code,

Ipv4InterfaceContainer interfaces = address.Assign (devices);

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

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

4.2.7 Using Application

Another of the main abstractions of the ns-3 system is Application (application). In this scenario, we use two base class specializations Application ns-3 called UdpEchoServerApplication ΠΈ UdpEchoClientApplication. As in the previous cases, we use helper objects to set up and manage the base objects. Here we use UdpEchoServerHelper ΠΈ UdpEchoClientHelper objects to make our life easier.

UdpEchoServerHelper

The following lines of code in our example script, first.cc, are used to set up a 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 snippet above creates UdpEchoServerHelper. As usual, it's not an application in itself, it's an object that helps us build real applications. One of our conventions is to pass the necessary attributes to the constructor of the helper object (helper). In this case, the helper cannot do anything useful unless it is given a port number on which the server will listen for packets, this number must also be known to the client. In this case, we are passing the port number to the helper constructor. The constructor, in turn, simply does SetAttribute with the passed value. Later, if desired, you can set a different value for the Port attribute using SetAttribute.

Like many other helper objects, the object UdpEchoServerHelper has a method install. Executing this method actually results in the base echo server application being created and bound to the node. Interestingly, the method install takes NodeContainer as a parameter just like the others install the methods we have seen.

The C++ implicit conversion working here takes the result of a method node.Get(1) (which returns a smart pointer to a node object - Ptr ) and use it in the constructor for an anonymous object NodeContainer, which is then passed to the method install. If you cannot determine in C++ code which method is compiled and executed with what signature, then look among the implicit conversions.

Now we see that echoServer.Install about to install the app UdpEchoServerApplication to found in NodeContainer, which we use to manage our nodes, the node at index 1. Method install will return a container that contains pointers to all applications (in this case, one, since we passed an anonymous NodeContainer, containing a single node) created by the helper.

Applications need to specify when to start generating traffic "Start" and may need to additionally specify the time when to stop it "stop". We provide both options. These times are set using the methods ApplicationContainer Home ΠΈ Stop. These methods accept type parameters Time. In this case, we use an explicit C++ conversion sequence to take C++ double 1.0 and convert it to a tns-3 Time object using the Seconds object to convert to seconds. Remember that the conversion rules can be controlled by the model author, and C++ has its own rules, so you can't always rely on parameters being converted the way you expect. Two lines

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

will cause the echo server application to start (turn on automatically) one second after the start of the simulation and stop (turn off) after ten seconds of the simulation. Due to the fact that we declared a simulation event (application stop event), which will be executed in ten seconds, at least ten seconds of network operation will be simulated.

UdpEchoClientHelper

Client application threw out configured in a manner similar to the server. There is a base object UdpEchoClientApplicationcontrolled 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 at creation time UdpEchoClientHelper. We pass parameters which are used (inside the helper) to set attributes remote address ΠΈ "RemotePort" according to our convention, to pass the necessary parameters to the helper's constructor.

Recall that we used ipv4InterfaceContainer to track the IP addresses we have assigned to our devices. The null interface in the interfaces container will correspond to the IP address of the null host in the hosts container. The first interface in the interfaces container corresponds to the IP address of the first host in the hosts container. So in the first line of code (top), we create a helper and tell it that the client's remote address will be the IP address assigned to the host hosting the server. We also say that we need to organize the sending of packets to the ninth port.

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

As with the echo server, we set the echo client attributes Home ΠΈ Stop, but here we start the client one second after the server is turned on (two seconds after the simulation starts).

4.2.8 Simulator

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

Simulator::Run ();

When we called methods earlier,

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

we actually scheduled events in the simulator for 1,0 seconds, 2,0 seconds and two events for 10,0 seconds. After the call Simulator::Run, the system will start to view the list of scheduled events and execute them. It will first fire an event after 1,0 seconds, which will activate the echo server application (this event can in turn schedule many other events). It will then fire an event scheduled for t = 2,0 seconds which will launch the echo client application. Again, this event can schedule many more events. The launch event implementation in the echo client will begin the simulation data transfer 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 scene and that will implement the mechanics of sending the echo packet according to the timing parameters we have set in the script.

As a result, since we are sending only one packet (recall, the attribute MaxPackets was set to one), the chain of events triggered by this single client ping will end and the simulation will go to sleep. Once this happens, the remaining scheduled events will be the events Stop for server and client. When these events are executed, there will be no events left for further processing and Simulator::Run will return control. Simulation completed.

It remains only to clean up after yourself. This is done by calling the global function Simulator::Destroy. Because helper functions (or low-level ns-3 code) were called, which are organized so that hooks are inserted in the simulator to destroy all objects that have been created. You don't need to keep track of any of these objects yourself - all you had to do was call Simulator::Destroy and exit. The ns-3 system does the hard work for you. The remaining lines of our first ns-3 script, first.cc, do just 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 its execution time, and the simulation continues by processing events in the order they occurred during the simulation. Events can cause future events to be scheduled (for example, a timer can reschedule itself to finish counting at the next interval).

Initial events are usually triggered by an object, for example IPv6 will schedule service discovery on the network, neighbor requests, and so on. The application schedules the first batch send event, and so on. When an event is processed, it can fire zero, one, or more events. As the simulation runs, events happen, simply ending or spawning new ones. The simulation will stop automatically if the event queue is empty or a special event is detected Stop. Event Stop generated by function Simulator::Stop (stop time).

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

β€’ FlowMonitor - periodic check for lost packets;

β€’ RIPng β€” periodic translation of routing table updates;

β€’ etc.

In such cases Simulator::Stop needed to gracefully stop the simulation. Also, when ns-3 is in emulation mode, RealtimeSimulator is used to synchronize the simulation clock with the machine clock, and Simulator::Stop needed to stop the process.

Many of the simulation programs in the tutorial do not call Simulator::Stop explicitly, since they end automatically with the exhaustion of events in the queue. However, these programs will also accept the Simulator::Stop call. For example, the following additional statement in the first example program will schedule an explicit stop at 11 seconds:

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

The above will not actually change the behavior of this program, as this particular simulation ends naturally after 10 seconds. But if you were to change the stop time in the above statement from 11 seconds to 1 second, you would notice that the simulation stops before any output hits the screen (because the output happens after about 2 seconds of simulation time).

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

4.2.9 Building your script

We have 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 if you run waf. Let's try. Go back to the top level directory and copy examples/tutorial/first.cc to catalog scratch

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

Now build your first sample script using WAF:

$ ./waf

You should see messages 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, then you must run it from scratch):

$ ./waf --run scratch/myfirst

You should see similar output:

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 has been built and then runs it. You see a component entry on the echo client indicating that it sent a single 1024-byte packet to echo server 10.1.1.2. You too see the logging component on the echo server to say that it received 1024 bytes from 10.1.1.1. The echo server silently repeats the packet, and you can see in the echo client's log that it received its packet back from the server.

4.3ns-3 Source Code

Now that you've used some of the ns-3 helpers, you can take a look at some of the source code that implements 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 see several links,

summary | shortlog | changelog | graph | tags | files

Go ahead and select the link to the files. This is 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 sample scripts are 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 first.cc you will see the code you just learned.

The source code is located mainly in the directory src. You 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 get a list of src subdirectories. 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 sent to the original file for abort.h, which contains useful macros for exiting scripts if abnormal conditions are detected. The source code for the helpers we have used in this chapter can be found in the directory src/Applications/helper. Feel free to rummage through the directory tree to figure out what's where and to understand the style of ns-3 programs.

Source: habr.com

Add a comment