1 ns-3初识
资料:
- ns-3官网:http://www.nsnam.org/
- ns-3官方开发文档:https://www.nsnam.org/releases/ns-3-34/documentation/
- ns-3维基百科(文档中没有的内容)、FAQ问题解答:https://www.nsnam.org/support/
- ns-3相关文献:https://www.nsnam.org/education/
2 ns-3快速上手
使用waf配置编译ns-3系统:
./waf clean #清除先前的配置编译
./waf -d optimized --enable-examples --enable-tests configure #重新配置ns-3,优化编译包括例子和测试(优化模式下禁止输出)
./waf -d --enable-examples --enable-tests configure #重新配置ns-3,优化编译包括例子和测试(这样就有输出了)
./waf #正式编译
测试安装
./test.py -c core # 测试ns-3发行版是否编译正确
./waf --run scratch-simulator# 运行脚本测试
3 ns-3基础
3.1 关键概念
-
节点
Node类描述节点,提供添加外设卡、驱动程序、协议栈、应用程序等功能。
NodeContainer类,拓扑生成器,用于创建、管理
使用节点,保存一组节点指针。
NodeContainer nodes;//声明一个名为nodes的NodeContainer
node.Create (2);//调用nodes对象的Create()方法创建2个节点对象,并把2个节点对象的指针存储在系统中
-
应用
Application类描述。
UdpEchoServerHelper和UdpEchoClientHelper为Application类实例。(动物类->实例:熊猫类)
-
信道
Channel类,实例包括:CsmaChannel(模拟了载波侦听多路访问通信子网中的媒介,有以太网的功能)、PointToPointChannel(最多2个点到点连接的网络设备)、WifiChannel(无线信道)
-
网络设备(网卡)
NetDevice类,提供连接节点和信道对象的各种方法。实例有:CsmaNetDevice、PointToPointNetDevice、Wi-FiNetDevice。
NetDeviceContainer类存放(类似NodeContainer类)。
-
拓扑帮助
Helper类,用于网络设备配置到节点、连接信道、配置IP地址等。
例如TopologyReaderHelper类可以配置和使用TopologyReader;InternetStackHelper安装PointToPointHelper对象和网络协议栈。
3.2 优化技术
3.2.1 Logging系统
将执行结果输出命令行中(类似cout、printf)。
LogComponentEnable ("UdpEchoClientApplication",LOG_LEVEL_INFO); //LogComponentEnable()使记录模块有效,参数1是类组件的字符串,参数2是显示的等级(例如改为下列其他值)
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
通过环境变量修改记录系统等级
export 'NS_LOG=UdpEchoClientApplication=level_all:UdpEchoServerApplication=level_all' #export为命令,level_all等级表示显示全部的调试信息
./waf --run scratch/first
添加附加前缀
自定义Logging代码(输出自定义信息)
NS_LOG_COMPONENT_DEFINE("FirstScriptExample");//向ns-3系统注册一个名为"FirstScriptExample"的记录组件,之后才能在Logging系统中自定义输出语句。
NS_LOG_INFO("Creating Topology")//在first.cc中添加
3.2.2 命令行参数
作用:不修改脚本,用命令行传递参数来修改脚本中的变量。
CommandLine cmd;
cmd.Parse(argc,argv);//之后用户可以用命令行访问代码中的全局变量和属性系统
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
挂钩自定义变量
int main(int argc,char *argv[]){
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets","Number of packets to echo",nPackets);//使得变量nPackets可以在命令行中修改
cmd.Parse(argc, argv);
//echoClient.SetAttribute("MaxPackets",UintegerValue(1));
echoClient.SetAttribute("MaxPackets",UintegerValue(nPackets));
}
# 运行
./waf --run "scratch/first --PrintHelp"
./waf --run "scratch/first --nPackets=2"
3.2.3 Tracing系统
- 作用:将执行结果输出一个文件中。
- 3个基本概念:Tracing Sources、Tracing Sink、连接的统一机制。
- Helper类:AsciiTraceHelper生成文档
- ASCII Tracing:以ASCII格式的信息输出
//在Simulator::Run();前写入
AsciiTraceHelper ascii;//声明一个AsciiTraceHelper对象,为调用其成员函数做准备
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));//CreateFileStream()创建一个名为myfirst.tr流文件。EnableAsciiAll()通知helper将所有关于pointToPoint设备的仿真信息打印为ASCII Tracing格式。
# 运行后在ns-3.xx中多一个文件myfirst.tr
./waf --run scratch/first
-
PCAP Tracing
生成.pacp格式文件,可用于Wireshark工具打开分析。
4 ns-3仿真结果统计分析
4.1 ns-3仿真可视化工具
4.1.1 PyViz 可视化模块
# 运行
./waf --run example/tutorial/third --vis
其他信息:http://www/nsnam.org/wiki/index.php/PyViz
4.1.2 NetAnim 动画演示工具
在脚本文件中写入代码,以生成XML记录文件。
//确保程序的wscript文件包含netanim模块(关于wscript文件的例子在src/netanim/examples/wscript中)
# include "ns3/netanim-module.h"
AnimationInterface anim ("first.xml");// 设置XML文件。生成文件名为animation.xml的xml格式的追踪文件
// AnimationInterface anim ("animation.xml",50000); // 确保每个动画XML文件仅包含50000个数据分组,多了分为几个XML文件
// 以下为可选语句
anim.SetMobilityPollInterval (Seconds (1)); //设置记录节点位置的周期,默认为250ms
anim.SetConstantPosition (Ptr<Node> n, double x, double y); //设置节点的位置,ConstantPosition为移动模型中节点静态位置的x、y坐标。
anim.SetStartTime (Seconds (150)); //动画接口可以只记录仿真过程中一部分。设置动画记录的开始和结束时间
anim.SetStopTime (Seconds (200));
anim.EnablePacketMetadata (true); // 设置XML文件记录包括元数据(如TCP序号、源节点目的节点的IP地址)。启用此特性会导致xml记录文件增大,在WIMAX仿真中不建议使用
//AnimationInterface的其他方法可以看ns-3/src/netanim/examples/wireless-anemation.cc目录下的文件例子
运行:
# 在NetAnim目录下
./NetAnim
# 在ns-3.xx目录下
./waf --run first
在netanim界面中点击open->first.xml->play
详细信息:http://www.nsnam.org/wiki/index.php/NetAnim
4.2 分析追踪记录文件数据
第三方网络数据分组显示和统计分析工具,读取ns-3的trace文件,并进行统计和分析。
4.2.1 TcpDump
TcpDump (dump the traffic on a network),读取pcap文件,截获分组,分析头部,输出
系统时间来源主机.端口 > 目标主机.端口数据分组参数
使用方法:
在脚本first.cc中加入代码
在命令行中使用TcpDump
# 格式
# tcpdump [-adeflnOpqStvx][-c 数量][-F 文件名][-i 网络接口][-r 文件名][-s snaplen][-T 类型][-w 文件名][表达式]
tcpdump -nn -tt -r first-0-0.pcap
# 输出
reading from file first-1-0.pcap, link-type PPP (PPP) # 链路类型为PPP
2.003686 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024 # 数据分组从节点0(IP:10.1.1.1, port:49153)发出,到达节点4(IP:10.1.2.4, port:9)
2.003686 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024
4.2.2 Wireshark
在Wireshark界面中打开first-0-0.pace,可以看到通信过程、数据分组格式、二进制流。
工作区上部分为数据分组的简单信息,中间为详细信息(各个协议的头格式),下面为二进制显示。
详细信息:http://www.wireshark.org/
4.3 统计模块status
使用方法
-
写脚本
例子:examples/stats/wifi-example-sim.cc
/* -*- 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
*
* Authors: Joe Kopena <[email protected]>
*
* This program conducts a simple experiment: It places two nodes at a
* parameterized distance apart. 把两个节点放在指定参数距离。 One node generates packets and the
* other node receives.一个发一个收 The stat framework collects data on packet
* loss. 数据统计模块收集丢包统计数据。 Outside of this program, a control script uses that data to
* produce graphs presenting performance at the varying distances.程序之外的一个控制脚本使用数据画图,以表示距离变化。
* This isn't a typical simulation but is a common "experiment"
* performed in real life and serves as an accessible exemplar for the
* stat framework. It also gives some intuition on the behavior and
* basic reasonability of the NS-3 WiFi models.
*
* Applications used by this program are in test02-apps.h and
* test02-apps.cc, which should be in the same place as this file.
*
*/
#include <ctime>
#include <sstream>
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/mobility-module.h"
#include "ns3/internet-module.h"
#include "ns3/stats-module.h"
#include "ns3/yans-wifi-helper.h"
#include "wifi-example-apps.h"
using namespace ns3;
using namespace std;
// 定义日志组件
NS_LOG_COMPONENT_DEFINE ("WiFiDistanceExperiment");
void TxCallback (Ptr<CounterCalculator<uint32_t> > datac,
std::string path, Ptr<const Packet> packet) {
NS_LOG_INFO ("Sent frame counted in " <<
datac->GetKey ());
datac->Update ();
// end TxCallback
}
//----------------------------------------------------------------------
//-- main
//----------------------------------------------
int main (int argc, char *argv[]) {
double distance = 50.0;
string format ("omnet");
string experiment ("wifi-distance-test");
string strategy ("wifi-default");
string input;
string runID;
{
stringstream sstr;
sstr << "run-" << time (NULL);
runID = sstr.str ();
}
// 1. Set up command line parameters used to control the experiment.声明参数和使用ns3::CommandLine解析命令
CommandLine cmd (__FILE__);
cmd.AddValue ("distance", "Distance apart to place nodes (in meters).",
distance);
cmd.AddValue ("format", "Format to use for data output.",
format);
cmd.AddValue ("experiment", "Identifier for experiment.",
experiment);
cmd.AddValue ("strategy", "Identifier for strategy.",
strategy);
cmd.AddValue ("run", "Identifier for run.",
runID);
cmd.Parse (argc, argv);
if (format != "omnet" && format != "db") {
NS_LOG_ERROR ("Unknown output format '" << format << "'");
return -1;
}
#ifndef STATS_HAS_SQLITE3
if (format == "db") {
NS_LOG_ERROR ("sqlite support not compiled in.");
return -1;
}
#endif
{
stringstream sstr ("");
sstr << distance;
input = sstr.str ();
}
//------------------------------------------------------------
//-- 2. Create nodes and network stacks创建节点和网络堆栈
//--------------------------------------------
NS_LOG_INFO ("Creating nodes.");
NodeContainer nodes;//NodeContainer
nodes.Create (2);
NS_LOG_INFO ("Installing WiFi and Internet stack.");
WifiHelper wifi;//WifiHelper
WifiMacHelper wifiMac;
wifiMac.SetType ("ns3::AdhocWifiMac");
YansWifiPhyHelper wifiPhy;
YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
wifiPhy.SetChannel (wifiChannel.Create ());
NetDeviceContainer nodeDevices = wifi.Install (wifiPhy, wifiMac, nodes);
InternetStackHelper internet; // InternetStackHelper
internet.Install (nodes);
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (nodeDevices);
//------------------------------------------------------------
//-- 3. Setup physical layout
//--------------------------------------------
NS_LOG_INFO ("Installing static mobility; distance " << distance << " .");
MobilityHelper mobility;//使用MobilityHelper定位节点。默认节点有静态流动性且不能移动
Ptr<ListPositionAllocator> positionAlloc =
CreateObject<ListPositionAllocator>();
positionAlloc->Add (Vector (0.0, 0.0, 0.0));
positionAlloc->Add (Vector (0.0, distance, 0.0));
mobility.SetPositionAllocator (positionAlloc);
mobility.Install (nodes);
//------------------------------------------------------------
//-- 4. Create a custom traffic source and sink安装流量发生器(发送端)和接收器。
//--------------------------------------------
NS_LOG_INFO ("Create traffic source & sink.");
Ptr<Node> appSource = NodeList::GetNode (0);
Ptr<Sender> sender = CreateObject<Sender>();
appSource->AddApplication (sender);
sender->SetStartTime (Seconds (1));
Ptr<Node> appSink = NodeList::GetNode (1);
Ptr<Receiver> receiver = CreateObject<Receiver>();
appSink->AddApplication (receiver);
receiver->SetStartTime (Seconds (0));
// 更改数据分组的目的地,默认情况为广播。
Config::Set ("/NodeList/*/ApplicationList/*/$Sender/Destination",
Ipv4AddressValue ("192.168.0.2"));
//------------------------------------------------------------
//-- Setup stats and data collection配置要收集的统计数据
//--------------------------------------------
// Create a DataCollector object to hold information about this run.
DataCollector data;
data.DescribeRun (experiment,//“实验”信息标签,标识研究对象,Wi-Fi性能和距离
strategy,//策略:实验中被检测的参数,e.g. Wi-Fi比特率
input,//输入,2个节点之间的距离
runID);//运行ID,实验的唯一标识符。运行信息
// Add any information we wish to record about this run.
data.AddMetadata ("author", "tjkopena");
// Create a counter to track how many frames are generated. Updates
// are triggered by the trace signal generated by the WiFi MAC model
// object. Here we connect the counter to the signal via the simple
// TxCallback() glue function defined above.
Ptr<CounterCalculator<uint32_t> > totalTx =
CreateObject<CounterCalculator<uint32_t> >();
totalTx->SetKey ("wifi-tx-frames");
totalTx->SetContext ("node[0]");
Config::Connect ("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx",
MakeBoundCallback (&TxCallback, totalTx));
data.AddDataCalculator (totalTx);
// This is similar, but creates a counter to track how many frames
// are received. Instead of our own glue function, this uses a
// method of an adapter class to connect a counter directly to the
// trace signal generated by the WiFi MAC.
Ptr<PacketCounterCalculator> totalRx =
CreateObject<PacketCounterCalculator>();
totalRx->SetKey ("wifi-rx-frames");
totalRx->SetContext ("node[1]");
Config::Connect ("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx",
MakeCallback (&PacketCounterCalculator::PacketUpdate,
totalRx));
data.AddDataCalculator (totalRx);
// This counter tracks how many packets---as opposed to frames---are
// generated. This is connected directly to a trace signal provided
// by our Sender class.
Ptr<PacketCounterCalculator> appTx =
CreateObject<PacketCounterCalculator>();
appTx->SetKey ("sender-tx-packets");
appTx->SetContext ("node[0]");
Config::Connect ("/NodeList/0/ApplicationList/*/$Sender/Tx",
MakeCallback (&PacketCounterCalculator::PacketUpdate,
appTx));
data.AddDataCalculator (appTx);
// Here a counter for received packets is directly manipulated by
// one of the custom objects in our simulation, the Receiver
// Application. The Receiver object is given a pointer to the
// counter and calls its Update() method whenever a packet arrives.
Ptr<CounterCalculator<> > appRx =
CreateObject<CounterCalculator<> >();
appRx->SetKey ("receiver-rx-packets");
appRx->SetContext ("node[1]");
receiver->SetCounter (appRx);
data.AddDataCalculator (appRx);
/**
* Just to show this is here...
Ptr<MinMaxAvgTotalCalculator<uint32_t> > test =
CreateObject<MinMaxAvgTotalCalculator<uint32_t> >();
test->SetKey("test-dc");
data.AddDataCalculator(test);
test->Update(4);
test->Update(8);
test->Update(24);
test->Update(12);
**/
// This DataCalculator connects directly to the transmit trace
// provided by our Sender Application. It records some basic
// statistics about the sizes of the packets received (min, max,
// avg, total # bytes), although in this scenaro they're fixed.
Ptr<PacketSizeMinMaxAvgTotalCalculator> appTxPkts =
CreateObject<PacketSizeMinMaxAvgTotalCalculator>();
appTxPkts->SetKey ("tx-pkt-size");
appTxPkts->SetContext ("node[0]");
Config::Connect ("/NodeList/0/ApplicationList/*/$Sender/Tx",
MakeCallback
(&PacketSizeMinMaxAvgTotalCalculator::PacketUpdate,
appTxPkts));
data.AddDataCalculator (appTxPkts);
// Here we directly manipulate another DataCollector tracking min,
// max, total, and average propagation delays. Check out the Sender
// and Receiver classes to see how packets are tagged with
// timestamps to do this.
Ptr<TimeMinMaxAvgTotalCalculator> delayStat =
CreateObject<TimeMinMaxAvgTotalCalculator>();
delayStat->SetKey ("delay");
delayStat->SetContext (".");
receiver->SetDelayTracker (delayStat);
data.AddDataCalculator (delayStat);
//------------------------------------------------------------
//-- Run the simulation
//--------------------------------------------
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
//------------------------------------------------------------
//-- Generate statistics output.
//--------------------------------------------
// Pick an output writer based in the requested format.
Ptr<DataOutputInterface> output = 0;
if (format == "omnet") {
NS_LOG_INFO ("Creating omnet formatted data output.");
output = CreateObject<OmnetDataOutput>();
} else if (format == "db") {
#ifdef STATS_HAS_SQLITE3
NS_LOG_INFO ("Creating sqlite formatted data output.");
output = CreateObject<SqliteDataOutput>();
#endif
} else {
NS_LOG_ERROR ("Unknown output format " << format);
}
// Finally, have that writer interrogate the DataCollector and save
// the results.
if (output != 0)
output->Output (data);
// Free any memory here at the end of this example.
Simulator::Destroy ();
// end main
}
4.4 绘图工具Gnuplot
第三方作图工具,有gnuplot类,可产生gnuplot数据,最后由gnuplot读取数据生成图表,从而进行仿真数据统计分析。
# 安装
apt-get install Gnuplot
# 进入Gnuplot交互界面
gnuplot>
# 得到正弦曲线
plot[-3.14:3.14]sin(x)
详细信息:http://www.gnuplot.info/
ns-3提供了Gnuplot类和GnuplotDataset类
ns-3中使用方法:(例子ns-3.xx/src/tools/examples/gnuplot-example.cc)
# 运行例子
./waf --run src/tools/examples/gnuplot-example
# 结果:生成Gnuplot控制文件 plot-2d.plt plot-2d-with-error-bars.plt plot-3d.plt
# 使用gnuplot处理gnuplot控制文件
gnuplot plot-2d.plt
gnuplot plot-2d-with-error-bars.plt
gnuplot plot-3d.plt
# 处理后生成图片文件:plot-2d.png plot-2d-with-error-bars.png plot-3d.png
# 图片可以用浏览器等工具打开(e.g. gimp, Image Viewer, Shotwell)
5 ns-3内核
5.1 ns-3的组织结构
- 组织结构在src目录中实现。下层为上层服务。
- 内核模块,在src/core中实现。包括了ns-3基本机制,例如随机变量、回调、属性系统、Tracing系统等。
- 网络模块,分组在src/network中实现,讨论网络数据分组的相关问题。
- Internet模块:介绍Internet网络基本协议,包括路由、传输层。
- 移动模块:提供一些移动模型。
- 应用层模块:提供一些应用程序。
- 能量模块:关于节点能量消耗。
5.2 随机变量Random Variables
-
随机数生成器(RNG)
随机变量通过调用类ns3::RandomVariableStream提供。这个类封装了随机数生成器并提供接口。子类:UniformVariableStream和ExponetialVariableStram。
- 仿真程序中,不改变种子或运行标识的前提下,仿真程序产生的结果的一定的。想要结果不一样,ns3::RngSeedManager::SetSeed()设置种子,或ns3::RngSeedManager::SetRun()设置运行标识。
- ns-3所有的随机变量使用一个基于全局固定的或随机的种子。
- 固定的种子和标识存储在类GlobalValue的g_rngSeed和g_rngRun成员中。
- 同一仿真程序多次实验,若要实现每次实验都具有独立性,有两种方法:
方法一:每次独立重复实验时,调用函数RngSeedManager::SetSeed()设置不同的全局种子。
//在scratch文件夹下,创建文件sample-random-variable-stream.cc
# include "ns3/simulator.h"
# include "ns3/nstime.h"
# include "ns3/command-line.h"
# include "ns3/rng-seed-manager.h"
# include "ns3/random-variable-stream.h"
# include <iostream>
using namespace ns3;
int main(int argc,char *argv[])
{
CommandLine cmd;
cmd.Parse(argc, argv);
RngSeedManager::SetSeed(1);//改为SetSeed(2)结果也会变化
Ptr<UniformRandomVariable> uv=CreateObject<UniformRandomVariable>();//创建一个指向随机变量类的指针uv
std::cout << uv->GetValue() << std::endl;
return 0;
}
# 运行
./waf --run scratch/sample-random-variable-stream
# 不改代码的话,每次运行输出同样的数字[0,1),例如0.816532
方法二:每次独立重复实验时,全局种子不变,每次设置不同的标识。改变标识有几种方式:
a. 调用函数RngSeedManager::SetRun(3)设置不同的运行标识。
// 修改文件代码
RngSeedManager::SetRun(3);//这一句代替RngSeedManager::SetSeed(1);
b. 修改全局变量NS_GLOBAL_VALUE值来修改运行标识。
# 运行
# ./waf --run scratch/sample-random-variable-stream
NS_GLOBAL_VALUE="RngRun=3" ./waf --run scratch/sample-random-variable-stream # RngRun=可以取不同的值
c(推荐). 使用命令行传递参数修改运行标识
# 运行
./waf --run "scratch/sample-random-variable-stream --RngRun=3"
d. 使用build
# 运行
./build/optimized/scratch/sample-random-variable-stream --RngRun=3
随机变量
ns-3中用来声明随机变量的类,都有一个基类RandomVariableStream。其基类有:
- UniformRandomVariable: 最基本的类。给定一个最大值和最小值,按均匀分布方式返回一个随机数。
# include "ns3/double.h"
# include "ns3/simulator.h"
# include "ns3/nstime.h"
# include "ns3/command-line.h"
# include "ns3/rng-seed-manager.h"
# include "ns3/random-variable-stream.h"
# include <iostream>
using namespace ns3;
int main(int argc,char *argv[])
{
CommandLine cmd;
cmd.Parse(argc,argv);
RngSeedManager::SetSeed(3);
double min = 0.0;
double max = 10.0;
Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable> ();
//改变返回随机变量的区间为[0,10),默认为[0,1)
uv->SetAttribute("Min",DoubleValue(min));
uv->SetAttribute("Max",DoubleValue(max));
std::cout << uv->GetValue() << std::endl;
return 0;
}
- ConstantRandomVariable: 返回一个固定的数,默认为0
...
// Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable> ();
//改变返回随机变量为10,默认为0
double dValue = 10.0;
Ptr<UniformRandomVariable> uv = CreateObject<ConstantRandomVariable> ();
uv->SetAttribute("Constant",DoubleValue(dValue))
...
}
- SequentialRandomVariable: 返回一串等差数列,超过最大上限重新从开始再次循环。
- ExponentialRandomVariable: 根据指数概率分布返回随机数。
- NormalRandomVariable: 根据正态分布返回随机数
5.3 回调Callbacks
-
为什么用回调机制?why
实现让A模块调用B模块的函数,并且两个模块没有任何依赖。
先看C语言中的回调机制:
//想调用另一个模块的函数,先把被调用函数的地址作为一个变量,这个变量为函数指针变量。
int (*pfi)(int arg) = 0;//声明一个带整型参数(int arg)的函数指针pfi,初始值为0,前面int表示指向函数的返回值为整型,函数参数和指针参数也一致。
int MyFunction(int arg){}//pfi对应函数可以声明为这样
pfi = MyFuction;//用函数初始化函数指针
int result = (*pfi)(1234);//通过函数指针调用函数
在C++中还要为成员函数建立成员函数指针、区分成员函数指针和函数指针:
int (MyClass::*pmi)(int arg) = 0;//声明类的成员函数指针,MyClass类名
class MyClass
{
public:
int MyMethod (int arg);//函数声明
};
pmi = &MyClass::MyMethod;//pmi类函数指针变量的赋值和调用
MyClass myclass;
myclass.*pmi(1234);
-
什么是回调机制?what
例如调用快排函数,函数参数中有回调函数地址,可以传递自己的比较函数。这种被调用者(快排函数)调用调用者的排序函数(cmp),称为回调。
-
怎么用回调机制?how
ns-3提供了Callback类的API接口:
- 静态函数
// 对于静态函数,声明和实例化Callback类型
static double CbOne (double a, double b)
{
std::cout<<"invoke cbOne a="<<a<<",b="<<b<<std::endl;
return a;
}
int main(int argc,char *argv[])
{
Callback<double, double, double> one;//实例化回调类。<返回类型,第一个参数,第二个参数>
one = MakeCallback(&CbOne);//将回调one与签名匹配的函数进行绑定
//使用回调
NS_ASSERT(!one.IsNull());//检查回调one是否为空
Double retOne;
retOne = one(10.0, 20.0);
}
- 类成员函数回调
class MyCb{
public:
int CbTwo (double a){
std::cout<<"invoke cbTwo a="<<a<<std::endl;
return -5;
}
}
int main(int argc,char *argv[])
{
Callback<int, double> two;
MyCb cb;
two = MakeCallback (&MyCb::CbTwo,&cb);//创建一个回调变量并指向MyCb::CbTwo
//若构建空回调:two = MakeCallback<int,double>();int retTwoNull = two(20.0);
//使用回调
NS_ASSERT(!two.IsNull());
int retTwo;
retTwo = two(10.0);
NS_ASSERT(retTwo == -5);
two = MakeNullCallback<int, double>();//传递一个额外指针给函数MakeNullCallback<>(),当函数two()被调用时,调用的是&cb指向的对象函数CbTwo。
NS_ASSERT(two.IsNull());
return 0;
}
5.4 对象模型
- 面向对象设计
-
对象基类
ns-3提供了3个对象基类:Object、ObjectBase、SimpleRefCount。若每个类继承这种类,就可以包含ns-3提供的特有特性:
基类 | 属性 |
---|---|
Object | 属性系统、对象聚合系统、智能指针和引用计数系统 |
ObjectBase | 属性系统、对象聚合系统 |
SimpleRefCount | 智能指针和引用计数系统 |
-
内存管理与引用计数指针(Ptr类)
ns-3使用引用计数来管理内存 。
//在ns-3中使用CreateObject()来创建对象,而不是使用C++的new操作符。这是对new操作的封装,自动处理智能指针的引用次数。
Ptr<WifiNetDevice> device = CreateObject<WifiNetDevice>();
- 聚合
-
why?
例如ns-3中,Node类在任何网络终端中都使用,但不同网络终端的构建和协议不同,为了创建满足不同网络节点需求,就要使用聚合,将不同构件聚合到节点中。
- what?
-
how?
例如,将IPv4协议加入节点中:
static void
AddIpv4Stack(Ptr<Node> node)
{
Ptr<IPv4L3Protocol> ipv4 = CreateObject<IPv4L3Protocol> ();//创建一个IPv4协议的指针对象ipv4
ipv4->SetNode (node);//把IPv4协议聚合到节点中。这样Node就不需要被单独编辑。
node->AggregateObject (ipv4);
Ptr<IPv4Impl> ipv4Impl = CreateObject<IPv4Impl> ();
ipv4Impl->SetIPv4 (ipv4);
node->AggregateObject (ipv4Impl);
}
5.5 属性系统
- 对象模型:设置实例化模型的参数。
- Object类:多数ns-3类都是继承Object类。所有继承Object的类都可以包含一个叫TypeId的元数据类,用于记录类的元信息(包括唯一标识字符串、子类的基类、子类的构造函数)。
- 例子:Node类,静态成员函数GetTypeId
// 头文件node.h中
Class Node : public Object
{
public:Static TypeId GetTypeId(void):...
}
//node.cc文件中
TypeId
Node::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::Node")
.SetParent<Object> ()//声明此类的基类,方便在用GetObject()时进行向上或向下类型转化
.AddConstryctor<Node> ()//构建对象
.AddAttribute ("DeviceList","The list of devices associated to this Node.",ObjectVectorValue (), MakeObjectVectorAccessor (&Node::m_devices), MakeObjectVectorChecker<NetDevice> ())//把字符串与类的成员变量关联。参数1:要绑定的字符串,参数2:解释说明,参数3:成员变量必须转化的类型,参数4:将成员变量强制转化为参数3的类型,参数5:对成员变量是否合法的检查
.AddAttribute ("ApplicationList","The list of application associated to this Node.",ObjectVectorValue (), MakeObjectVectorAccessor (&Node::m_applications), MakeObjectVectorChecker<Application> ())
.AddAttribute ("Id","The id(unique integer)of this Node.",TypeId::ATTR_GET, UintegerValue (0), MakeUintegerAccessor (&Node::m_id), MakeUintegerChecker<uint32_t> ());
return tid;
}
//使用CreateObject(),创建节点:
Ptr<Node> n = CreateObject<Node>();
//使用对象工程机制,创建节点:
ObjectFactory factory;
const st::string typeId = "ns3::Node";
factory.SetTypeId (typeId);
Ptr(Object) node = factory.Create<Object> ();
- 属性系统:管理和组织仿真中内部对象(方便用户访问模拟中内部变量,e.g. cwnd)
- 例如:DropTailQueue类,无符号整型成员变量m_maxPackets(用来控制队列的大小)
// drop-tail-queue.h
class DropTailQueue : public Queue
{
public:
static typeId GetTypeId (void);
...
private:
std::queue<Ptr<Packet>> m_packets;
uint32_t m_maxPackets;
};
// drop-tail-queue.cc
// 用TypeId类实现创建类时,成员变量被初始化为默认值
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);//自定义类必须有,使属性系统涉及的属性可以正常初始化。
TypeId DropTailQueue::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.SetParent<Queue> ()
.AddConstructor<DropTailQueue> ()
.AddAttribute ("MaxPackets","The maximum number of packets accepted by this DropTailQueue.", UintegerValue (100), MakeUintegerAccessor (&DropTailQueue::m_maxPackets), MakeUintegerChecker<uint32_t> ());//使用AddAttribute()方法处理变量m_maxPackets:将m_maxPackets绑定到字符串“MaxPackets”中,默认值为100,Checker可用来设置允许的范围
return tid;
}
在脚本中操纵属性系统中的数据(src/point-to-point/examples/main-attribute-value.cc)
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2008 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
*
* Author: Tom Henderson <[email protected]>
*/
#include "ns3/log.h"
#include "ns3/command-line.h"
#include "ns3/ptr.h"
#include "ns3/config.h"
#include "ns3/uinteger.h"
#include "ns3/string.h"
#include "ns3/pointer.h"
#include "ns3/simulator.h"
#include "ns3/node.h"
#include "ns3/queue.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/point-to-point-net-device.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("AttributeValueSample");
//
// This is a basic example of how to use the attribute system to
// set and get a value in the underlying system; namely, the maximum
// size of the FIFO queue in the PointToPointNetDevice
//
int
main (int argc, char *argv[])
{
LogComponentEnable ("AttributeValueSample", LOG_LEVEL_INFO);
// Queues in ns-3 are objects that hold items (other objects) in
// a queue structure. The C++ implementation uses templates to
// allow queues to hold various types of items, but the most
// common is a pointer to a packet (Ptr<Packet>).
//
// The maximum queue size can either be enforced in bytes ('b') or
// packets ('p'). A special type called the ns3::QueueSize can
// hold queue size values in either unit (bytes or packets). The
// queue base class ns3::QueueBase has a MaxSize attribute that can
// be set to a QueueSize.
// By default, the MaxSize attribute has a value of 100 packets ('100p')
// (this default can be observed in the function QueueBase::GetTypeId)
//
// Here, we set it to 80 packets. We could use one of two value types:
// a string-based value or a QueueSizeValue value
Config::SetDefault ("ns3::QueueBase::MaxSize", StringValue ("80p"));
// The below function call is redundant
Config::SetDefault ("ns3::QueueBase::MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, 80)));
// Allow the user to override any of the defaults and the above
// SetDefaults() at run-time, via command-line arguments
// For example, via "--ns3::QueueBase::MaxSize=80p"
CommandLine cmd;
// This provides yet another way to set the value from the command line:
cmd.AddValue ("maxSize", "ns3::QueueBase::MaxSize");
cmd.Parse (argc, argv);
// Now, we will create a few objects using the low-level API
Ptr<Node> n0 = CreateObject<Node> ();
Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
n0->AddDevice (net0);
Ptr<Queue<Packet> > q = CreateObject<DropTailQueue<Packet> > ();
net0->SetQueue (q);
// At this point, we have created a single node (Node 0) and a
// single PointToPointNetDevice (NetDevice 0) and added a
// DropTailQueue to it.
// Now, we can manipulate the MaxSize value of the already
// instantiated DropTailQueue. Here are various ways to do that.
// We assume that a smart pointer (Ptr) to a relevant network device
// is in hand; here, it is the net0 pointer.
// 1. Pointer-based access 通过指针访问属性值
//
// One way to change the value is to access a pointer to the
// underlying queue and modify its attribute.
//
// First, we observe that we can get a pointer to the (base class)
// queue via the PointToPointNetDevice attributes, where it is called
// TxQueue
PointerValue ptr;// 创建一个指针变量
net0->GetAttribute ("TxQueue", ptr);// 为变量赋值
Ptr<Queue<Packet> > txQueue = ptr.Get<Queue<Packet> > ();// 获取队列
// Using the GetObject function, we can perform a safe downcast。通过GetObject函数安全地把txQueue向下类型转化(Queue->DropTailQueue)
// to a DropTailQueue
Ptr<DropTailQueue<Packet> > dtq = txQueue->GetObject <DropTailQueue<Packet> > ();
NS_ASSERT (dtq);
// Next, we can get the value of an attribute on this queue
// We have introduced wrapper "Value" classes for the underlying
// data types, similar to Java wrappers around these types, since
// the attribute system stores values and not disparate types.
// Here, the attribute value is assigned to a QueueSizeValue, and
// the Get() method on this value produces the (unwrapped) QueueSize.
// 通过输出数据验证程序是否将默认值100改成了80:
QueueSizeValue limit;
dtq->GetAttribute ("MaxSize", limit);
NS_LOG_INFO ("1. dtq limit: " << limit.Get ());
// Note that the above downcast is not really needed; we could have
// done the same using the Ptr<Queue> even though the attribute
// is a member of the subclass
// 实际上,不向下转化,也可以得到MaxSize值:80
txQueue->GetAttribute ("MaxSize", limit);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get ());
// Now, let's set it to another value (60 packets). Let's also make
// use of the StringValue shorthand notation to set the size by
// passing in a string (the string must be a positive integer suffixed
// by either the 'p' or 'b' character).
// 在创建对象后再改变MaxSize值:
txQueue->SetAttribute ("MaxSize", StringValue ("60p"));
txQueue->GetAttribute ("MaxSize", limit);
NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get ());
// 2. Namespace-based access
// 访问属性值的第二种方法:通过命名空间方式
// An alternative way to get at the attribute is to use the configuration
// namespace. Here, this attribute resides on a known path in this
// namespace; this approach is useful if one doesn't have access to
// the underlying pointers and would like to configure a specific
// attribute with a single statement.
// 修改了第一个节点的第一个网络设备属性值
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxSize", StringValue ("25p"));
txQueue->GetAttribute ("MaxSize", limit);
NS_LOG_INFO ("4. txQueue limit changed through namespace: " <<
limit.Get ());
// we could have also used wildcards to set this value for all nodes
// and all net devices (which in this simple example has the same
// effect as the previous Set())
// 修改了所有节点的所有网络设备属性值
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxSize", StringValue ("15p"));
txQueue->GetAttribute ("MaxSize", limit);
NS_LOG_INFO ("5. txQueue limit changed through wildcarded namespace: " <<
limit.Get ());
Simulator::Destroy ();
}
# 运行
./waf --run scratch/main-attribute-value
写自己的网络模块:
-
添加现有类的成员变量到元数据系统中
例如TcpSocket类的成员变量:uint32_tm_cWnd。使用TCP模块时想用元数据获得、设置变量的值,而ns-3没有提供这个变量,用户可以在元数据系统中添加声明:
./AddAttribute ("Congestion window","Tcp congestion window (bytes)",
UintergerValue(1),
MakeUintergerAccessor(&TcpSocket::m_cWnd),
MakeUintergerChecker<uint16_t>())
从而可以用指向TcpSocket类的指针执行设置和获取操作。
-
向属性系统中添加自定义类
例如:把自定义的类A加入到系统属性中
//在a.h中声明类A
class A:public Object{
public:
...
static TypeId GetTypeId(void);
...
private:
int16_t m_int16;
};
NS_OBJECT_ENSURE_REGISTERED(A);
//在a.cc中定义类函数GetTypeId
static TypeId GetTypeId(void){
static TypeId tid = TypeId("ns3::A")
.SetParent<Object>()
.addAttribute("TestInt16","help text",IntgerValue(-2),MakeIntegerAccessor(&A::m_int16),MakeInterChecker<int16_t>());
return tid;
}
5.6 Tracing系统
作用:追踪某仿真数据。
Tracing系统由三部分构成:Tracing Sources, Tracing Sinks, 连接前两者的方法。
- Tracing Sources提供信息(生产者)。
- Tracing Sinks使用信息做相关事务(消费者)。
- 二者关联。使用到回调。
(1)使用函数TraceConnectWithoutContext将二者关联。
例子:(examples/tutorial/fourth.cc)
/* -*- 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/object.h" //定义自己的类,父类为Object
# include "ns3/uinteger.h"//要用到自定义的无符号整型
# include "ns3/traced-value.h"//此头文件中引入了要跟踪的数据的类型,即TracedValue
# include "ns3/traced-source-accessor.h"//使用把自定义数据转换为traced-source的函数
# include <iostream>
using namespace ns3;
class MyObject : public Object
{//追踪系统与属性系统关联紧密,因此追踪的数据必须属于一个类
public:
/**
* Register this type.
* \return The TypeId.
*/
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("MyObject")
.SetParent<Object> ()
.SetGroupName ("Tutorial")
.AddConstructor<MyObject> ()
.AddTraceSource ("MyInteger",
"An integer value to trace.",
MakeTraceSourceAccessor (&MyObject::m_myInt),
"ns3::TracedValueCallback::Int32")//AddTraceSorce()使得m_myInt成为一个Trace Source
;
return tid;
}
MyObject () {}
TracedValue<int32_t> m_myInt;
};
// 此函数为定义Trace Sink
void
IntTrace (int32_t oldValue, int32_t newValue)
{
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}
int
main (int argc, char *argv[])
{//定义一个对象实例,实例中包含一个TraceSource,m_myInt
Ptr<MyObject> myObject = CreateObject<MyObject> ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback (&IntTrace));//TraceConnectWithoutContext()将Trace Source和Trace Sink关联。当Trace Source数据m_myInt变化时,IntTrace函数会被调用。
myObject->m_myInt = 1234;//m_myInt变化了,系统将m_myInt赋值前和赋值后的两个值作为形参传递给Trace Sink的回调函数IntTrace
}
# 运行
./waf --run example/tutorial/fourth
# outpuy
Traced 0 to 1234
(2)使用“配置路径”将Trace Sources和Trace Sink关联起来。
例子:third.cc改写,通过定义一个Trace Sink输出移动节点的位置变化信息。
#include "ns3/core-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/network-module.h"
#include "ns3/applications-module.h"
#include "ns3/mobility-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
#include "ns3/yans-wifi-helper.h"
#include "ns3/ssid.h"
// Default Network Topology
//
// Wifi 10.1.3.0
// AP
// * * * *
// | | | | 10.1.1.0
// n5 n6 n7 n0 -------------- n1 n2 n3 n4
// point-to-point | | | |
// ================
// LAN 10.1.2.0
//2.命名空间
using namespace ns3;
using namespace std;// 加一行代码
//3.定义一个LOG模块
NS_LOG_COMPONENT_DEFINE ("ThirdScriptExample");
// 加一个函数,定义Trace Sink。
void CourseChange (string context, Ptr<const MobilityModel> model)
{
Vector position = model->GetPosition();// 获得模型的位置
NS_LOG_UNCOND(context << " x = " << position.x << "y = " << position.y);// 输出模型的x,y坐标
}
//4.主函数
int
main (int argc, char *argv[])
{
bool verbose = true;
uint32_t nCsma = 3;
uint32_t nWifi = 3;
bool tracing = false;
CommandLine cmd;
cmd.AddValue ("nCsma", "Number of \"extra\" CSMA nodes/devices", nCsma);
cmd.AddValue ("nWifi", "Number of wifi STA devices", nWifi);
cmd.AddValue ("verbose", "Tell echo applications to log if true", verbose);
cmd.AddValue ("tracing", "Enable pcap tracing", tracing);
cmd.Parse (argc,argv);
// The underlying restriction of 18 is due to the grid position
// allocator's configuration; the grid layout will exceed the
// bounding box if more than 18 nodes are provided.
if (nWifi > 18)
{
std::cout << "nWifi should be 18 or less; otherwise grid layout exceeds the bounding box" << std::endl;
return 1;
}
if (verbose)
{//打印指定LOG组件信息
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
}
//5.创建网络拓扑
NodeContainer p2pNodes;
p2pNodes.Create (2);//创建两个p2p节点
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
NetDeviceContainer p2pDevices;
p2pDevices = pointToPoint.Install (p2pNodes);
NodeContainer csmaNodes;
csmaNodes.Add (p2pNodes.Get (1));
csmaNodes.Create (nCsma);
CsmaHelper csma;
csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps"));
csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
NetDeviceContainer csmaDevices;
csmaDevices = csma.Install (csmaNodes);
NodeContainer wifiStaNodes;
wifiStaNodes.Create (nWifi);
NodeContainer wifiApNode = p2pNodes.Get (0);
YansWifiChannelHelper channel = YansWifiChannelHelper::Default ();//默认传播延迟模型,默认损耗模型
YansWifiPhyHelper phy = YansWifiPhyHelper::Default ();//默认误码率模型
phy.SetChannel (channel.Create ());
WifiHelper wifi;
wifi.SetRemoteStationManager ("ns3::AarfWifiManager");// wifiRemoteStationManager主要用于wifi的速率控制(rate control)
WifiMacHelper mac;
Ssid = Ssid ("ns-3-ssid");
mac.SetType ("ns3::StaWifiMac",//移动节点
"Ssid", SsidValue (ssid),
"ActiveProbing", BooleanValue (false));
NetDeviceContainer staDevices;//安装移动节点
staDevices = wifi.Install (phy, mac, wifiStaNodes);
mac.SetType ("ns3::ApWifiMac",//AP节点
"Ssid", SsidValue (ssid));
NetDeviceContainer apDevices;//为AP节点安装应用
apDevices = wifi.Install (phy, mac, wifiApNode);
MobilityHelper mobility;//移动模型助手类
mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
"MinX", DoubleValue (0.0),//起点坐标(0.0,0.0)
"MinY", DoubleValue (0.0),
"DeltaX", DoubleValue (5.0),//x轴节点间距:5m
"DeltaY", DoubleValue (10.0), //y轴节点间距:10m
"GridWidth", UintegerValue (3),//每行最大节点数
"LayoutType", StringValue ("RowFirst"));
mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel",
"Bounds", RectangleValue (Rectangle (-50, 50, -50, 50)));
mobility.Install (wifiStaNodes);//为AP节点设置移动模型
mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
mobility.Install (wifiApNode);
//6.安装TCP/IP协议族
InternetStackHelper stack;
stack.Install (csmaNodes);
stack.Install (wifiApNode);
stack.Install (wifiStaNodes);
Ipv4AddressHelper address;
address.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer p2pInterfaces;
p2pInterfaces = address.Assign (p2pDevices);
address.SetBase ("10.1.2.0", "255.255.255.0");
Ipv4InterfaceContainer csmaInterfaces;
csmaInterfaces = address.Assign (csmaDevices);
address.SetBase ("10.1.3.0", "255.255.255.0");
address.Assign (staDevices);
address.Assign (apDevices);
//7.安装应用程序
UdpEchoServerHelper echoServer (9);
ApplicationContainer serverApps = echoServer.Install (csmaNodes.Get (nCsma));
serverApps.Start (Seconds (1.0));
serverApps.Stop (Seconds (10.0));
UdpEchoClientHelper echoClient (csmaInterfaces.GetAddress (nCsma), 9);
echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
echoClient.SetAttribute ("PacketSize", UintegerValue (1024));
ApplicationContainer clientApps =
echoClient.Install (wifiStaNodes.Get (nWifi - 1));
clientApps.Start (Seconds (2.0));
clientApps.Stop (Seconds (10.0));
//8.设置路由
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
Simulator::Stop (Seconds (10.0));
//9.数据追踪
if (tracing == true)
{
pointToPoint.EnablePcapAll ("third");
phy.EnablePcap ("third", apDevices.Get (0));
csma.EnablePcap ("third", csmaDevices.Get (0), true);
}
// 附加:使CourseChange (Trace Sink)和CourseChange (Trace Sources)相关联的代码(使用config path子系统,从系统中选取用户所要使用的Trace Sources)
ostringstream oss;
oss << "/NodeList" << wifiStaNodes.Get(nWifi -1) -> GetId() << "/$ns3::MobilityModel/CourseChange";// "/"后面加的表示一个命名空间,这里用的为NodeList,即一个仿真中使用的节点的列表。后面是列表的索引,通过调用函数Get()获取节点,再通过GetId()得到节点的索引ID。当程序遇到$符号时,使调用GetObject()返回一个对象,需要给出返回对象的类型,这里为MobilityModel类,CourseChange属性(即要追踪的Tracing Source)。(注:如何确定Config Path:进入API文档->找到需要的类->Config Path标题下) (nWifi -1表示追踪n7节点的位置)
Config::Connect(oss.str(), MakeCallback(&CourseChange));// 使用类Config的静态成员函数Connect将二者关联起来。函数的第二个参数:使函数CourseChange成为一个回调函数。第一个参数是一个由各种字符组成的字符串。
//10.启动与结束
Simulator::Run ();
Simulator::Destroy ();
return 0;
}
# 运行
./waf --run scratch/mythird
(3)如何确定Trace Sources
ns3官网->API文档->Modules->C++Constructs Used by All Modules->The list of all trace sources->找到可以直接使用的Trace Sources。
(4)如何确定Trace Sink
Trace Sink是一个函数,因此要确定其返回值和参数。看例子中已经写好的回调函数,例如CourseChange回调函数可以在ns-3.xx/examples/wirless/mixed-wireless.cc中找到一个函数CourseChangeCallback()。