# 4. 仿真流程
在本节中,将介绍 ns3
中仿真的流程,
# 基本流程
使用 ns3
仿真时,一般经过以下 4 个步骤。
# 1. 选择或开发相应的模块
根据实际仿真对象和仿真场景选择相应的仿真模块:如有线网或还是无线网 ( Wi-Fi
),节点移动性 mobility
,能量 energy
, 路由协议,应用程序 application
等,若是没有相应模块,那就需要自己编写 (在后续章节中讲述)。
# 2. 编写网络仿真脚本
若是有了相应的模块,那么我们就可以搭建网络仿真环境,ns-3 仿真脚本支持 2 种语言:C 和 Python,两种语言接口的 API 接口是一样的,但是部分 API 可能没有 Python 的接口。所以,仿真主要还是采用 C 进行编写。
编写 ns-3 仿真脚本的大体过程如下【具体的例子查看 examples/tutorial/
下面 的示例,后面也会分析一些】:
生成节点:ns-3 中节点相当于一个空的计算机外壳,我们需要根据需求给这个计算机安装网络所需要的软硬件,如网卡、应用程序、协议栈等。
安装网卡设备:不同的网络类型有不同的网络设备,从而提供不同的通信、物理层和 MAC 层,如
CSMA WI-FI WIMAX
和point-to-point
等。安装协议栈:
ns-3
网络中一般是TCP/IP
协议栈,依据网络选择具体协议,如是UDP
还是TCP
,选择何种不同的路由协议(OLSR
AODV
和Global
等)并为其配置相应的 IP 地址,ns3
既支持IPv4
也支持IPv6
。安装应用层协议:依据选择的传输层协议选择相应的应用层协议,但是有时需要自己编写应用层产生数据流量的代码。
其他配置:如节点是否移动,是否需要能量管理等
启动仿真:整个网络场景配置完毕,启动仿真
# 3. 仿真结果分析
仿真结果一般有两种:一种是网络场景,二是网络数据。网络场景如节点拓扑结构、移动模型等,一般通过可视化界面( PyViz
或者 NetAnim
)可直接观测到。网络数据可以在可视化界面进行简单统计,此外还可以通过专门的统计框架 status
或者自行通过 ns3
提供的追踪框架收集、统计和分析相应的网络数据,如数据分组的延迟、网络流量、分组丢失和节点消息缓存队列的等, 但是上述统计结果对于一些新的开源模块可能不一定有用,所以更推荐的是采用 track 自行分析。。
# 4. 依据仿真结果调整网络配置参数或者修改源代码。
# 例子
为了更好的展示上述流程,样例代码是 aodv
的实例代码,位于 ns-3.xx/src/aodv/examples
完整代码如下所示:
#include <iostream> | |
#include <cmath> | |
#include "ns3/aodv-module.h" | |
#include "ns3/core-module.h" | |
#include "ns3/network-module.h" | |
#include "ns3/internet-module.h" | |
#include "ns3/mobility-module.h" | |
#include "ns3/point-to-point-module.h" | |
#include "ns3/v4ping-helper.h" | |
#include "ns3/yans-wifi-helper.h" | |
using namespace ns3; | |
/** | |
* \ingroup aodv-examples | |
* \ingroup examples | |
* \brief Test script. | |
* | |
* This script creates 1-dimensional grid topology and then ping last node from the first one: | |
* | |
* [10.0.0.1] <-- step --> [10.0.0.2] <-- step --> [10.0.0.3] <-- step --> [10.0.0.4] | |
* | |
* ping 10.0.0.4 | |
* | |
* When 1/3 of simulation time has elapsed, one of the nodes is moved out of | |
* range, thereby breaking the topology. By default, this will result in | |
* only 34 of 100 pings being received. If the step size is reduced | |
* to cover the gap, then all pings can be received. | |
*/ | |
class AodvExample | |
{ | |
public: | |
AodvExample (); | |
/** | |
* \brief Configure script parameters | |
* \param argc is the command line argument count | |
* \param argv is the command line arguments | |
* \return true on successful configuration | |
*/ | |
bool Configure (int argc, char **argv); | |
/// Run simulation | |
void Run (); | |
/** | |
* Report results | |
* \param os the output stream | |
*/ | |
void Report (std::ostream & os); | |
private: | |
// parameters | |
/// Number of nodes | |
uint32_t size; | |
/// Distance between nodes, meters | |
double step; | |
/// Simulation time, seconds | |
double totalTime; | |
/// Write per-device PCAP traces if true | |
bool pcap; | |
/// Print routes if true | |
bool printRoutes; | |
// network | |
/// nodes used in the example | |
NodeContainer nodes; | |
/// devices used in the example | |
NetDeviceContainer devices; | |
/// interfaces used in the example | |
Ipv4InterfaceContainer interfaces; | |
private: | |
/// Create the nodes | |
void CreateNodes (); | |
/// Create the devices | |
void CreateDevices (); | |
/// Create the network | |
void InstallInternetStack (); | |
/// Create the simulation applications | |
void InstallApplications (); | |
}; | |
int main (int argc, char **argv) | |
{ | |
AodvExample test; | |
if (!test.Configure (argc, argv)) | |
NS_FATAL_ERROR ("Configuration failed. Aborted."); | |
test.Run (); | |
test.Report (std::cout); | |
return 0; | |
} | |
//----------------------------------------------------------------------------- | |
AodvExample::AodvExample () : | |
size (10), | |
step (50), | |
totalTime (100), | |
pcap (true), | |
printRoutes (true) | |
{ | |
} | |
bool | |
AodvExample::Configure (int argc, char **argv) | |
{ | |
// Enable AODV logs by default. Comment this if too noisy | |
// LogComponentEnable("AodvRoutingProtocol", LOG_LEVEL_ALL); | |
SeedManager::SetSeed (12345); | |
CommandLine cmd (__FILE__); | |
cmd.AddValue ("pcap", "Write PCAP traces.", pcap); | |
cmd.AddValue ("printRoutes", "Print routing table dumps.", printRoutes); | |
cmd.AddValue ("size", "Number of nodes.", size); | |
cmd.AddValue ("time", "Simulation time, s.", totalTime); | |
cmd.AddValue ("step", "Grid step, m", step); | |
cmd.Parse (argc, argv); | |
return true; | |
} | |
void | |
AodvExample::Run () | |
{ | |
// Config::SetDefault ("ns3::WifiRemoteStationManager::RtsCtsThreshold", UintegerValue (1)); // enable rts cts all the time. | |
CreateNodes (); | |
CreateDevices (); | |
InstallInternetStack (); | |
InstallApplications (); | |
std::cout << "Starting simulation for " << totalTime << " s ...\n"; | |
Simulator::Stop (Seconds (totalTime)); | |
Simulator::Run (); | |
Simulator::Destroy (); | |
} | |
void | |
AodvExample::Report (std::ostream &) | |
{ | |
} | |
void | |
AodvExample::CreateNodes () | |
{ | |
std::cout << "Creating " << (unsigned)size << " nodes " << step << " m apart.\n"; | |
nodes.Create (size); | |
// Name nodes | |
for (uint32_t i = 0; i < size; ++i) | |
{ | |
std::ostringstream os; | |
os << "node-" << i; | |
Names::Add (os.str (), nodes.Get (i)); | |
} | |
// Create static grid | |
MobilityHelper mobility; | |
mobility.SetPositionAllocator ("ns3::GridPositionAllocator", | |
"MinX", DoubleValue (0.0), | |
"MinY", DoubleValue (0.0), | |
"DeltaX", DoubleValue (step), | |
"DeltaY", DoubleValue (0), | |
"GridWidth", UintegerValue (size), | |
"LayoutType", StringValue ("RowFirst")); | |
mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); | |
mobility.Install (nodes); | |
} | |
void | |
AodvExample::CreateDevices () | |
{ | |
WifiMacHelper wifiMac; | |
wifiMac.SetType ("ns3::AdhocWifiMac"); | |
YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default (); | |
YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default (); | |
wifiPhy.SetChannel (wifiChannel.Create ()); | |
WifiHelper wifi; | |
wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", "DataMode", StringValue ("OfdmRate6Mbps"), "RtsCtsThreshold", UintegerValue (0)); | |
devices = wifi.Install (wifiPhy, wifiMac, nodes); | |
if (pcap) | |
{ | |
wifiPhy.EnablePcapAll (std::string ("aodv")); | |
} | |
} | |
void | |
AodvExample::InstallInternetStack () | |
{ | |
AodvHelper aodv; | |
// you can configure AODV attributes here using aodv.Set(name, value) | |
InternetStackHelper stack; | |
stack.SetRoutingHelper (aodv); // has effect on the next Install () | |
stack.Install (nodes); | |
Ipv4AddressHelper address; | |
address.SetBase ("10.0.0.0", "255.0.0.0"); | |
interfaces = address.Assign (devices); | |
if (printRoutes) | |
{ | |
Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper> ("aodv.routes", std::ios::out); | |
aodv.PrintRoutingTableAllAt (Seconds (8), routingStream); | |
} | |
} | |
void | |
AodvExample::InstallApplications () | |
{ | |
V4PingHelper ping (interfaces.GetAddress (size - 1)); | |
ping.SetAttribute ("Verbose", BooleanValue (true)); | |
ApplicationContainer p = ping.Install (nodes.Get (0)); | |
p.Start (Seconds (0)); | |
p.Stop (Seconds (totalTime) - Seconds (0.001)); | |
// move node away | |
Ptr<Node> node = nodes.Get (size/2); | |
Ptr<MobilityModel> mob = node->GetObject<MobilityModel> (); | |
Simulator::Schedule (Seconds (totalTime/3), &MobilityModel::SetPosition, mob, Vector (1e5, 1e5, 1e5)); | |
} |
从上述代码 run
函数中也可以很清晰的看出,实际上总共就分为几个步骤.
void AodvExample::Run () | |
{ | |
CreateNodes (); // 创建节点 | |
CreateDevices ();// 创建设备 | |
InstallInternetStack ();// 安装协议栈 | |
InstallApplications ();// 设置应用 | |
std::cout << "Starting simulation for " << totalTime << " s ...\n"; | |
Simulator::Stop (Seconds (totalTime)); | |
Simulator::Run (); | |
Simulator::Destroy (); | |
} |
# 1. 创建节点过程
创建了 size
个节点,并编号命名,
然后节点采用了 Grid
布局,(0,0) 点开始,
然后设置节点静止不动。
void AodvExample::CreateNodes () | |
{ | |
std::cout << "Creating " << (unsigned)size << " nodes " << step << " m apart.\n"; | |
nodes.Create (size); | |
// Name nodes | |
for (uint32_t i = 0; i < size; ++i) | |
{ | |
std::ostringstream os; | |
os << "node-" << i; | |
Names::Add (os.str (), nodes.Get (i)); | |
} | |
// Create static grid | |
MobilityHelper mobility; | |
mobility.SetPositionAllocator ("ns3::GridPositionAllocator", | |
"MinX", DoubleValue (0.0), | |
"MinY", DoubleValue (0.0), | |
"DeltaX", DoubleValue (step), | |
"DeltaY", DoubleValue (0), | |
"GridWidth", UintegerValue (size), | |
"LayoutType", StringValue ("RowFirst")); | |
mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); | |
mobility.Install (nodes); | |
} |
# 2. 创建设备
设置了 wifi 信道,wifi 的速率,pcap 设置一般没什么用,需要第三方软件分析可以采用。
void AodvExample::CreateDevices () | |
{ | |
WifiMacHelper wifiMac; | |
wifiMac.SetType ("ns3::AdhocWifiMac"); | |
YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default (); | |
YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default (); | |
wifiPhy.SetChannel (wifiChannel.Create ()); | |
WifiHelper wifi; | |
wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", "DataMode", StringValue ("OfdmRate6Mbps"), "RtsCtsThreshold", UintegerValue (0)); | |
devices = wifi.Install (wifiPhy, wifiMac, nodes); | |
if (pcap) | |
{ | |
wifiPhy.EnablePcapAll (std::string ("aodv")); | |
} | |
} |
# 3. 安装协议栈
路由选择了 aodv
路由协议,设置了节点的 Ipv4
地址,设置是否需要打印路由表。
void AodvExample::InstallInternetStack () | |
{ | |
AodvHelper aodv; | |
// you can configure AODV attributes here using aodv.Set(name, value) | |
InternetStackHelper stack; | |
stack.SetRoutingHelper (aodv); // has effect on the next Install () | |
stack.Install (nodes); | |
Ipv4AddressHelper address; | |
address.SetBase ("10.0.0.0", "255.0.0.0"); | |
interfaces = address.Assign (devices); | |
if (printRoutes) | |
{ | |
Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper> ("aodv.routes", std::ios::out); | |
aodv.PrintRoutingTableAllAt (Seconds (8), routingStream); | |
} | |
} |
# 4. 设置应用
设置了 ping
的应用程序,正常测网络性能,用的是 OnOffHelper
或者直接 socket
测试。
void AodvExample::InstallApplications () | |
{ | |
V4PingHelper ping (interfaces.GetAddress (size - 1)); | |
ping.SetAttribute ("Verbose", BooleanValue (true)); | |
ApplicationContainer p = ping.Install (nodes.Get (0)); | |
p.Start (Seconds (0)); | |
p.Stop (Seconds (totalTime) - Seconds (0.001)); | |
// move node away | |
Ptr<Node> node = nodes.Get (size/2); | |
Ptr<MobilityModel> mob = node->GetObject<MobilityModel> (); | |
Simulator::Schedule (Seconds (totalTime/3), &MobilityModel::SetPosition, mob, Vector (1e5, 1e5, 1e5)); | |
} |