示例脚本:【examples/tutorial/third.cc】
用ns-3脚本搭建一个Wi-Fi无线网络
模拟了一个混合场景,包含PPP,CSMA和Wi-Fi
1. 头文件
头文件命名规范:"<模块名>-module.h"
头文件所在目录:【build/ns3】
#include "ns3/core-module.h" // 定义了ns-3的核心功能(如模拟事件、事件调度),必须包括
#include "ns3/point-to-point-module.h" // PPP模块
#include "ns3/network-module.h" // 定义了ns-3的基本网络组件(如网络结点、分组和地址等),必须包括
#include "ns3/applications-module.h" // 定义了应用层的分组收发模型(如贪婪模型、ON/OFF模型等)
#include "ns3/wifi-module.h" // Wi-Fi模块
#include "ns3/mobility-module.h" // 移动模块
#include "ns3/csma-module.h" // CSMA模块
#include "ns3/internet-module.h" // 定义了TCP/IP协议栈
2. 命名空间
using namespace ns3;
3. NS_LOG_COMPONENT_DEFINE
下面这行代码的作用是允许在该脚本中使用Log系统中的宏定义打印辅助信息,如打印调试信息的NS_LOG_DEBUG和打印错误信息的NS_LOG_ERROR宏等
NS_LOG_COMPONENT_DEFINE ("ThirdScriptExample");
尽管这是一个可选步骤,但建议添加,因为这些辅助信息对调试代码和了解模拟流程有用
4. main()函数中的准备工作
从这一步开始,后续操作均在main()函数中完成
bool verbose = true; // Tell echo applications to log if true
uint32_t nCsma = 3; // Number of "extra" CSMA nodes/devices
uint32_t nWifi = 3; // Number of wifi STA devices / 无线结点数量
bool tracing = false; // Enable pcap tracing
// 读取命令行参数
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;
}
// 开启Log组件,打印指定Log组件信息
if (verbose)
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
}
5. 创建网络拓扑
有了1-4,现在开始搭建网络拓扑
PPP和CSMA都属于有线网络技术
CSMA(Carrier Sense Multiple Access,载波监听多路访问)
PPP信道只能连接2个结点,是专属信道;CSMA信道可以连接多个结点(总线型网络),需要结点竞争信道使用权
5.1. PPP网络
NodeContainer p2pNodes;
p2pNodes.Create (2);
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
NetDeviceContainer p2pDevices;
p2pDevices = pointToPoint.Install (p2pNodes);
5.2. CSMA网络
NodeContainer csmaNodes;
// p2pNodes.Get(1)结点有PointToPointNetDevice和CsmaNetDevice两个网络设备,是一个双模结点
csmaNodes.Add (p2pNodes.Get (1));
csmaNodes.Create (nCsma); // 创建CSMA网络结点
CsmaHelper csma; // CSMA助手类
csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps")); // 设置CSMA信道属性:传输速率100Mbps,传播延迟6560ns
csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
NetDeviceContainer csmaDevices;
// csma创建1个CSMA信道和csmaNodes个网络设备,并将创建的网络设备连接到信道上,然后返回一个网络设备容器
csmaDevices = csma.Install (csmaNodes);
5.3. Wi-Fi网络
third脚本的Wi-Fi无线网络由1个AP(AccessPoint,接入点)和nWifi个移动节点组成,AP也是一个双模结点(装有Wi-Fi和PPP两个网络设备)
除了WifiNetDevice类(NetDevice的子类),Wi-Fi网络设备还包括链路(WifiMac)层和物理(WifiPhy)层部分
这里的WifiNetDevice只起一个连接上下层协议的桥梁作用,无实质性功能
主要的Wi-Fi协议功能都集中在WifiMac和WifiPhy两个基类和其各种子类中实现
5.3.1. 设置Channel和WifiPhy
NodeContainer wifiStaNodes;
wifiStaNodes.Create (nWifi); // nWifi个STA结点
NodeContainer wifiApNode = p2pNodes.Get (0); // AP结点
// 1. 设置Channel和WifiPhy
// 默认传播延迟模型:ConstantSpeedPropagationDelayModel
// 默认损耗模型:LogDistancePropagationLossModel
// Wi-Fi Channel需要配置传播延迟(propagation delay)模型和损耗(propagation loss)模型
// 这两个模型和最后一步中的移动模型一起决定了一个分组在无线信道中的传播延迟和接收功率
YansWifiChannelHelper channel = YansWifiChannelHelper::Default ();
// 默认误码率模型:NistErrorRateModel
// WifiPhy需要配置误码率(error rate)模型
YansWifiPhyHelper phy = YansWifiPhyHelper::Default ();
phy.SetChannel (channel.Create ());
5.3.2. 设置WifiMac并在结点中安装NetDevice
// 2. 设置WifiMac,并在结点中安装NetDevice
// WifiHelper也是决定Wi-Fi协议类型的助手类
// 默认状态下,WifiHelper安装的协议种类是802.11a,可以通过WifiHelper的SetStandard()函数来选择其他Wi-Fi协议类型
WifiHelper wifi;
// WifiHelper设置WifiRemoteStationManager子类类型,WifiRemoteStationManager主要用于Wi-Fi的速率控制
wifi.SetRemoteStationManager ("ns3::AarfWifiManager");
// WifiMacHelper设置WifiMac子类和服务集标识符(SSID)
// 前者决定这个结点的种类是AP还是移动结点,后者决定一个结点所属的服务集,AP与移动结点的服务集必须一致才能通信
WifiMacHelper mac;
Ssid 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);
5.3.3. 设置移动模型
// 3. 设置移动模型
// 移动模型一般在Wi-Fi网络设备安装至结点后配置,因为不同Wi-Fi结点类型需要不同的移动模型
// ns-3移动模型采用笛卡尔坐标系标识结点位置,其助手类是MobilityHelper
// 与设置链路层类似,在Wi-Fi网络中设置移动模型也要区分AP和移动结点
MobilityHelper mobility;
// STA结点是移动结点
// 移动结点的移动模型设置分为两部分:初始位置分布和后续移动轨迹模型
// 前者定义了一个移动结点的初始坐标,后者定义了结点的移动路径
// third脚本使用的初始位置分布器是GridPositionAllocator
// 该分布器按照设置好的行列参数把结点等距放置在一个二维笛卡尔坐标系中
// third脚本使用的移动轨迹模型是RandomWalk2dMobilityModel
// 该模型中的结点在一个指定大小的长方形区域内按照随机的速度(步行速度,默认取值范围是[2,4]m/s)和方向移动
// 为移动结点设置移动模型之初始位置分布器
mobility.SetPositionAllocator ("ns3::GridPositionAllocator",
"MinX", DoubleValue (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结点是固定结点
// third脚本为其使用的是固定位置移动模型ConstantPositionMobilityModel
mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel");
mobility.Install (wifiApNode);
6. 安装TCP/IP协议族
ns-3中的TCP/IP协议族主要包括:传输层的TCP和UDP;网络层的IP(IPv4和IPv6)、ICMP(ICMPv4和ICMPv6);一系列路由协议
为结点安装TCP/IP协议栈的助手类是InternetStackHelper
// third脚本为所有结点都安装了TCP/IP协议栈
InternetStackHelper stack;
stack.Install (csmaNodes);
stack.Install (wifiApNode);
stack.Install (wifiStaNodes);
安装了TCP/IP协议栈的结点还不能直接通信,还需要为结点的网络设备分配IP地址
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. 安装应用程序
ns-3中的应用是对物理世界中应用程序内部网络通信功能的抽象,i.e.模拟分组的收发行为
ns-3应用层协议对应的C++类是Application,其不同子类定义了不同的分组收发行为,如BulkSendApplication使用了贪婪分组收发模型,OnOffApplication使用了ONOFF分组收发模型
// 安装应用程序
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. 设置路由
// 设置路由
// 当ns-3的多个【有线网络子网】存在通信时,需要在脚本中设置路由协议
// p2pNodes.Get(1)结点有PointToPointNetDevice和CsmaNetDevice两个网络设备,分别连接PPP有线网络和CSMA有线网络
// 这两个网络属于不同子网,需要连接两个子网的p2pNodes.Get(1)结点具有路由功能,才能正确转发子网间的分组
// ns-3有线网络最常用的路由协议之一是【全局路由】
// 全局路由通过【开放式最短路径优先(OSPF)路由算法】计算有线网络拓扑中每两个结点的最短路径,并为每个结点生成【路由表】
// 对于IPv4协议,全局路由设置一般通过调用Ipv4GlobalRoutingHelper::PopulateRoutingTables()函数完成
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
Simulator::Stop (Seconds (10.0));
9. 数据追踪
产生数据是网络模拟的一个必备功能,否则人们就无法分析网络性能
属性配置和数据生成,一个输入一个输出
// 数据追踪
// 模拟的目的之一是分析网络性能,而获取实验数据是分析网络性能的前提
// ns-3提供了丰富的数据追踪与收集功能,收集到的数据常以pcap或ASCII文本的格式保存在指定文件中
if (tracing == true)
{
// PointToPointHelper::EnablePcapAll()函数收集该信道上所有结点的链路层分组收发记录,文件前缀名third,文件格式pcap
// EnablePcapAll()函数对文件名的命名规则【前缀名-<结点标号>-<网络设备标号>】,如third-0-0.pcaph和third-1-0.pcap
pointToPoint.EnablePcapAll ("third");
// YansWifiPhyHelper::EnablePcap()函数打印AP结点中Wi-Fi网络设备的物理层分组收发信息
phy.EnablePcap ("third", apDevices.Get (0));
csma.EnablePcap ("third", csmaDevices.Get (0), true);
}
10. 启动与结束
Simulator::Run ();
Simulator::Destroy ();
return 0;
上述代码是所有ns-3模拟脚本的最后一步
C++模拟脚本本质上是一个main()函数,所以程序最后需要返回0来告诉os程序执行成功