2190 lines
89 KiB
Plaintext
2190 lines
89 KiB
Plaintext
@c ========================================================================
|
|
@c Other Network Topologies
|
|
@c ========================================================================
|
|
|
|
@node Other-network-topologies
|
|
@chapter Other Network Topologies
|
|
@cindex topology
|
|
@cindex Channel
|
|
@cindex NetDevice
|
|
@cindex topology!bus
|
|
@cindex topology!point-to-point
|
|
@cindex PointToPointChannel
|
|
@cindex PointToPointNetDevice
|
|
|
|
@emph{Network topology} is the study of the arrangement of of the elements
|
|
(in @command{ns-3} represented by the classes @code{Channel} and @code{Node})
|
|
of a network. Two fundamental types of physical topologies are the
|
|
@emph{point-to-point} and @emph{bus} topologies. We have already been exposed
|
|
to the @command{ns-3} channel specialization named @code{CsmaChannel}. This is
|
|
a simulation of a bus network. We also provide a simulation of a
|
|
point-to-point channel with associated net devices. As described previously,
|
|
the associated C++ classes specialize the @command{ns-3} base classes
|
|
@code{NetDevice} and @code{Channel} and are called @code{PointToPointNetDevice}
|
|
and @code{PointToPointChannel} respectively.
|
|
|
|
We will use combinations of these bus and point-to-point topology elements
|
|
to show how to create several commonly seen network topologies.
|
|
|
|
@section A Point-to-Point Network
|
|
We're going to take what might be seen as a step backward and look at a simple
|
|
point-to-point network. We will be building the simplest network you can
|
|
imagine. A serial link (point to point) between two computers. When you
|
|
see this point-to-point network, you can think of an RS-422 (or RS-232 for
|
|
you old-timers) cable. This topology is shown below.
|
|
|
|
@sp 1
|
|
@center @image{pp,,,,png}
|
|
|
|
@cindex CreateObject
|
|
@cindex InternetNode
|
|
We have provided a file for you in the @code{tutorial}
|
|
directory called @code{tutorial-point-to-point.cc}. You should now be
|
|
familiar enough with the system to pick out fairly easily what has been
|
|
changed. Let's focus on the following lines:
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n1 = CreateObject<InternetNode> ();
|
|
|
|
Ptr<PointToPointChannel> link = PointToPointTopology::AddPointToPointLink (
|
|
n0, n1, DataRate (38400), MilliSeconds (20));
|
|
|
|
PointToPointTopology::AddIpv4Addresses (link, n0, "10.1.1.1",
|
|
n1, "10.1.1.2");
|
|
@end verbatim
|
|
|
|
You can see that we created two @code{InternetNode} objects in the usual way.
|
|
Then, instead of creating a @code{CsmaChannel} we create a
|
|
@code{PointToPointChannel}. This point-to-point channel, which we call
|
|
@code{link}, connects node zero (@code{n0}) and node one (@code{n1}) over a
|
|
simulated link that runs at 38400 bits per second and has a 20 millisecond
|
|
simulated speed-of-light delay. This call also creates appropriate net devices
|
|
and attaches them to nodes zero and one.
|
|
|
|
We then add IP addresses to the net devices we just created using the topology
|
|
helper @code{AddIpv4Addresses}. Node zero gets the IP address 10.1.1.1 and
|
|
node one gets the IP address 10.1.1.2 assigned.
|
|
|
|
The alert tutorial user may wonder what the network number or prefix is of
|
|
those IP addresses. The point-to-point topology assumes that you want a
|
|
@code{/30} subnet and assigns an appropriate net mask for you. It then then
|
|
@emph{asserts} that the network numbers of the two net devices match. So there
|
|
is an implicit network mask created down in the topology code that looks like,
|
|
|
|
@verbatim
|
|
Ipv4Mask netmask("255.255.255.252");
|
|
@end verbatim
|
|
|
|
The rest of the code you should recognize and understand. We are just going
|
|
to echo one packet across the point-to-point link. You should be now be able
|
|
to build and run this example and to locate and interpret the ASCII trace
|
|
file. This is left as an exercise for you.
|
|
|
|
The file @code{tutorial-point-to-point.cc} is reproduced here for your
|
|
convenience:
|
|
|
|
@verbatim
|
|
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/point-to-point-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/point-to-point-net-device.h"
|
|
#include "ns3/point-to-point-topology.h"
|
|
#include "ns3/udp-echo-client.h"
|
|
#include "ns3/udp-echo-server.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/nstime.h"
|
|
#include "ns3/ascii-trace.h"
|
|
#include "ns3/pcap-trace.h"
|
|
#include "ns3/global-route-manager.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("PointToPointSimulation");
|
|
|
|
using namespace ns3;
|
|
|
|
// Network topology
|
|
//
|
|
// point to point
|
|
// +--------------+
|
|
// | |
|
|
// n0 n1
|
|
//
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("PointToPointSimulation", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("Point to Point Topology Simulation");
|
|
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n1 = CreateObject<InternetNode> ();
|
|
|
|
Ptr<PointToPointChannel> link = PointToPointTopology::AddPointToPointLink (
|
|
n0, n1, DataRate (38400), MilliSeconds (20));
|
|
|
|
PointToPointTopology::AddIpv4Addresses (link, n0, "10.1.1.1",
|
|
n1, "10.1.1.2");
|
|
|
|
uint16_t port = 7;
|
|
|
|
Ptr<UdpEchoClient> client = CreateObject<UdpEchoClient> (n0, "10.1.1.2",
|
|
port, 1, Seconds(1.), 1024);
|
|
|
|
Ptr<UdpEchoServer> server = CreateObject<UdpEchoServer> (n1, port);
|
|
|
|
server->Start(Seconds(1.));
|
|
client->Start(Seconds(2.));
|
|
|
|
server->Stop (Seconds(10.));
|
|
client->Stop (Seconds(10.));
|
|
|
|
AsciiTrace asciitrace ("tutorial.tr");
|
|
asciitrace.TraceAllQueues ();
|
|
asciitrace.TraceAllNetDeviceRx ();
|
|
|
|
Simulator::Run ();
|
|
Simulator::Destroy ();
|
|
}
|
|
@end verbatim
|
|
|
|
@section A Star Network
|
|
A point-to-point network is considered a special case of a star network. As
|
|
you might expect, the process of constructing a star network is an extension
|
|
of the very simple process used for a point-to-point link. We have provided
|
|
a file for you in the @code{tutorial} directory called @code{tutorial-star.cc}
|
|
that implements a simple star network as seen below.
|
|
|
|
@sp 1
|
|
@center @image{star,,,,png}
|
|
|
|
In order to create a star network, we need to be able to instantiate some
|
|
number (greater than one) of net devices on a node. In the name of simplicity
|
|
of use, the @code{PointToPointTopology} topology helper does not allow one to
|
|
do this. We provided a separate topology helper class, the
|
|
@code{PointToPointIpv4Topology} helper class that provides the slightly finer
|
|
granularity we need to accomplish a star network. In order to use this new
|
|
helper we have to load the definitions by including the appropriate file.
|
|
|
|
@verbatim
|
|
#include "ns3/point-to-point-ipv4-topology.h"
|
|
@end verbatim
|
|
|
|
The star that we're going to create has a node in the center (@code{n0}) with
|
|
six nodes surrounding (@code{n1} - @code{n6}). You should be able to easily
|
|
find and understand the code that creates these nodes.
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n1 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n2 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n3 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n4 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n5 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n6 = CreateObject<InternetNode> ();
|
|
@end verbatim
|
|
|
|
Next, we get into the differences between the @code{PointToPointTopology}
|
|
helper and the @code{PointToPointIpv4Topology} helper. The
|
|
@code{PointToPointIpv4Topology} helper looks and feels a little like the
|
|
@code{CsmaIpv4Topology} helper. Just like you created a CSMA channel
|
|
previously, you need to create a point-to-point channel. The following
|
|
code creates a @code{PointToPointChannel} and calls it @code{link01}. You can
|
|
interpret this name as being the channel (or @emph{link}) from node zero to
|
|
node one.
|
|
|
|
@verbatim
|
|
Ptr<PointToPointChannel> link01 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
@end verbatim
|
|
|
|
You need to provide a data rate for the channel which we set at 38400 bits
|
|
per second. You must also provide a speed-of-light delay which we set at
|
|
20 milliseconds.
|
|
|
|
Just as you added a net device to the nodes in the CSMA tutorial section, you
|
|
do the same here but with a point-to-point net device. The following code
|
|
illustrates how we do that:
|
|
|
|
@verbatim
|
|
uint32_t nd01 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link01);
|
|
@end verbatim
|
|
|
|
We call the @code{PointToPointIpv4Topology} helper and ask it to add a net
|
|
device to node zero (@code{n0}) and connect it to the appropriate
|
|
point-to-point link (@code{link01}) which you will recall is the serial link
|
|
from node zero to node one.
|
|
|
|
If you look at the following code, you will see the same calls are repeated
|
|
to create the remaining five point-to-point channels and connect them
|
|
to net devices on node zero.
|
|
|
|
The next new code is found after the ``spokes'' of the star have been created.
|
|
It looks like the following:
|
|
|
|
@verbatim
|
|
uint32_t nd1 = PointToPointIpv4Topology::AddNetDevice (n1, link01);
|
|
uint32_t nd2 = PointToPointIpv4Topology::AddNetDevice (n2, link02);
|
|
uint32_t nd3 = PointToPointIpv4Topology::AddNetDevice (n3, link03);
|
|
uint32_t nd4 = PointToPointIpv4Topology::AddNetDevice (n4, link04);
|
|
uint32_t nd5 = PointToPointIpv4Topology::AddNetDevice (n5, link05);
|
|
uint32_t nd6 = PointToPointIpv4Topology::AddNetDevice (n6, link06);
|
|
@end verbatim
|
|
|
|
Here we are creating the net devices on the nodes surrounding the center node.
|
|
In the first call, we are adding a net device on node one (@code{n1}) and
|
|
connecting that net device to the channel named @code{link01}. Remember that
|
|
we created the channel @code{link01} as the channel connecting node zero and
|
|
node one. We previously created a net device on node zero and attached that
|
|
device to @code{link01}. Here we are connecting the other side of that link
|
|
to node one. The return value from this call is the net device index of the
|
|
created net device.
|
|
|
|
The next section of code adds addresses to the net devices we just created.
|
|
The first call adds the IP address 10.1.1.1 to the net device going from
|
|
node zero to node one. Recall that we first created a node named @code{n0}
|
|
and a channel called @code{link01}. We added a net device to @code{n0} and
|
|
remembered the net device index as the @code{uint32_t nd01}. This meant
|
|
the net device @emph{nd} on node @emph{0} that we connected to node @emph{1}.
|
|
We call @code{AddAddress} to add an IP address (10.1.1.1) to the net device
|
|
on node zero identified by the net device index @code{nd01}. We provide a
|
|
net mask suitable for a point to point network. This is typically a /30
|
|
address but we don't force that in this API.
|
|
|
|
After setting up the address on node zero, we do the same for the node on
|
|
the other end of the ``spoke'' --- in this case node one, with its single
|
|
net device. Note that the network number is the same on both sides of this
|
|
network.
|
|
|
|
@verbatim
|
|
PointToPointIpv4Topology::AddAddress (n0, nd01, "10.1.1.1",
|
|
``255.255.255.252'');
|
|
|
|
PointToPointIpv4Topology::AddAddress (n1, nd1, "10.1.1.2",
|
|
``255.255.255.252'');
|
|
@end verbatim
|
|
|
|
The following code repeats this pattern assining similar IP addresses to the
|
|
remaining net devices. Note that there are no @code{Mac48Address} address
|
|
assignments --- they are not required.
|
|
|
|
The rest of the code you should recognize and understand. We are just going
|
|
to echo one packet across the point-to-point link. You should be now be able
|
|
to build and run this example and to locate and interpret the ASCII trace
|
|
file. This is left as an exercise for you.
|
|
|
|
The file @code{tutorial-star.cc} is reproduced here for your convenience:
|
|
|
|
@verbatim
|
|
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/ptr.h"
|
|
#include "ns3/internet-node.h"
|
|
#include "ns3/point-to-point-channel.h"
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/point-to-point-net-device.h"
|
|
#include "ns3/point-to-point-ipv4-topology.h"
|
|
#include "ns3/udp-echo-client.h"
|
|
#include "ns3/udp-echo-server.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/nstime.h"
|
|
#include "ns3/ascii-trace.h"
|
|
#include "ns3/pcap-trace.h"
|
|
#include "ns3/global-route-manager.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("StarSimulation");
|
|
|
|
using namespace ns3;
|
|
|
|
// Network topology
|
|
//
|
|
// n3 n2
|
|
// | /
|
|
// | /
|
|
// n4 --- n0 --- n1
|
|
// / |
|
|
// / |
|
|
// n5 n6
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
LogComponentEnable ("StarSimulation", LOG_LEVEL_INFO);
|
|
|
|
NS_LOG_INFO ("Star Topology Simulation");
|
|
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n1 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n2 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n3 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n4 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n5 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n6 = CreateObject<InternetNode> ();
|
|
|
|
Ptr<PointToPointChannel> link01 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd01 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link01);
|
|
|
|
Ptr<PointToPointChannel> link02 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd02 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link02);
|
|
|
|
Ptr<PointToPointChannel> link03 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd03 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link03);
|
|
|
|
Ptr<PointToPointChannel> link04 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd04 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link04);
|
|
|
|
Ptr<PointToPointChannel> link05 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd05 = PointToPointIpv4Topology::AddNetDevice (n0,
|
|
link05);
|
|
|
|
Ptr<PointToPointChannel> link06 =
|
|
PointToPointIpv4Topology::CreateChannel (DataRate (38400),
|
|
MilliSeconds (20));
|
|
|
|
uint32_t nd06 = PointToPointIpv4Topology::AddNetDevice (n0, link06);
|
|
|
|
uint32_t nd1 = PointToPointIpv4Topology::AddNetDevice (n1, link01);
|
|
uint32_t nd2 = PointToPointIpv4Topology::AddNetDevice (n2, link02);
|
|
uint32_t nd3 = PointToPointIpv4Topology::AddNetDevice (n3, link03);
|
|
uint32_t nd4 = PointToPointIpv4Topology::AddNetDevice (n4, link04);
|
|
uint32_t nd5 = PointToPointIpv4Topology::AddNetDevice (n5, link05);
|
|
uint32_t nd6 = PointToPointIpv4Topology::AddNetDevice (n6, link06);
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd01, "10.1.1.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n1, nd1, "10.1.1.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd02, "10.1.2.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n2, nd2, "10.1.2.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd03, "10.1.3.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n3, nd3, "10.1.2.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd04, "10.1.4.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n4, nd4, "10.1.4.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd05, "10.1.5.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n5, nd5, "10.1.5.2",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n0, nd06, "10.1.6.1",
|
|
"255.255.255.252");
|
|
|
|
PointToPointIpv4Topology::AddAddress (n6, nd6, "10.1.6.2",
|
|
"255.255.255.252");
|
|
|
|
uint16_t port = 7;
|
|
|
|
Ptr<UdpEchoClient> client = CreateObject<UdpEchoClient> (n0, "10.1.1.2",
|
|
port, 1, Seconds(1.), 1024);
|
|
|
|
Ptr<UdpEchoServer> server = CreateObject<UdpEchoServer> (n1, port);
|
|
|
|
server->Start(Seconds(1.));
|
|
client->Start(Seconds(2.));
|
|
|
|
server->Stop (Seconds(10.));
|
|
client->Stop (Seconds(10.));
|
|
|
|
AsciiTrace asciitrace ("tutorial.tr");
|
|
asciitrace.TraceAllQueues ();
|
|
asciitrace.TraceAllNetDeviceRx ();
|
|
|
|
Simulator::Run ();
|
|
Simulator::Destroy ();
|
|
}
|
|
@end verbatim
|
|
|
|
@subsection Routing
|
|
If you are really excited about this simulator you may have already tried to
|
|
modify the scripts outside the tutorial. I know that one of the first things
|
|
that would have occurred to me when I saw the star network would have been to
|
|
start trying to add applications to echo packets from nodes other than zero.
|
|
If you tried, for example, to start the echo client on node one instead of
|
|
node zero, you would have found an empty trace file. The reason for this
|
|
is that you have now created an internetwork. This means you will need to
|
|
enable internetwork routing.
|
|
|
|
We have provided a file for you in the @code{tutorial} directory called
|
|
@code{tutorial-star-routing.cc} to show you how this is done. This extremely
|
|
tricky and difficult change is shown below:
|
|
|
|
@verbatim
|
|
GlobalRouteManager::PopulateRoutingTables ();
|
|
@end verbatim
|
|
|
|
This one-line addition, located just before the simulation runs, tells the
|
|
@command{ns-3} @emph{global route manager} to walk the topology you created and
|
|
build internetwork routing tables for all of the nodes in the simulation.
|
|
We changed the client application so that it runs on node four:
|
|
|
|
@verbatim
|
|
Ptr<UdpEchoClient> client = CreateObject<UdpEchoClient> (n4, "10.1.1.2",
|
|
port, 1, Seconds(1.), 1024);
|
|
@end verbatim
|
|
|
|
Now if you build and run @code{tutorial-star-routing.cc} you can examine the
|
|
@code{tutorial.tr} file and see that your UDP echo packets are now correctly
|
|
routed through the topology.
|
|
|
|
@section A Dumbbell Network
|
|
One of the most interesting simple topologies (from a phenomenological point of
|
|
view) is commonly called a dumbbell network. The name derives from a
|
|
superficial similarity in form to a piece of exercise equipment.
|
|
|
|
The dumbbell model is typically composed of two bus or star network elements
|
|
connected via a point-to-point link. The point-to-point link is usually
|
|
configured with a lower bandwidth than the bus elements to provide a
|
|
@emph{choke point}.
|
|
|
|
The following is a representation of the topology.
|
|
|
|
@sp 1
|
|
@center @image{dumbbell,,,,png}
|
|
|
|
We have provided a file that constructs this dumbbell network and creates
|
|
enough data flowing across the choke point that some packets will be dropped.
|
|
The file is called @code{tutorial-linear-dumbbell.cc} and is located in the
|
|
@code{tutorial} directory. We have already covered all of the code used to
|
|
create this network, so we will just quickly go over the main sections of the
|
|
script.
|
|
|
|
The first section creates a CSMA lan that will become the left side of the
|
|
dumbbell network. This code should be very familiar since we used the same
|
|
process to create our first example.
|
|
|
|
@verbatim
|
|
//
|
|
// Create the lan on the left side of the dumbbell.
|
|
//
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n1 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n2 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n3 = CreateObject<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan1 =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (10000000), MilliSeconds (2));
|
|
|
|
uint32_t nd0 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n0, lan1,
|
|
"08:00:2e:00:00:00");
|
|
|
|
uint32_t nd1 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n1, lan1,
|
|
"08:00:2e:00:00:01");
|
|
|
|
uint32_t nd2 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n2, lan1,
|
|
"08:00:2e:00:00:02");
|
|
|
|
uint32_t nd3 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n3, lan1,
|
|
"08:00:2e:00:00:03");
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n0, nd0, "10.1.1.1", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n1, nd1, "10.1.1.2", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n2, nd2, "10.1.1.3", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n3, nd3, "10.1.1.4", "255.255.255.0");
|
|
@end verbatim
|
|
|
|
The code to generate the CSMA lan on the right side is similar; only the names
|
|
have been changed.
|
|
|
|
@verbatim
|
|
//
|
|
// Create the lan on the right side of the dumbbell.
|
|
//
|
|
Ptr<Node> n4 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n5 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n6 = CreateObject<InternetNode> ();
|
|
Ptr<Node> n7 = CreateObject<InternetNode> ();
|
|
|
|
Ptr<CsmaChannel> lan2 =
|
|
CsmaTopology::CreateCsmaChannel (DataRate (10000000), MilliSeconds (2));
|
|
|
|
uint32_t nd4 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n4, lan2,
|
|
"08:00:2e:00:00:04");
|
|
|
|
uint32_t nd5 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n5, lan2,
|
|
"08:00:2e:00:00:05");
|
|
|
|
uint32_t nd6 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n6, lan2,
|
|
"08:00:2e:00:00:06");
|
|
|
|
uint32_t nd7 = CsmaIpv4Topology::AddIpv4CsmaNetDevice (n7, lan2,
|
|
"08:00:2e:00:00:07");
|
|
|
|
CsmaIpv4Topology::AddIpv4Address (n4, nd4, "10.1.2.1", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n5, nd5, "10.1.2.2", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n6, nd6, "10.1.2.3", "255.255.255.0");
|
|
CsmaIpv4Topology::AddIpv4Address (n7, nd7, "10.1.2.4", "255.255.255.0");
|
|
@end verbatim
|
|
|
|
Next, we create a point to point link to connect the two lans. We connect
|
|
the point-to-point channel between nodes three (on the left lan) and four
|
|
(on the right lan). You should recoginze this as substantially similar to
|
|
the link setup from the @code{point-to-point} example.
|
|
|
|
@verbatim
|
|
//
|
|
// Create the point-to-point link to connect the two lans.
|
|
//
|
|
Ptr<PointToPointChannel> link = PointToPointTopology::AddPointToPointLink (
|
|
n3, n4, DataRate (38400), MilliSeconds (20));
|
|
|
|
PointToPointTopology::AddIpv4Addresses (link, n3, "10.1.3.1",
|
|
n4, "10.1.3.2");
|
|
@end verbatim
|
|
|
|
Then we configure data flows. We create four echo clients that send UDP
|
|
packets from the left side lan to servers created on the right side lan.
|
|
Notice that we send 100 packets with an inter-packet gap of ten milliseconds
|
|
instead of the single packet we have previously used. This data rate is
|
|
sufficient to saturate the point-to-point link and will cause packets to be
|
|
dropped when the queue on the link net devices overflows (the default maximum
|
|
queue depth is 100 packets). Note that we stagger the start of the echo
|
|
clients to slowly bring up the data rates.
|
|
|
|
@verbatim
|
|
//
|
|
// Create data flows across the link:
|
|
// n0 ==> n4 ==> n0
|
|
// n1 ==> n5 ==> n1
|
|
// n2 ==> n6 ==> n2
|
|
// n3 ==> n7 ==> n3
|
|
//
|
|
uint16_t port = 7;
|
|
|
|
Ptr<UdpEchoClient> client0 = CreateObject<UdpEchoClient> (n0, "10.1.2.1",
|
|
port, 100, Seconds(.01), 1024);
|
|
Ptr<UdpEchoClient> client1 = CreateObject<UdpEchoClient> (n1, "10.1.2.2",
|
|
port, 100, Seconds(.01), 1024);
|
|
Ptr<UdpEchoClient> client2 = CreateObject<UdpEchoClient> (n2, "10.1.2.3",
|
|
port, 100, Seconds(.01), 1024);
|
|
Ptr<UdpEchoClient> client3 = CreateObject<UdpEchoClient> (n3, "10.1.2.4",
|
|
port, 100, Seconds(.01), 1024);
|
|
|
|
Ptr<UdpEchoServer> server4 = CreateObject<UdpEchoServer> (n4, port);
|
|
Ptr<UdpEchoServer> server5 = CreateObject<UdpEchoServer> (n5, port);
|
|
Ptr<UdpEchoServer> server6 = CreateObject<UdpEchoServer> (n6, port);
|
|
Ptr<UdpEchoServer> server7 = CreateObject<UdpEchoServer> (n7, port);
|
|
|
|
server4->Start(Seconds(1.));
|
|
server5->Start(Seconds(1.));
|
|
server6->Start(Seconds(1.));
|
|
server7->Start(Seconds(1.));
|
|
|
|
client0->Start(Seconds(2.));
|
|
client1->Start(Seconds(2.1));
|
|
client2->Start(Seconds(2.2));
|
|
client3->Start(Seconds(2.3));
|
|
|
|
server4->Stop (Seconds(10.));
|
|
server5->Stop (Seconds(10.));
|
|
server6->Stop (Seconds(10.));
|
|
server7->Stop (Seconds(10.));
|
|
|
|
client0->Stop (Seconds(10.));
|
|
client1->Stop (Seconds(10.));
|
|
client2->Stop (Seconds(10.));
|
|
client3->Stop (Seconds(10.));
|
|
@end verbatim
|
|
|
|
The remainder of the file should be quite familiar to you. Go ahead and
|
|
run @code{tutorial-linear-dumbbell}. Now take a look at the trace
|
|
(@code{tutorial.tr}) file. You will now see trace lines that begin with
|
|
@code{d}. Alternatively you can search for the string ``queue-drop'' which
|
|
is the expansion of the drop code ('d').
|
|
|
|
Interpretation of a dropped packet is straightforward. We have expanded
|
|
the first @code{queue-drop} trace for you below. See the section on ASCII
|
|
tracing for details.
|
|
|
|
@verbatim
|
|
00 d
|
|
01 2.40938
|
|
02 nodeid=3
|
|
03 device=1
|
|
04 queue-drop
|
|
05 pkt-uid=124
|
|
06 LLCSNAP(type 0x800)
|
|
07 IPV4(
|
|
08 tos 0x0
|
|
09 ttl 63
|
|
10 id 20
|
|
11 offset 0
|
|
12 flags [none]
|
|
13 length: 1052) 10.1.1.3 > 10.1.2.3
|
|
14 UDP(length: 1032)
|
|
15 49153 > 7
|
|
16 DATA (length 1024)
|
|
@end verbatim
|
|
|
|
We leave it as an exercise to examine the trace files in more detail.
|
|
|
|
@c ========================================================================
|
|
@c Nonlinear Thinking
|
|
@c ========================================================================
|
|
|
|
@node Nonlinear-Thinking
|
|
@chapter Nonlinear Thinking
|
|
|
|
One thing that all of our examples so far have in common is that they are
|
|
composed of a linear collection of calls into the @command{ns-3} system. The
|
|
programmers among the readers may have wondered why there is not as much
|
|
as a for-loop in all of the examples. The answer is that we wanted to
|
|
introduce you to @command{ns-3} scripting with a minimum of conceptual
|
|
overhead. We're going to remedy that situation shortly.
|
|
|
|
We have written a number of @command{ns-3} scripts in C++. Although we have
|
|
been perfectly linear in our script implementations, just like any other C++
|
|
program, an @command{ns-3} script can use any features of the language you
|
|
desire. If you will look back at the @code{tutorial-linear-dumbbell.cc}
|
|
example, you may notice that the code to create the left and right sides of
|
|
the dumbbell is operationally identical --- only the names change. An obvious
|
|
improvement of this program would be to use subroutines to create the sides.
|
|
Since we are working with C++, we should probably do this in an
|
|
object-oriented way. Since object-oriented design is somewhat of a black art
|
|
to some people, we'll take some time here and outline a simple methodology
|
|
you can follow.
|
|
|
|
@section Object Design 101 --- Class Ipv4BusNetwork
|
|
If you are a master of object oriented design, feel free to skip or skim this
|
|
section, in which we derive a simplistic but fully operational bus network
|
|
class.
|
|
|
|
So you want to create a BusNetwork class. Often the biggest hurdle in a
|
|
design is figuring out how to get started. One of the simplest and most
|
|
straightforward ways to do an object decomposition of a problem is to simply
|
|
write down a description of the problem and take a look at the words
|
|
you used. Let's take some time and do that, first at a very high level.
|
|
|
|
@example
|
|
A bus network is an implementation of a particular network topology that
|
|
contains some number of nodes. Each of these nodes is attached to a single
|
|
multi-drop channel. The network itself has some attributes independent of
|
|
the topology such as a network mask, network number (prefix) and base IP
|
|
address.
|
|
@end example
|
|
|
|
The first thing to do is to focus on the nouns and adjectives. These will
|
|
give you a starting point for required classes and member variables.
|
|
|
|
Immediately we can notice that at the highest level we are talking about the
|
|
noun @emph{network}. This probably won't surprise you. We also have an
|
|
adjective that modifies the noun --- @emph{bus}. This should lead us to our
|
|
first class defintion. Usually class names are constructed in the same way
|
|
as an English language sentence would be spoken. For example, one would speak
|
|
of a @emph{bus network} in conversation, so we would normally create a
|
|
@code{class BusNetwork} to represent it.
|
|
|
|
One thing to note is that we have used two words in our description quite
|
|
naturally: @emph{is} and @emph{has}. When you see these words should should
|
|
immediately think of the object-oriented concepts of @emph{ISA} (inheritance)
|
|
and @emph{HASA} (containment) respectively. We wrote that a bus network
|
|
@emph{is} an implementation of a particular network topology. Perhaps you
|
|
will agree that there is a natural base class called @code{Network} that
|
|
@emph{has} the attributes discussed above. The fact that a @code{BusNetwork}
|
|
@emph{ISA} kind of @code{Network} suggests inheritance. Let's capture that
|
|
thought right away remembering that we're focused on IP version four here:
|
|
|
|
@verbatim
|
|
class Ipv4Network
|
|
{
|
|
public:
|
|
Ipv4Address m_network;
|
|
Ipv4Mask m_mask;
|
|
Ipv4Address m_baseAddress;
|
|
};
|
|
|
|
class Ipv4BusNetwork : public Ipv4Network
|
|
{
|
|
};
|
|
@end verbatim
|
|
|
|
Let's take a look at the @emph{HASA} relationships of the bus network. Clearly
|
|
it will @emph{have} a reference to the underlying channel that implements the
|
|
actual communications medium. We use smart pointers for those references, so
|
|
one member variable is obvious:
|
|
|
|
@verbatim
|
|
Ptr<CsmaChannel> m_channel;
|
|
@end verbatim
|
|
|
|
A bus network will also need to contain references to all of the nodes we
|
|
eventually want to create. If you are working in C++ and see the words contain
|
|
or container, you should immediately think of the Standard Template Library
|
|
or STL. A quick search of the available containers there will probably lead
|
|
you to consider the vector class. A vector is a container that looks like an
|
|
array. This is just what we need here. Again, we want to use smart pointers
|
|
to reference our nodes, so the declaration of the vector would look like,
|
|
|
|
@verbatim
|
|
std::vector<Ptr<Node> > m_nodes;
|
|
@end verbatim
|
|
|
|
It will save you headaches in the future if you notice that the space between
|
|
the two right brackets is required to differentiate this situation from a
|
|
right-shift operator. So we have a pretty good start already after just a
|
|
little work. Now we need to turn our attention to actions. Let's write
|
|
another little description of the things you consider doing to a Bus network.
|
|
|
|
@example
|
|
We need to be able to create a bus network. We need to be able to delete a
|
|
bus network. We need to be able to get a handle to a node in order to add
|
|
applications. We need to be able to set the network, mask and base address
|
|
somehow, specify how many nodes to create and provide the underlying channel
|
|
its required bandwidth and delay parameters.
|
|
@end example
|
|
|
|
We now look at the @emph{verbs} in that sentence. These will give a good
|
|
starting point for the methods of the classes. For example, the verbs
|
|
@emph{create} and @emph{delete} should suggest @emph{constructor} and
|
|
@emph{destructor}. The verb @emph{get} leads us to providing a method called
|
|
@code{GetNode}. We have to provide a number of parameters so we can either
|
|
provide @emph{setters} or we can simply pass them in as parameters to our
|
|
constructors. Since this is a simple example, we won't bother to implement
|
|
getters and setters (methods to get and set member variables to enhance data
|
|
hiding). Let's use this guidance to finish up our class declarations:
|
|
|
|
@verbatim
|
|
class Ipv4Network
|
|
{
|
|
public:
|
|
Ipv4Network (Ipv4Address network, Ipv4Mask mask, Ipv4Address address);
|
|
virtual ~Ipv4Network ();
|
|
|
|
Ipv4Address m_network;
|
|
Ipv4Mask m_mask;
|
|
Ipv4Address m_baseAddress;
|
|
};
|
|
|
|
class Ipv4BusNetwork : public Ipv4Network
|
|
{
|
|
public:
|
|
Ipv4BusNetwork (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address startAddress,
|
|
DataRate bps,
|
|
Time delay,
|
|
uint32_t n);
|
|
|
|
virtual ~Ipv4BusNetwork ();
|
|
|
|
Ptr<Node> GetNode (uint32_t n);
|
|
|
|
private:
|
|
std::vector<Ptr<Node> > m_nodes;
|
|
Ptr<CsmaChannel> m_channel;
|
|
};
|
|
@end verbatim
|
|
|
|
That's it. We have actually already walked through almost all of the code
|
|
required to construct a bus network in our @code{tutorial-csma-echo.cc}
|
|
example, so let's just jump forward and take a look at an implementation
|
|
of this thing. We provide an implementation for you in the files
|
|
@code{ipv4-bus-network.h} and @code{ipv4-bus-network.cc} located in the
|
|
@code{tutorial} directory. We also provide an example that uses the new
|
|
class in the file @code{tutorial-bus-network.cc}.
|
|
|
|
The interesting method from our current perspective is the Ipv4BusNetwork
|
|
constructor, shown below:
|
|
|
|
@verbatim
|
|
Ipv4BusNetwork::Ipv4BusNetwork (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address baseAddress,
|
|
DataRate bps,
|
|
Time delay,
|
|
uint32_t n)
|
|
:
|
|
Ipv4Network (network, mask, baseAddress)
|
|
{
|
|
Ipv4AddressGenerator::SeedNetwork (mask, network);
|
|
Ipv4AddressGenerator::SeedAddress (mask, baseAddress);
|
|
|
|
m_channel = CsmaTopology::CreateCsmaChannel (bps, delay);
|
|
|
|
for (uint32_t i = 0; i < n; ++i)
|
|
{
|
|
Ptr<Node> node = CreateObject<InternetNode> ();
|
|
uint32_t nd = CsmaIpv4Topology::AddIpv4CsmaNetDevice (node, m_channel,
|
|
Mac48Address::Allocate ());
|
|
Ipv4Address address = Ipv4AddressGenerator::AllocateAddress (mask,
|
|
network);
|
|
CsmaIpv4Topology::AddIpv4Address (node, nd, address, mask);
|
|
m_nodes.push_back (node);
|
|
}
|
|
}
|
|
@end verbatim
|
|
|
|
Notice that we do the simple and straightforward thing and pass all of our
|
|
parameters to the constructor. For those unfamiliar with C++, the line after
|
|
the colon and before the opening brace (shown below),
|
|
|
|
@verbatim
|
|
:
|
|
Ipv4Network (network, mask, baseAddress)
|
|
{
|
|
@end verbatim
|
|
|
|
Passes the appropriate parameters to the constructor of the base class
|
|
@code{Ipv4Network}. There are two new calls that we haven't seen immediately
|
|
after this initialization. They are:
|
|
|
|
@verbatim
|
|
Ipv4AddressGenerator::SeedNetwork (mask, network);
|
|
Ipv4AddressGenerator::SeedAddress (mask, baseAddress);
|
|
@end verbatim
|
|
|
|
We provide an IP address generator class to allow us to programatically
|
|
allocate IP addresses. The first call to @code{SeedNetwork} gives the
|
|
address generator a starting network number to use when generating addresses.
|
|
The second call to @code{SeedAddress} gives the address generator a starting
|
|
IP address to use. There is a starting network and starting address for each
|
|
of the 32 possible network masks. Later in the for loop, you will see a
|
|
call to @code{AllocateAddress} in which the IP address for each node created
|
|
in the loop is actually generated.
|
|
|
|
The only unfamiliar call in the reset of the constructor will be:
|
|
|
|
@verbatim
|
|
m_nodes.push_back (node);
|
|
@end verbatim
|
|
|
|
This is the STL code to add the newly created node to the vector of nodes
|
|
attached to the bus.
|
|
|
|
For your convenience, we reproduce the entire bus network implementation below:
|
|
|
|
@verbatim
|
|
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2007 University of Washington
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include "ns3/mac48-address.h"
|
|
#include "ns3/csma-net-device.h"
|
|
#include "ns3/csma-topology.h"
|
|
#include "ns3/csma-ipv4-topology.h"
|
|
|
|
#include "ipv4-bus-network.h"
|
|
#include "ipv4-address-generator.h"
|
|
|
|
namespace ns3 {
|
|
|
|
Ipv4Network::Ipv4Network (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address address)
|
|
:
|
|
m_network (network), m_mask (mask), m_baseAddress (address)
|
|
{
|
|
}
|
|
|
|
Ipv4Network::~Ipv4Network ()
|
|
{
|
|
}
|
|
|
|
Ipv4BusNetwork::Ipv4BusNetwork (
|
|
Ipv4Address network,
|
|
Ipv4Mask mask,
|
|
Ipv4Address baseAddress,
|
|
DataRate bps,
|
|
Time delay,
|
|
uint32_t n)
|
|
:
|
|
Ipv4Network (network, mask, baseAddress)
|
|
{
|
|
Ipv4AddressGenerator::SeedNetwork (mask, network);
|
|
Ipv4AddressGenerator::SeedAddress (mask, baseAddress);
|
|
|
|
m_channel = CsmaTopology::CreateCsmaChannel (bps, delay);
|
|
|
|
for (uint32_t i = 0; i < n; ++i)
|
|
{
|
|
Ptr<Node> node = CreateObject<InternetNode> ();
|
|
uint32_t nd = CsmaIpv4Topology::AddIpv4CsmaNetDevice (node, m_channel,
|
|
Mac48Address::Allocate ());
|
|
Ipv4Address address = Ipv4AddressGenerator::AllocateAddress (mask,
|
|
network);
|
|
CsmaIpv4Topology::AddIpv4Address (node, nd, address, mask);
|
|
m_nodes.push_back (node);
|
|
}
|
|
}
|
|
|
|
Ipv4BusNetwork::~Ipv4BusNetwork ()
|
|
{
|
|
}
|
|
|
|
Ptr<Node>
|
|
Ipv4BusNetwork::GetNode (uint32_t n)
|
|
{
|
|
return m_nodes[n];
|
|
}
|
|
|
|
}; // namespace ns3
|
|
@end verbatim
|
|
|
|
@section Using Ipv4BusNetwork
|
|
If all you ever want to do with a bus network can be captured in a topology
|
|
with four nodes on the bus, the preceeding section may seem like a colossal
|
|
waste of time. This is probably not the case, though. Now that we have a
|
|
relatively abstract bus class, we can create bus networks with 4, 40 or 4000
|
|
nodes with no additional effort.
|
|
|
|
A use of the bus network class is shown in the file
|
|
@code{bus-netowrk.cc} located in the @code{tutorial} directory. The
|
|
interesting code is,
|
|
|
|
@verbatim
|
|
Ipv4BusNetwork bus ("10.1.0.0", "255.255.0.0", "0.0.0.3",
|
|
DataRate(10000000), MilliSeconds(20), 10);
|
|
@end verbatim
|
|
|
|
Here we create a bus network with the network number ``10.1.0.0'' and the
|
|
network mask ``255.255.0.0'' that completes the IP network definition. You
|
|
can consider these together as ``10.1.0.0/16'' if you prefer. The next
|
|
parameter tells the bus to start numbering IP addresses of contained nodes at
|
|
``10.1.0.3'' (remember the network number will be combined). We provided a
|
|
data rate of 10 megabits per second and a latency of 20 milliseconds.
|
|
Finally, we ask the @code{Ipv4BusNetwork} object to create ten nodes in the
|
|
network.
|
|
|
|
If you are feeling brave, go ahead and change the number of nodes to be 100,
|
|
1000, 10,000 or more to generate larger and larger networks. Before you go
|
|
too far, remember that a trace file will be generated when you run your
|
|
resulting program and ee asked the trace facility to trace all net device
|
|
receive events. This will include the reception of the broadcast ARP request
|
|
by all of the nodes in the simulation, so this can add up quickly.
|
|
|
|
@c ========================================================================
|
|
@c Summary
|
|
@c ========================================================================
|
|
|
|
@node Summary
|
|
@chapter Summary
|
|
|
|
This concludes the first part of the tutorial. We have focused on
|
|
using the @command{ns-3} system to construct various network topologies and to
|
|
simulate sendng data across the networks; and we've shown you how to use the
|
|
trace facility to get access to simulation results.
|
|
|
|
We now encourage you to play with the system a little. Experiment with what
|
|
we have provided. Build a hierarchical network simulation. Perhaps exercise
|
|
your object design skills and create a new @code{Ipv4DumbbellNetwork} class
|
|
to create dumbbell networks using the Ipv4BusNetwork class we just created.
|
|
Hint: An Ipv4DumbbellNetwork @emph{has} two @code{Ipv4BusNetwork} objects;
|
|
a left side and a right side.
|
|
|
|
In the next part of the tutorial we are going to drop down a level and begin
|
|
examining the lower levels of the system in more detail. We are going to
|
|
explain how to change the behavior of the system and eventually how to write
|
|
new models and applications. This is a good time to make sure that you
|
|
thorougly understand what we've gone over so far.
|
|
|
|
@c ========================================================================
|
|
@c Object Model
|
|
@c ========================================================================
|
|
|
|
@node Object-Model
|
|
@chapter Object Model
|
|
|
|
@cindex Object Model
|
|
There are two distinctly different meanings associated with the term Object
|
|
Model. The first speaks to the implementation of an object system --- a system
|
|
view; and the second speaks to the application programming interface (classes
|
|
or objects) one uses to access some service or system --- an application view.
|
|
|
|
As an example of the system view sense of the term, the C++ language has an
|
|
associated object model that describes how objects are laid out in memory,
|
|
how virtual functions work, how inheritance is implemented, constructor and
|
|
destructor execution ordering, template instantiation, etc.
|
|
|
|
@cindex API
|
|
@cindex DOM
|
|
@cindex Document Object Model
|
|
In the case of the application view, the Document Object Model is a good
|
|
example. In the words of W3C, the Document Object Model (DOM) is an
|
|
application programming interface (API) for HTML and XML documents. It defines
|
|
the logical structure of documents and the way a document is accessed and
|
|
manipulated.
|
|
|
|
@cindex API
|
|
@cindex COM
|
|
@cindex Component Object Model
|
|
The Component Object Model (COM) from Microsoft actually spans both meanings
|
|
of the term and extends further into policy statements. From a system
|
|
perspective, COM specifies an interface definition language, the layout of
|
|
objects virtual function tables, the formats of Globally Unique Identifiers
|
|
and also specifies lifetime management mechanisms for objects via reference
|
|
counting. From the point of view of the API, COM specifies a number of
|
|
Interfaces as well as functions such as CoCreateInstance and various
|
|
threading models. The COM specification extends to policy by disallowing
|
|
implementation inheritance.
|
|
|
|
@cindex Feynman
|
|
The @command{ns-3} object model takes the C++ language (system level) object
|
|
model as its basis, and extends that model by providing an API for software
|
|
componentry. You may find terms like Component, Interface and QueryInterface
|
|
in the following discussion, or used informally in other discussions about
|
|
@command{ns-3}. It is important to understand from the outset that this is
|
|
the @command{ns-3} object model, and not any other object model.
|
|
Richard Feynman (an American physicist) once described the behavior of matter
|
|
and light on a very small scale in the following way,
|
|
|
|
@quotation
|
|
``They do not behave like waves, they do not behave like particles, they do
|
|
not behave like clouds, or billiard balls, or weights on springs, or like
|
|
anything that you have ever seen.''
|
|
@end quotation
|
|
|
|
Just as students of quantum mechanics must rid themselves of preconceptions
|
|
regarding the behavior of matter at small scales, you should rid yourself of
|
|
any preconceptions you may have about components, interfaces and APIs for
|
|
software componentry before continuing. To paraphrase Feynman, @command{ns-3}
|
|
components do not behave like COM Components, or Java Beans, or CORBA
|
|
objects, or clouds or weights on springs, or like anything that you have
|
|
ever seen --- they are @command{ns-3} components.
|
|
|
|
@section The C++ Object Model is the Root of all Things
|
|
@command{Ns-3} is primarily a C++ system. The system is written in C++ and
|
|
one can use standard C++ mechanisms for creating and using ns-3 objects. We
|
|
do not change this at all, nor do we make any pronouncements about the
|
|
superiority of one mechanism or another. What we will do is provide
|
|
convenience functions that we think will make creating and managing simulation
|
|
objects easier.
|
|
|
|
@cindex CreateObject
|
|
Previously, you have seen objects created using the template function
|
|
@code{CreateObject} as in the following example:
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
@end verbatim
|
|
|
|
This line of code, while it may be unfamiliar to some, is pure C++. If you
|
|
were to look in the header file ptr.h, you would find the following definition
|
|
of the @code{CreateObject} template.
|
|
|
|
@verbatim
|
|
template <typename T>
|
|
Ptr<T> CreateObject (void)
|
|
{
|
|
Ptr<T> p = Ptr<T> (new T (), false);
|
|
p->SetTypeId (T::GetTypeId ());
|
|
return p;
|
|
}
|
|
@end verbatim
|
|
|
|
@cindex template
|
|
As you can see, this template creates objects of type @code{T} using the
|
|
operator @code{new}. Its a little harder to find the corresponding delete ---
|
|
it's in the file @code{object.cc} inside the method @code{Object::MaybeDelete},
|
|
but when that @code{Ptr} which you see above goes out of scope it will call
|
|
@code{Unref} and ultimately the C++ @code{delete} operator will be called.
|
|
|
|
@cindex new
|
|
@cindex delete
|
|
The ns-3 system uses the C++ @code{new} and @code{delete} operators, so there
|
|
is really no reason that you as a user of the ns-3 system are forbidden from
|
|
using these or any other C++ mechanism. If you so desire, you can take on
|
|
the responsibility for managing object lifetime (i.e., do not use the
|
|
@code{Ptr} smart pointer), work directly with the @code{new} and @code{delete}
|
|
operators and call methods like any C++ object as in the following example:
|
|
|
|
@verbatim
|
|
MyClass *obj = new MyClass ();
|
|
obj->Method();
|
|
delete obj;
|
|
@end verbatim
|
|
|
|
@cindex model
|
|
You, as a competent model author, are encouraged to use whatever methods you
|
|
think are appropriate in your private code. Remember, however, that the
|
|
public ns-3 APIs do use smart pointers to pass objects around in an effort to
|
|
reduce the burden of object lifetime management. If you do intend to export
|
|
an API publicly, you should use the same object lifetime management approaches
|
|
as those found in the ns-3 public API if only for consistency.
|
|
|
|
These APIs are there for convenience and consistency, but do not change the
|
|
fact that in ns-3 all of the objects are really just C++ objects, ultimately
|
|
created using the C++ new operator with C++ constructor semantics and are
|
|
ultimately deleted using the C++ delete operator, following C++ destructor
|
|
semantics. Although it may sometimes appear so, there is really no system-
|
|
level magic going on in ns-3. Ns-3 components and interfaces are C++ objects
|
|
just like any other object and our object model is simply a collection of APIs
|
|
built on the normal C++ object model.
|
|
|
|
@cindex Interface
|
|
@cindex Abstract Data Type
|
|
@cindex ADT
|
|
@cindex Abstract Base Class
|
|
@cindex ABC
|
|
@section Interface
|
|
There are many different ideas floating around of what exactly the term
|
|
@emph{interface} means. Originally an interface just meant a communication
|
|
boundary between two entities. As the concepts of object oriented programming
|
|
(OOP) were surfacing in the 1980s, the term interface was applied to the
|
|
collection of access methods for the modular entities that were being defined.
|
|
|
|
@cindex OOP
|
|
@cindex Object Oriented Programming
|
|
Two distinct approaches developed regarding specifying access mechanisms for
|
|
objects. The OOP purists were very concerned about object reuse and were led
|
|
to Abstract Data Types (ADT). These were eventually implemented in the case
|
|
of C++, as pure virtual methods in Abstract Base Classes (ABC). Another group
|
|
of folks was more interested in simply specifying object access methods in one
|
|
place and using inheritance as the primary reuse mechanism.
|
|
|
|
Bjarne Stroustroup, the creator of C++, embraced both approaches. He makes
|
|
the following interesting observation:
|
|
|
|
@quotation
|
|
``Many classes [@dots{}] are useful both as themselves and also as bases for
|
|
derived classes. [@dots{}] Some classes, such as class @strong{Shape},
|
|
represent abstract concepts for which objects cannot exist.''
|
|
@end quotation
|
|
|
|
@cindex PIMPL
|
|
@command{Ns-3} does not pick and enforce a particular approach. In
|
|
@command{ns-3} an interface is determined completely by a class declaration
|
|
just as any C++ object interface is declared. If you think of an object as
|
|
an abstract concept that should be implemented by derived classes, by all
|
|
means, use the Abstract Base Class approach to interface declaration. If you
|
|
think that an object should be completely concrete and you foresee no need
|
|
to ever modify its behavior, feel free to avoid declaring any methods virtual.
|
|
If you think that an object could be useful as a base class, feel free to
|
|
declare its methods virtual. If you like to use the PIMPL idiom, again, feel
|
|
free. If you want to use any combination of these techniques, feel free.
|
|
We make no restrictions.
|
|
|
|
@cindex API
|
|
When we speak of an ns-3 interface, we do not worry about interface definition
|
|
languages, or pure virtual classes, or registries we just think about C++
|
|
object declarations and their associated methods. We tend to think of
|
|
interfaces to objects as simply a private or public API. When we instantiate
|
|
an @command{ns-3} interface, it is the C++ object model that dictates how that
|
|
object is brought into existence. When a method is called on an @command{ns-3}
|
|
Interface, it is the C++ object model that dictates how that method is
|
|
dispatched.
|
|
|
|
We do, however, provide a base class that endows vanilla C++ objects with
|
|
capabilities that can be seen as conceptually similar to those provided by
|
|
Microsoft Component Model @emph{Interfaces}.
|
|
|
|
@section The Ns-3 Object and GetObject
|
|
@cindex Component Object Model
|
|
One thing that Microsoft arguably got right in the Component Object Model was
|
|
the idea of Interface aggregation and discovery via QueryInterface. We have
|
|
embraced these ideas in @command{ns-3}. This was done primarily to address a
|
|
common problem in large software systems. A good example of this problem
|
|
happens in the @command{ns-3} Node class.
|
|
|
|
@cindex OOP
|
|
@cindex weak base class
|
|
@cindex base class bloat
|
|
@cindex Swiss Army Knife class
|
|
@cindex Node
|
|
If one were to take the standard OOP view of specializing a @code{Node} into
|
|
an internet host, for example, one would typically inherit from the @code{Node}
|
|
base class and include functionality to implement such things as internet
|
|
routing and a TCP/IP protocol stack. Other types of @code{Node}s might
|
|
inherit from the node class and specialize in different ways, or further
|
|
specialize the internet host class, treating it as a base class. This can
|
|
result in a complicated inheritance tree in which some specializations are
|
|
simply not available to other branches of the tree which can make reuse
|
|
difficult or impossible. This is known as the @emph{weak base class} problem
|
|
and creates pressure to drive functionality up the inheritance tree into the
|
|
base classes. This, in turn, results in @emph{base class bloat} and the
|
|
resulting @emph{swiss army knife} base classes which end up trying to do
|
|
everything in one place.
|
|
|
|
Even if one successfully avoided these swiss army knife base classes, one
|
|
would also want to be able to treat new specializations of @code{Node}
|
|
generically in the system. This means one would pass references to the base
|
|
class (@code{Node}) across public APIs. This introduces @emph{upcasts} prior
|
|
to passing across public APIs and corresponding @emph{downcasts} on the other
|
|
side in order to gain access to required specialized functions. As the
|
|
inheritance tree becomes more complicated, this approach can cause another
|
|
related problem known as the @emph{fragile base class} problem. This happens
|
|
when changes to the base class cause unexpected problems in the various and
|
|
sundry subclasses.
|
|
|
|
These effects seem always to result in a positive feedback loop driving
|
|
everything into the base class and destroying much of the encapsulation which
|
|
is a hallmark of the object oriented approach.
|
|
|
|
@subsection Interface Composition
|
|
@cindex Node
|
|
There is a completely different way to address the Node specialization
|
|
problem. Instead of approaching the situation using inheritance, one can
|
|
look at the problem as one of composition. We can look at the @code{Node}
|
|
class as a container of sorts that holds other objects. In this case, the
|
|
objects would be instances of the classes implementing the internetwork
|
|
routing code, or the TCP/IP protocol stack described above. This approach
|
|
preserves the encapsulation and solves the weak base class, base class bloat
|
|
and fragile base class problems; but the question of method dispatch
|
|
immediately comes to mind.
|
|
|
|
@cindex delegation
|
|
In many systems, @emph{delegation} is used. The base class, @code{Node},
|
|
in this approach would provide methods that simply forward to the objects
|
|
implementing the desired functionality. This situation clearly does not
|
|
address the base class bloat problem since dispatch methods must be added
|
|
to the base class. The situation is mitigated somewhat by pushing the
|
|
implementation of the dispatch methods to contained objects, but the
|
|
fundamental problems are still present. What is really needed is a way
|
|
to compose objects but at the same time keep the interfaces to those
|
|
objects separated.
|
|
|
|
@cindex aggregation
|
|
Composition, usually called @emph{aggregation}, along with runtime Interface
|
|
discovery is the solution that Microsoft originally championed and that
|
|
@command{ns-3} has adopted --- albeit with many simplifications and a few name
|
|
changes.
|
|
|
|
@subsection Objects and Interfaces
|
|
@cindex COM
|
|
@cindex QueryInterface
|
|
Now that we have mentioned Microsoft COM and are almost obligated to mention
|
|
the terms Interface and QueryInterface. For those familiar with COM, loosely
|
|
speaking, QueryInterface is to COM as GetObject is to @command{ns-3}.
|
|
The analogy, while good conceptually, is superficial from an implementation
|
|
point of view.
|
|
|
|
@cindex Node
|
|
Addressing our current example of a @code{Node}, generically speaking, each
|
|
node needs to aggregate an object that will implement internetwork routing
|
|
and TCP/IP. The system will need to provide a mechanism for locating the
|
|
aggregated objects and allow a client to discover them.
|
|
|
|
@cindex aggregation
|
|
@cindex Object
|
|
These aggregated objects have interfaces in the C++ sense of collections of
|
|
method signatures. In @command{ns-3}, when objects are capable of
|
|
participating in this aggregation process, they are called @command{ns-3}
|
|
@code{Objects}. @code{Objects} receive the functionality required for this
|
|
participation by inheriting from the @command{ns-3} base class @code{Object}.
|
|
|
|
Note well that when we write the word @code{Object} (note the uppercase 'O' in
|
|
the spelling and the change of font) we are referring to a kind of C++ object
|
|
that has inherited the capability of participating in an aggregation. The
|
|
@command{ns-3}-specific word @code{Object} can have a significantly different
|
|
meaning than that of a vanilla C++ object outside the aforementioned
|
|
inheritance tree, and the difference is only readily apparent via context.
|
|
In this tutorial we will always write the @command{ns-3}-specific kind of
|
|
@code{Object} in a fixed font; and will write the vanilla C++ term object in
|
|
normal font. In conversation, you will need to be careful to understand which
|
|
term is meant: object or @code{Object}.
|
|
|
|
Once an object has inherited from class @code{Object} it has the ability to
|
|
@emph{host} an aggregation. This means that it has the ability to add other
|
|
@code{Objects} to its aggregation via the method @code{AggregateObject}. It
|
|
also means that it can provide a service to @emph{discover} other objects in
|
|
its aggregation via the method @code{GetObject}.
|
|
|
|
@cindex base class
|
|
Technically, the class named @code{Object} is simply a base class that you
|
|
will inherit from if you want your @code{Objects} to support aggregation and
|
|
discovery. Many systems have a base class that implements common
|
|
functionality and these base classes are typically called somthing like
|
|
Object. The @command{ns-3} version of this base class relates primarily to
|
|
@code{Object} aggregation and discovery, although it does also provide methods
|
|
to help with intrusive reference counting and tracing as well.
|
|
|
|
When a C++ object inherits from the ns-3 Object base class, it is conceptually
|
|
promoted to an ns-3 @code{Object} irrespective of how the object was declared
|
|
(e.g., as an abstract base class, concrete class, with virtual methods, etc.).
|
|
In ns-3, you should associate inheritance from the class named @code{Object}
|
|
with promotion of an object to the status of some locatable @code{Object}
|
|
rather than with the form of the class declaration.
|
|
|
|
@cindex COM
|
|
@cindex CORBA
|
|
@cindex ORBit
|
|
For those of you unfamiliar with Microsoft COM, CORBA or ORBit, this might
|
|
sound obvious. For those of with such a background, the point we are making
|
|
is that there is no such thing in @command{ns-3} as a separate Interface
|
|
declaration, no such thing as an Interface Definiition Language, no such thing
|
|
as a UUID or GUID, etc. In @command{ns-3} we just work with C++ objects that
|
|
may be given some very useful abilities by inheriting from the @command{ns-3}
|
|
base class @code{Object}. @command{Ns-3} @code{Objects} are not required to
|
|
inherit from classes composed of pure virtual methods in order to define an
|
|
Interface. It's all really just ``plain old C++.''
|
|
|
|
To summarize, when you instantiate an object that inherits from the
|
|
@code{Object} class, you will have a C++ object that has four important
|
|
properties:
|
|
|
|
@cindex AggregateObject
|
|
@cindex GetObject
|
|
@itemize @bullet
|
|
@item The @code{Object} has a C++ interface defined by the collection of method signatures in its inheritance tree;
|
|
@item The @code{Object} has some way to identify its underlying class uniquely;
|
|
@item The @code{Object} is a kind of container that has the ability to aggregate other @code{Objects} using the method @code{AggregateObject};
|
|
@item The @code{Object} exports a method called @code{GetObject} that allows for discovery of other aggregated @code{Objects}.
|
|
@end itemize
|
|
|
|
@cindex base class
|
|
@cindex Object
|
|
It is crucially important to understand what we have described here
|
|
(especially for those coming from other systems that provide similar
|
|
functionality). A given C++ class has an object access interface that is
|
|
essentially the collection of method signatures specified in its inheritance
|
|
tree. This is a C++ object model thing. Ns-3 provides a base class from
|
|
which the class in question can inherit and be promoted to the status of
|
|
@code{Object}. Once a class becomes an @code{Object} it has inherited the
|
|
ability to aggregate and search for other @code{Objects} that are added to
|
|
its aggregation.
|
|
|
|
That last detail is important. In @command{ns-3} @code{Objects} are both
|
|
containers and specifications for a object method access. We have previously
|
|
mentioned that the @code{Node} class acts as a container. In fact, the
|
|
@code{Node} class inherits from @code{Object} and is itself an @command{ns-3}
|
|
@code{Object}. So, when the @code{Node} object is created it is really an
|
|
aggregation of one @code{Object} and you can call @code{AggregateObject} or
|
|
@code{GetObject} on the resulting @code{Node} object. Along with being an
|
|
aggregation, the @code{Node} class also describes a public interface. THis
|
|
public interface (API) is declared just as any C++ object is declared, via its
|
|
class methods as specified in the inheritance tree. For those steeped in
|
|
COM or CORBA, this is where the concept of Interface works in @command{ns-3}.
|
|
Remember that it is generally true that @code{Objects} are both aggregations
|
|
and APIs.
|
|
|
|
@subsection Aggregations
|
|
@cindex aggregate
|
|
The figure below shows how an @code{Object} could be illustrated in detail.
|
|
The line with the circle at the top of the diagram represents the appearance
|
|
of the @code{Object} API to the external world. This circle and line are
|
|
together called a lollipop because of its superficial similarity to a kind of
|
|
childs candy.
|
|
|
|
@sp 1
|
|
@center @image{oneobj,,,,png}
|
|
|
|
@cindex API
|
|
You could declare this API and associated @code{Object} quite simply using a
|
|
non-virtual class as follows,
|
|
|
|
@verbatim
|
|
class A : public Object {
|
|
public:
|
|
static ns3::TypeId GetTypeId (void)
|
|
{
|
|
static ns3::TypeId tid = ns3::TypeId ("A")
|
|
.SetParent (Object::GetTypeId ())
|
|
.AddConstructor<A> ();
|
|
return tid;
|
|
}
|
|
|
|
A ()
|
|
{
|
|
}
|
|
|
|
void MethodA (void);
|
|
};
|
|
@end verbatim
|
|
|
|
The methods that are then available via the API labeled @code{A} in the
|
|
figure above are the methods inherited from the @code{Object} base class
|
|
(@code{GetObject}, @code{Ref}, and @code{Unref}) and those from class
|
|
@code{A} (@code{MethodA}).
|
|
|
|
Note that you must declare a @code{TypeId} in your @code{Object} class, and
|
|
it must be declared static to make it class-wide in scope. This @code{TypeId}
|
|
is a unifying element in the @command{ns-3} object model and uniquely
|
|
identifies @code{Objects} at run-time as being instantiated from a particular
|
|
class. We'll have much more to say about @code{TypiId} shortly.
|
|
|
|
You can think of the arc and arrow device coming off each side of the
|
|
illustrated @code{Objects} as part of a connector. These connectors allow
|
|
@code{GetObject} to search aggregations for an instance of a class type.
|
|
The figure below shows an aggregation of three @code{Objects}: A, B and C.
|
|
The class declarations for classes @code{B} and @code{C} are substantially
|
|
similar to that of class @code{A}.
|
|
|
|
@sp 1
|
|
@center @image{threeobj,,,,png}
|
|
|
|
You can visualize these @code{Objects} as being snapped together like Lego
|
|
building blocks if you like. When @code{Objects} are aggregated, a
|
|
@code{GetObject} search path is formed through the connectors. In order
|
|
to create this aggregation you will first need to create the @code{Objects}.
|
|
These are just normal, everyday C++ objects that we can create using the
|
|
@code{CreateObject} template function and manage using smart pointers. The
|
|
following code should be obvious to you by now:
|
|
|
|
@verbatim
|
|
Ptr<A> a = CreateObject<A> ();
|
|
Ptr<B> b = CreateObject<B> ();
|
|
Ptr<C> c = CreateObject<C> ();
|
|
@end verbatim
|
|
|
|
@cindex aggregation
|
|
When you create an aggregation, you pick one of the @code{Objects} of the
|
|
aggregation to think of as the container. In this case well pick @code{Object}
|
|
A. In order to aggregate an @code{Object}, you simply call the method
|
|
@code{AggregateObject} that your class has inherited from class @code{Object}.
|
|
The following code will aggregate @code{Object B} and @code{Object C} onto
|
|
the @code{Object} (and container/aggregation) @code{A}.
|
|
|
|
@cindex AggregateObject
|
|
@cindex GetObject
|
|
@cindex Object
|
|
@verbatim
|
|
a->AggregateObject (b);
|
|
a->AggregateObject (c);
|
|
@end verbatim
|
|
|
|
Thats all there is to it. Now that you have those connectors snapped
|
|
together, you can ask each of the @code{Objects} in the aggregation for any of
|
|
the other @code{Objects} in the aggregation. Lets look at a simple example:
|
|
|
|
@verbatim
|
|
Ptr<B> newB = a->GetObject<B> ();
|
|
@end verbatim
|
|
|
|
Now, the explanation of what this snippet does is not as simple as writing it.
|
|
The left hand side of this assignment declares a smart pointer to the class
|
|
@code{B} to help with memory management of the returned @code{Object} pointer.
|
|
You should be very familiar with smart pointers at this stage of the tutorial.
|
|
|
|
The right hand side illustrates how @code{GetObject} is acutally used.
|
|
The method @code{GetObject} is templated. The assocated template parameter
|
|
(between the brackets) specifies the @emph{class} that is being requested.
|
|
This is important. Since it is the class type that specifies the search
|
|
criteron, there can be only one instance of a particular class present in an
|
|
aggregation. Looking back a little, although the parameter to
|
|
@code{AggregateObject} appears to be a vanilla C++ object (@code{b} or @code{c}
|
|
above), it actually represents (is an instance of) a class that has an
|
|
associated @code{TypeId} and inherits from @code{Object}. When you call
|
|
@code{GetObject} you specify the search criterion (using the template
|
|
parameter) as a class name. This referenced class must also have an
|
|
associated @code{TypeId} and must also have inherited from @code{Object}.
|
|
|
|
This may be summarized by saying that @code{AggregateObject} takes an
|
|
@emph{instance} of an object of a particular class that inherits from
|
|
@code{Object}. GetObject looks for a @emph{class} of a particular type
|
|
(that again inherits from @code{Object}) and possibly returns an aggregated
|
|
object instance of that type.
|
|
|
|
Now that you have those conceptual connectors snapped together, you can ask
|
|
each of the @code{Objects} in the aggregation for any of the @code{Objects}
|
|
in the aggregation. For example we could walk the @code{Objects} asking each
|
|
for the next in the aggregation. First we would ask the @code{Object} pointed
|
|
to by the smart pointer @code{a} to look for the @code{Object} @code{class B}:
|
|
|
|
@verbatim
|
|
Ptr<B> newB = a->GetObject<B> ();
|
|
@end verbatim
|
|
|
|
Next, we can ask the @code{Object} pointed to by the smart pointer @code{newB}
|
|
to look for the @code{Object} representing @code{class C}:
|
|
|
|
@verbatim
|
|
Ptr<C> newC = newB->GetObject<C> ();
|
|
@end verbatim
|
|
|
|
@cindex Object
|
|
Then, we can ask the @code{Object} pointed to by the smart pointer @code{newC}
|
|
to look for the @code{Object} representing @code{class A} and complete our
|
|
circuit of the aggregation:
|
|
|
|
@verbatim
|
|
Ptr<A> newA = newC->GetObject<A> ();
|
|
@end verbatim
|
|
|
|
@cindex GetObject
|
|
@code{GetObject} has some important properties that we need to go over.
|
|
Technically, @code{GetObject} is a @emph{symmetric}, @emph{reflexive} and
|
|
@emph{transitive} operation with respect to the set of aggregated
|
|
@code{Objects}.
|
|
|
|
@subsubsection Symmetry
|
|
@cindex symmetry
|
|
The symmetric nature of @code{GetObject} guarantees that if one performs a
|
|
@code{GetObject} on a given @code{Object} for the class of that same
|
|
@code{Object}, that @code{GetObject} must succeed. In other words, the
|
|
fact that you accessed the aggregation via an instance of an @code{Object A}
|
|
in the aggregation implies the reachability of that @code{Object} in the
|
|
aggregation. This is usually written (by Microsoft) as,
|
|
|
|
@center must succeed (A >> A)
|
|
|
|
We can illustrate this property with the code snippet,
|
|
|
|
@verbatim
|
|
Ptr<A> symmetricA = a->GetObject<A> ();
|
|
NS_ASSERT (symmetricA);
|
|
@end verbatim
|
|
|
|
Here we take as given an interface (smart) pointer --- named @code{a} --- on
|
|
which we perform a @code{GetObject} looking for the class that represents that
|
|
same @code{Object}. This call must always succeed and a smart pointer to the
|
|
aggregated instance of that class is returned.
|
|
|
|
@subsubsection Reflexivity
|
|
@cindex reflexivity
|
|
Calls to @code{GetObject} must also be reflexive. This means that if you
|
|
successfully @code{GetObject} for @code{Object B} from @code{Object A}, then
|
|
you must always be able to @code{GetObject} for @code{A} from @code{B}. This
|
|
is usually written as,
|
|
|
|
@center must succeed (A >> B, then B >> A)
|
|
|
|
This property can be illustrated with the code snippet,
|
|
|
|
@verbatim
|
|
Ptr<B> b = a->GetObject<B> ();
|
|
Ptr<A> reflexiveA = b->GetObject<A> ();
|
|
NS_ASSERT (reflexiveA);
|
|
@end verbatim
|
|
|
|
If the first @code{GetObject} on @code{Object A} looking for @code{Object B}
|
|
succeeds, then a @code{GetObject} on @code{Object B} looking @code{Object A}
|
|
must succeed.
|
|
|
|
@subsubsection Transitivity
|
|
@cindex transitivity
|
|
@code{GetObject} must also be transitive. This means that if one can
|
|
find @code{Object B} from @code{Object A}, and @code{Object C} from
|
|
@code{Object B}, then one must also be able to find @code{Object C} from
|
|
@code{Object A}. This is usually written as,
|
|
|
|
@center must succeed (A >> B, and B >> C, then A >> C)
|
|
|
|
This property can be illustrated with the code snippet,
|
|
|
|
@verbatim
|
|
Ptr<B> b = a->GetObject<B> ();
|
|
Ptr<C> c = b->GetObject<C> ();
|
|
Ptr<C> transitiveC = a->GetObject<C> ();
|
|
NS_ASSERT (transitiveC);
|
|
@end verbatim
|
|
|
|
If you can get to @code{Object B} from @code{Object A}, and you can get to
|
|
@code{Object C} from @code{Object B}, then a @code{GetObject} on
|
|
@code{Object A} looking for @code{Object C} must also succeed.
|
|
|
|
@subsection Creating the TypeId
|
|
@cindex TypeId
|
|
@cindex GetTypeId
|
|
The final piece of this puzzle is the @code{TypeId}. Recall that the
|
|
declaration our eample object above included the following code
|
|
|
|
@verbatim
|
|
static ns3::TypeId GetTypeId (void)
|
|
{
|
|
static ns3::TypeId tid = ns3::TypeId ("A")
|
|
.SetParent (Object::GetTypeId ())
|
|
.AddConstructor<A> ();
|
|
return tid;
|
|
}
|
|
@end verbatim
|
|
|
|
This is the bit of code that ties this all together. For those unfamiliar
|
|
with the idioms involved, this declaration can be rather dense. First, let's
|
|
examine the function declaration itself. The following code,
|
|
|
|
@verbatim
|
|
static ns3::TypeId GetTypeId (void) ...
|
|
@end verbatim
|
|
|
|
declares a function that will be associated with all of the instances of the
|
|
given class. This is a function, not a method, in that it can be accessed
|
|
without a @emph{this} pointer; but it is associated with the class in a
|
|
namespace sense. The use of this kind of declaration allows one to write,
|
|
|
|
@verbatim
|
|
return A::GetTypeId (void);
|
|
@end verbatim
|
|
|
|
if the @code{TypeId} is needed for our @code{class A}. More generically the
|
|
class name can be substituted in a template, as is done deep in the
|
|
@command{ns-3} object system.
|
|
|
|
From this perspective, if you leave out the middle of the function definition,
|
|
the boundaries should make sense to you.
|
|
|
|
@verbatim
|
|
static ns3::TypeId GetTypeId (void)
|
|
{
|
|
return tid;
|
|
}
|
|
@end verbatim
|
|
|
|
@cindex function-local variable
|
|
You are obviously looking at a global function associated with your class
|
|
that simply returns a @code{TypeId}. Now, what about the rest. The code
|
|
|
|
@verbatim
|
|
static ns3::TypeId tid = ns3::TypeId ("A")
|
|
.SetParent (Object::GetTypeId ())
|
|
.AddConstructor<A> ();
|
|
@end verbatim
|
|
|
|
when found inside the function declaration is called a function-local variable
|
|
with associated initialization. It'll be easier to pick this statement apart
|
|
piece by piece as well. The first line,
|
|
|
|
@verbatim
|
|
static ns3::TypeId tid = ...
|
|
@end verbatim
|
|
|
|
is the declaration of the function-local variable tid. This is essentially
|
|
an initialized global variable, the scope of which has been reduced to within
|
|
the enclosing method. You can think of this as a kind of global variable
|
|
that can only be accessed right there where it is created. If the variable
|
|
is initialized, this amounts to the same behavior as if a global static
|
|
initializer was declared in a namespace of the same name as your class.
|
|
Global static initializers are guaranteed by the C++ language definition to
|
|
be executed before your main procedure is entered. So are function-local
|
|
variables.
|
|
|
|
The variable that is being initialized is of type @code{ns3::TypeId}, is
|
|
named @code{A::tid} since it is inside the class declaration for
|
|
@code{class A}, and is initialized by a call to the constructor for the class
|
|
@code{TypeId}. The constructor for @code{TypeId} takes a @code{std::string}
|
|
that can be used to locate the type information for your class. We usually
|
|
privide the class name as the string.
|
|
|
|
Hopefully, this much of the declaration is now clear:
|
|
|
|
@verbatim
|
|
static ns3::TypeId GetTypeId (void)
|
|
{
|
|
static ns3::TypeId tid = ns3::TypeId ("A")
|
|
...
|
|
return tid;
|
|
}
|
|
@end verbatim
|
|
|
|
All that is left now are the lines including @code{SetParent} and
|
|
@code{AddConstructor}.
|
|
|
|
@verbatim
|
|
static ns3::TypeId tid = ns3::TypeId ("A")
|
|
.SetParent (Object::GetTypeId ())
|
|
.AddConstructor<A> ();
|
|
@end verbatim
|
|
|
|
The last bit may seem quite odd at first glance, but don't let the way the
|
|
code is broken up over several lines throw you. If you saw something like,
|
|
|
|
@verbatim
|
|
pointer->TypeId()->SetParent()->AddConstructor();
|
|
@end verbatim
|
|
|
|
you probably wouldn't hesitate at all. Clearly, you would think, a method
|
|
called @code{TypeId} is called using the pointer called @code{pointer} as
|
|
shown below.
|
|
|
|
@verbatim
|
|
pointer->TypeId()
|
|
@end verbatim
|
|
|
|
The method @code{TypeId} must further return a pointer to an object that has
|
|
a method called @code{SetParent}. Just as clearly, @code{SetParent} must
|
|
return a pointer to an object that has a method called @code{AddConstructor}.
|
|
The same sort of thing is happening in our code snipped, except we are using
|
|
references instead of pointers. Perhaps if we rearrange this code to live on
|
|
one line it will be clearer.
|
|
|
|
@verbatim
|
|
ns3::TypeId ("A").SetParent (Object::GetTypeId ()).AddConstructor<A> ();
|
|
@end verbatim
|
|
|
|
It's just a string of method calls. The remaining question is then, what do
|
|
those three methods do.
|
|
|
|
The first, @code{ns3::TypeId ("A")}, simply allocates a new type in the system
|
|
and allows you to refer to it in the future by a string. We have mentioned
|
|
inheritance trees often in the previous discussion. The second method,
|
|
@code{SetParent} associates the class being defined with its parents in the
|
|
tree. Finally, the @code{AddConstructor} method allows you to specify a
|
|
constructor to be used when an instance of your class is created using
|
|
@code{CreateObject}.
|
|
|
|
@verbatim
|
|
AddConstructor<A> ();
|
|
@end verbatim
|
|
|
|
You can interpret this as explaining to the @command{ns-3} object ssytem that
|
|
you have a constructor named @code{A::A} which takes no parameters. You are
|
|
saying that this constructor should be used when @code{CreateObject} is called
|
|
with no parameters.
|
|
|
|
By including the structure of the inheritance tree, in @command{ns-3} we can
|
|
use implementation inheritance to easily create new @code{Objects}. You are
|
|
prevented from doing so in Microsoft COM, but this was almost universally
|
|
identified as a problem.
|
|
|
|
So, looking at the entire @code{GetTypeId} declaration again,
|
|
|
|
@verbatim
|
|
static ns3::TypeId GetTypeId (void)
|
|
{
|
|
static ns3::TypeId tid = ns3::TypeId ("A")
|
|
.SetParent (Object::GetTypeId ())
|
|
.AddConstructor<A> ();
|
|
return tid;
|
|
}
|
|
@end verbatim
|
|
|
|
it should be clear what is happening.
|
|
|
|
@subsection A Very Real Example
|
|
@cindex Node
|
|
@cindex AggregateObject
|
|
@cindex GetObject
|
|
@cindex Object
|
|
At this point you may be asking yourself what the point of all of this is,
|
|
since you already had those pointers laying around when you created the
|
|
objects. The typical case is that one will create and aggregate some number
|
|
of @code{Objects} in a constructor and return only a pointer to a single
|
|
@code{Object} as in our canonical example with @code{class Node}. In this
|
|
case, the @code{Node} would be created and the @code{Node} constructor might
|
|
create and call @code{AggregateObject} to aggregate the @code{Objects} for
|
|
internetwork routing and TCP/IP. From an external point of view, these
|
|
aggregated objects may be discovered at run-time using @code{GetObject}.
|
|
|
|
Generally one tends to think of one of the @code{Objects} in the aggregation
|
|
as being the container and other @code{Objects} being aggregated to that
|
|
container. In the case of a Node, for example, it is quite natural to think
|
|
of the Node as being the container which contains protocol stacks, internet
|
|
routing, etc. So, lets start thinking about a real example by calling the
|
|
container @code{Object Node} instead of @code{A} as we have been. The
|
|
creation of this @code<Object> is found all over our example programs. For
|
|
example, you will find code like the following in
|
|
@code{samples/simple-point-to-point.cc}:
|
|
|
|
@verbatim
|
|
Ptr<Node> n = CreateObject<InternetNode> ();
|
|
@end verbatim
|
|
|
|
It may appear obvious to you now that the @code{InternetNode} class name
|
|
provided to the template function @code{CreateObject} means that
|
|
@code{InternetNode} is an @command{ns-3} @code{Object} and you will be able to
|
|
call @code{GetObject} on the resulting smart pointer. Well, I'm afraid that's
|
|
not entirely true. It's slightly more complicated.
|
|
|
|
Take a look at @code{src/internet-node/internet-node.h} and find the class
|
|
declaration for @code{InternetNode}.
|
|
|
|
@verbatim
|
|
class InternetNode : public Node
|
|
{
|
|
public:
|
|
InternetNode();
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
@cindex GetTypeId
|
|
@cindex TypeId
|
|
@cindex Object
|
|
There is no declaration of a @code{static TypeId GetTypeId (void)} in this
|
|
class. This means that the @code{InternetNode} is really not an @code{Object}
|
|
for which you can @code{GetObject}. It turns out that the @code{InternetNode}
|
|
is an @emph{implementation class} of the @code{Node Object}.
|
|
|
|
You may recall that there can be an implicit cast in a smart pointer
|
|
assignment if the cast is to a visible, unambiguous base class. That is, in
|
|
fact, what is happening here. Now, take a look at @code{src/node/node.h} and
|
|
find the class declaration for @code{class Node}. There you will find,
|
|
|
|
@verbatim
|
|
class Node : public Object
|
|
{
|
|
public:
|
|
static TypeId GetTypeId (void);
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
Class @code{InternetNode} inherits from class @code{Node} that, in turn,
|
|
inherits from class @code{Object}. It is @code{Node} that provides a
|
|
@code{GetTypeId} method. Therefore it is @code{Node} that is an
|
|
@command{ns-3} @code{Object}. Note well that @code{InternetNode} is not an
|
|
@code{Object} in the sense that one should call @code{GetObject} on an
|
|
aggregation looking for an @code{InternetNode} class. That is, you should not
|
|
do,
|
|
|
|
@verbatim
|
|
Ptr<InternetNode> i = node->GetObject<InternetNode> ();
|
|
@end verbatim
|
|
|
|
since there really is not InternetNode::GetTypeId. It is @code{Node} that is
|
|
the @emph{proper} @code{Object} in this case and you should view
|
|
@code{InternetNode} as an implementation of the @code{Node Object}. This may
|
|
become clearer as we look a little deeper.
|
|
|
|
We spoke of a protocol stack that is aggregated to a @code{Node} in our
|
|
discussions above, what we see in the real @command{ns-3} code is that this
|
|
is represented by the @code{Ipv4 Object}. If you look in
|
|
@code{src/node/ipv4.h} you will find,
|
|
|
|
@verbatim
|
|
class Ipv4 : public Object
|
|
{
|
|
public:
|
|
static TypeId GetTypeId (void);
|
|
...
|
|
};
|
|
@end verbatim
|
|
|
|
Since class @code{Ipv4} inherits from class @code{Object} and has a
|
|
@code{GetTypeId}, it is an @command{ns-3} @code{Object}. If you look in
|
|
@code{src/node/ipv4.cc} you will find,
|
|
|
|
@verbatim
|
|
TypeId
|
|
Ipv4::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("Ipv4")
|
|
.SetParent<Object> ();
|
|
return tid;
|
|
}
|
|
@end verbatim
|
|
|
|
After all of this reading you know that this code snippet is asking the
|
|
system to create a unique @code{TypeId} for the @code{Ipv4} class and
|
|
declares that @code{Ipv4} inherits from class @code{Object}. This is what
|
|
makes an @code{Ipv4} an @code{Object}.
|
|
|
|
@cindex Ipv4
|
|
It turns out that the Ipv4 class is an abstract base class (ABC). There are
|
|
a number of pure virtual methods declared in that class. This means that
|
|
an @code{Ipv4} object may not be instantiated. This is reflected by the fact
|
|
that there are no constructors registered in the @code{GetTypeId} method above.
|
|
What is instantiated in the real system is an implementation class, called
|
|
@code{Ipv4Impl}. This class inherits from @code{Ipv4} and provides the
|
|
required virtual methods. This is where understanding what is an
|
|
@code{Object} and what is not can get tricky. The @code{Object} is the
|
|
@code{Ipv4} class since that is where the @code{GetTypeId} is found. The fact
|
|
that you see @code{GetTypeId} there tells you that the @code{Ipv4} class is
|
|
the class for which you can @code{GetObject}.
|
|
|
|
@cindex implementation class
|
|
The class @code{Ipv4Impl} provides an implementation for the pure virtual
|
|
methods in @code{Ipv4}. Since class @code{Ipv4} cannot be instantiated, one
|
|
instantiates the @code{Ipv4Impl} class to create an @code{Ipv4} @code{Object}.
|
|
You will use the @code{CreateObject} template function to create an object that
|
|
implements the methods of an @code{Object}. You can probably see how this
|
|
gets even more tricky in conversation.
|
|
|
|
Once the @code{Ipv4Impl} object is instantiated, the resulting pointer is
|
|
immediately cast to an @code{Ipv4} pointer. Clients will then use the
|
|
methods specified in the @code{Ipv4} class to access the @code{Ipv4 Object}
|
|
methods which are, in turn, implemented in the @code{Ipv4Impl} object.
|
|
|
|
If you now look in the file, @code{src/internet-node/internet-node.cc} you
|
|
will see the following code in @code{InternetNode::Construct} that creates the
|
|
@code{Ipv4} Interface and aggregates it.
|
|
|
|
@verbatim
|
|
Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> (ipv4);
|
|
...
|
|
Object::AggregateObject (ipv4Impl);
|
|
@end verbatim
|
|
|
|
Note that the parameter @code{ipv4} passed to the @code{CreateObject} template
|
|
function is actually a pointer to an @code{Ipv4L3Protocol} which you can
|
|
ignore at this point --- it doesn't really have anything to do with the
|
|
@code{Ipv4} Interface.
|
|
|
|
This is exactly the same thing that is happening in the case of the
|
|
@code{InternetNode}.
|
|
|
|
@verbatim
|
|
Ptr<Node> n = CreateObject<InternetNode> ();
|
|
@end verbatim
|
|
|
|
@cindex implementation object
|
|
@code{CreateObject} is being called to create an implementation object,
|
|
in this case @code{InternetNode}, which implements the methods of the
|
|
@code{Node Object}. It is the resulting @code{Node Object} which you would
|
|
use as the container and it is the @code{Node} class that you would use as
|
|
the template parameter when calling @code{GetObject}. In the same way, you
|
|
would @emph{not} want to do,
|
|
|
|
@verbatim
|
|
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4Impl> ();
|
|
@end verbatim
|
|
|
|
Rather you should understand that the @emph{proper} @code{Object} is the
|
|
@code{Ipv4} not the @code{Ipv4Impl} and do the following,
|
|
|
|
@verbatim
|
|
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
|
|
@end verbatim
|
|
|
|
@cindex CreateObject
|
|
This does illustrate that the fact that whether an object created by
|
|
@code{CreateObject} is or is not an @code{Object} in the usual sense can be
|
|
quite well hidden if you are casually looking at the object creation code.
|
|
The designers of the system had long and involved discussions on this issue
|
|
and in the end decided that mnemonic aids such as Hungarian notation were a
|
|
stylistic thing and you should just refer to the system documentation to
|
|
determine what objects are @command{ns-3} @code{Objects} and what the APIs
|
|
of those @code{Objects} actually are (RTFM --- as in Read the Fine Manual,
|
|
of course).
|
|
|
|
@cindex AggregateObject
|
|
@cindex Object
|
|
In the case of @code{Ipv4Impl}, you know that the class inherits somehow
|
|
from @code{Object} since there is a call to @code{AggregateObject} that
|
|
refers to an instance of an @code{Ipv4Impl}. You will have to go to
|
|
the header file @code{src/internet-node/ipv4-impl.h} and find that
|
|
@code{Ipv4Impl} inherits from class @code{Ipv4}. You will then have go to
|
|
the file @code{src/node/ipv4.h} and see that it inherits from @code{Object} and
|
|
defines a @code{GetTypeId}. Thus the @code{Object} for which you can
|
|
@code{GetObject} is really the @code{Ipv4 Object}.
|
|
|
|
Returning to some real @command{ns-3} example code, lets take a look at
|
|
@code{examples/simple-point-to-point.cc}. You will find the following
|
|
code in this file:
|
|
|
|
@verbatim
|
|
Ptr<Node> n0 = CreateObject<InternetNode> ();
|
|
...
|
|
Ptr<Ipv4> ipv4;
|
|
ipv4 = n0->GetObject<Ipv4> ();
|
|
ipv4->SetDefaultRoute (Ipv4Address (``10.1.1.2''), 1);
|
|
@end verbatim
|
|
|
|
@cindex InternetNode
|
|
@cindex Node
|
|
@cindex Object
|
|
@cindex GetObject
|
|
The first line creates an @code{InternetNode} implementation object and casts
|
|
the resulting smart pointer to a @code{Node} as we have discussed extensively.
|
|
The next line shown declares a smart pointer to an @code{Ipv4 Object}. We
|
|
then do a @code{GetObject} on the @code{Node} looking for the
|
|
@code{Ipv4 Object}. You know since you've read every line of this tutorial
|
|
in detail exactly how that @code{Ipv4 Object} got into every @code{Node}. You
|
|
know that the @code{GetObject} will return a smart pointer to its aggregated
|
|
@code{Ipv4} Interface. Once we have the @code{Ipv4} smart pointer, we simply
|
|
use it as if it were any other C++ object. The last line shows this by
|
|
setting the default route for the node.
|
|
|
|
@section Caveats
|
|
There are a few things that you should remember but which may not be
|
|
immediately obvious.
|
|
|
|
@subsection Ns-3 Objects are Associated with Classes not C++ objects
|
|
@cindex Object
|
|
@cindex GetObject
|
|
@cindex iterate
|
|
@cindex aggregation
|
|
@cindex GetNDevices
|
|
Okay, you can see some of the problems with the terminology popping up again.
|
|
We are reminding you that when you do a GetObject you are providing the key
|
|
to the lookup by giving a class name and not anything that is unique to a
|
|
C++ object.
|
|
|
|
You cannot add more than one @code{Object} of a given type (class name) to an
|
|
aggregation. If you need to contain a number of @code{Objects} of the same
|
|
type in the same aggregation, you will need to provide a separate container
|
|
over which you can iterate. For example, the @code{Node} class provides
|
|
methods,
|
|
|
|
@verbatim
|
|
uint32_t GetNDevices (void) const;
|
|
Ptr<NetDevice> GetDevice (uint32_t index) const;
|
|
@end verbatim
|
|
|
|
that are used iterate over the multiple @code{NetDevice} @code{Objects}
|
|
associated with it.
|
|
|
|
@emph{Remember: Object types do not identify objects.}
|
|
|
|
@subsection Dont use GetObject to Check Your Own Type.
|
|
@cindex GetObject
|
|
It is tempting to use @code{GetObject} as a form of runtime type
|
|
information. Dont do it. You have no control over what @emph{other}
|
|
object may be added to your aggregation. Someone else may have
|
|
appropriated (reimplemented) your type and aggregated themselves onto the
|
|
aggregation.
|
|
|
|
Consider a socket factory implementation. Sockets can be either UDP sockets
|
|
or TCP sockets. A socket factory will have a generic @code{SocketFactory}
|
|
Object and either a UDP specific interface for setting UDP parameters or a
|
|
similar TCP-specific interface.
|
|
|
|
Consider what might happen if you declared your socket factory as a partially
|
|
abstract base class, and then provided separate implementations for UDP and
|
|
TCP specific methods of this factory in separate concrete classes. Now
|
|
consider what might happen if you used @code{GetObject} in your base class
|
|
to determine if you were a UDP or a TCP factory.
|
|
|
|
If a factory, say the UDP version, were not aggregated to any other
|
|
@code{Object}, the base class could @code{GetObject} on itself for the
|
|
UDP-specific class name. If the @code{GetObject} succeeded, it could then
|
|
infer that it was a UDP implementation and would then do any UDP-specific
|
|
tasks it could. [Experienced C++ folks are cringing about how
|
|
horrible this design is, but bear with me --- its a simple illustration of
|
|
a specific and perhaps not-too-obvious problem.]
|
|
|
|
If another factory, say the TCP version, were not aggregated to any other
|
|
Interface, the base class could @code{GetObject} on itself for the UDP-specific
|
|
interface. If this failed, it could then infer that it had a TCP
|
|
implementation and would then do any TCP-specific tasks it could.
|
|
|
|
Now, what happens when these two working objects are aggregated together by
|
|
some innocent end-user. Since the @code{Objects} are conceptually snapped
|
|
together, the TCP implementation would suddenly begin finding the UDP
|
|
Interface from the other class factory and think it was the UPD implementation.
|
|
|
|
@emph{Objects should not be used as run-time type information.}
|
|
|
|
@section Connecting the Dots
|
|
@cindex Object
|
|
@cindex GetObject
|
|
@cindex AggregateObject
|
|
@cindex GetTypeId
|
|
@cindex API
|
|
This may all sound very complicated to you if this is your first exposure to
|
|
these concepts. It may be annoying if I tell you that its really not as hard
|
|
as it sounds. Rest assured that if you take some time, look at and understand
|
|
the examples and write a little test code it will all come together for you.
|
|
Grep around the system for @code{AggregateObject} and @code{GetObject} and
|
|
take a look at how we have used them. This will also give you a good idea of
|
|
what our core @code{Objects} and associated APIs are. If you grep for
|
|
@code{GetTypeId} you will find most, if not all of the @code{Object} API
|
|
interface declarations in the system. The more you see this idiom in
|
|
use, the more comfortable you will be with the idea and the more you will see
|
|
how this addresses the weak base class, swiss army knife base class, and
|
|
fragile base class problems I explained at the beginning.
|
|
|
|
As I alluded to earlier, the developers had long discussions regarding how to
|
|
make navigating the @code{Object} environment easier. The primary issue was
|
|
how we could make it easier to convey to you, the model writer, that an object
|
|
was an @code{Object}. We originally used similar terminology as Microsoft
|
|
COM and used QueryInterface instead of @code{GetObject}. One suggestion was
|
|
to adopt the convention that classes that implemented Interfaces must begin
|
|
with the letter I. Microsoft does this, as exemplified by the class IUnknown.
|
|
We also toyed with the idea of beginning our header files with ``i-'' as in
|
|
``i-ipv4.h.'' We considered forcing some structure on Interfaces with a pure
|
|
virtual class specification, the names of which begin with an I; and
|
|
corresponding implementations, the names of which begin with a C. This all
|
|
got out of hand fairly quickly.
|
|
|
|
In the end we decided that we were really discussing issues of programming
|
|
style, and we really could not come up with a strong reason to impose any
|
|
particular solution. No matter what direction we took, we ended up with some
|
|
form of extra confusion or extra complexity somewhere in the system. The
|
|
resulting system is extremely flexible and easy to use. It is, unfortunately,
|
|
sometimes hard to document and talk about.
|
|
|
|
@cindex Feynman
|
|
If it helps you to think in terms of Microsoft COM and Interfaces, by all means
|
|
do so, just be aware that even though @command{ns-3} @code{Objects} descend
|
|
from COM in some sense, there are subtle differences that may get you lost or
|
|
into trouble. So to paraphrase Feynman one more time,
|
|
|
|
@quotation
|
|
``@command{Ns-3} @code{Objects} do not behave like COM Components, or Java
|
|
Beans, or CORBA objects, or clouds or weights on springs, or like anything
|
|
that you have ever seen --- they are @command{ns-3} components.''
|
|
@end quotation
|
|
|
|
Just get very familiar with the @command{ns-3} object model. It is the heart
|
|
of the system and if you do not understand it you will not understand how to
|
|
write an @command{ns-3} model properly.
|
|
|
|
@c ========================================================================
|
|
@c Doxygen
|
|
@c ========================================================================
|
|
|
|
@node The-Doxygen-Documentation-System
|
|
@chapter The Doxygen Documentation System
|
|
|
|
@node How-To-Change-Things
|
|
@chapter How to Change Things
|
|
|
|
@node How-To-Set-Default-Values
|
|
@chapter How to Set Default Values
|
|
|
|
@node How-To-Write-A-New-Application
|
|
@chapter How to Write a New Application
|
|
|