NS-3 网络仿真:从零搭建到三个样例
前言
NS-3 是一个用于 Internet 系统的离散事件网络模拟器,主要用于研究和教育用途
NS-3 是一个免费的开源软件,在 GNU GPL v2 许可下授权,并由全球社区维护
安装
依赖:
sudo apt install g++ python3 cmake ninja-build git
sudo apt install ccache
sudo apt install python3-pip
python3 -m pip install --user cppyy
sudo apt install gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3
sudo apt install python3-setuptools git
sudo apt install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
sudo apt install openmpi-bin openmpi-common openmpi-doc libopenmpi-dev
sudo apt install mercurial unzip
sudo apt install gdb valgrind
sudo apt install clang-format
sudo apt install doxygen graphviz imagemagick
sudo apt install texlive texlive-extra-utils texlive-latex-extra texlive-font-utils dvipng latexmk
sudo apt install python3-sphinx dia
sudo apt install gsl-bin libgsl-dev libgslcblas0
sudo apt install tcpdump
sudo apt install sqlite sqlite3 libsqlite3-dev
sudo apt install libxml2 libxml2-dev
sudo apt install libgtk-3-dev
sudo apt install vtun lxc uml-utilities
sudo apt install libxml2 libxml2-dev libboost-all-dev安装 NS-3:
# 下载
mkdir repos
cd repos
git clone https://gitlab.com/nsnam/ns-3-allinone.git # 下载安装脚本
cd ns-3-allinone
python3 download.py -n ns-3.40
# 构建
./build.py --enable-examples --enable-tests
# 测试模块
cd ns-3.40
./test.py运行
构建:
# 进入 ~/repos/ns-3-allinone/ns-3.40
cp examples/tutorial/first.cc scratch/myfirst.cc
./ns3 build运行:
./ns3 run scratch/myfirst样例
1. p2p
// SPDX-License-Identifier: GPL-2.0-only
#include "ns3/applications-module.h"
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"
// Default Network Topology
//
// 10.1.1.0
// n0 -------------- n1
// point-to-point
//
using namespace ns3;
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d8/dff/group__logging.html#ga225a95395fa117b7309aa3c43518d02e
*
* 这个宏定义了一个新的“日志组件”
*/
NS_LOG_COMPONENT_DEFINE("FirstScriptExample");
int
main(int argc, char* argv[])
{
CommandLine cmd(__FILE__);
cmd.Parse(argc, argv);
// 设置时间分辨率为 1ns(默认也为 1 纳秒)
Time::SetResolution(Time::NS);
// 启用两个日志记录组件,内置在 Echo 客户端和 Echo 服务器应用程序中
LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d7/db2/classns3_1_1_node_container.html
*
* 用于跟踪一组的节点指针
*/
NodeContainer nodes;
// 让容器创建两个节点
nodes.Create(2);
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d3/d84/classns3_1_1_point_to_point_helper.html
*
* 相当于一个拓扑帮助器,用于配置和连接 以太网设备 与 无线信道
* 生成一组 PointToPointNetDevice 对象
*/
PointToPointHelper pointToPoint;
// 设置信息传输速率和延迟
pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d3/d84/classns3_1_1_point_to_point_helper.html#ab9162fea3e88722666fed1106df1f9ec
*
* 此方法(Install)创建一个 无线信道,并对于输入容器中的每个节点创建一个 以太网设备
* 并将生成的这个 以太网设备 的队列与 无线信道 相关联
*/
NetDeviceContainer devices;
devices = pointToPoint.Install(nodes);
// 接下来是配置协议栈
InternetStackHelper stack;
stack.Install(nodes);
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d5/d4f/classns3_1_1_ipv4_address_helper.html
*
* 声明一个地址助手,进行简单的 IPv4 地址分配
* 参数分别为
* - 初始网络号
* - 掩码
* - (可选)起始的 IP 地址
*
* 下面的 IP 地址将从 10.1.1.1 开始分配
*/
Ipv4AddressHelper address;
address.SetBase("10.1.1.0", "255.255.255.0");
// 使用 Ipv4Interface 将 IP 地址与设备进行关联
Ipv4InterfaceContainer interfaces = address.Assign(devices);
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d6/d64/classns3_1_1_udp_echo_server_helper.html
*
* 创建一个等待输入 UDP 数据包并将其发送回原始发送方的服务器应用程序
* 设置端口为 9
*/
UdpEchoServerHelper echoServer(9);
// 在指定节点上创建一个 UdpEchoServerApplication
ApplicationContainer serverApps = echoServer.Install(nodes.Get(1));
// 指定生成流量的开始时间以及结束时间
serverApps.Start(Seconds(1.0));
serverApps.Stop(Seconds(10.0));
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d5/d2f/classns3_1_1_udp_echo_client_helper.html
*
* 创建一个应用程序,它发送UDP数据包并等待此数据包的回显
* echoClinet 的参数:
* - 远程 UDP 回送服务器的 IP 地址
* - 远程 UDP 回送服务器的端口号
*/
UdpEchoClientHelper echoClient(interfaces.GetAddress(1), 9);
// 允许它在模拟过程中最多能发送的数据包
echoClient.SetAttribute("MaxPackets", UintegerValue(1));
// 在数据包之间等待多长时间
echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
// 数据包有效载荷大小(单位字节)
echoClient.SetAttribute("PacketSize", UintegerValue(1024));
// 在指定节点上创建一个 UdpEchoClientApplication
ApplicationContainer clientApps = echoClient.Install(nodes.Get(0));
// 指定生成流量的开始时间以及结束时间
clientApps.Start(Seconds(2.0));
clientApps.Stop(Seconds(10.0));
// 运行模拟 以及 销毁和清理
// 当然,也可以手动指定停止时间,而不是等待事件队列自动耗尽
// Simulator::Stop(Seconds(11.0));
Simulator::Run();
Simulator::Destroy();
return 0;
}2. p2p + CSMA
// SPDX-License-Identifier: GPL-2.0-only
#include "ns3/applications-module.h"
#include "ns3/core-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"
// Default Network Topology
//
// 10.1.1.0
// n0 -------------- n1 n2 n3 n4
// point-to-point | | | |
// ================
// LAN 10.1.2.0
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("SecondScriptExample");
int
main(int argc, char* argv[])
{
bool verbose = true;
uint32_t nCsma = 3;
CommandLine cmd(__FILE__);
cmd.AddValue("nCsma", "Number of \"extra\" CSMA nodes/devices", nCsma);
cmd.AddValue("verbose", "Tell echo applications to log if true", verbose);
cmd.Parse(argc, argv);
if (verbose)
{
LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);
}
nCsma = nCsma == 0 ? 1 : nCsma;
NodeContainer p2pNodes;
p2pNodes.Create(2);
/**
* CSMA:载波侦听多路访问
* - 载波侦听(Carrier Sense)
* 指任何连接到介质的设备在欲发送帧前,必须对介质进行侦听,当确认其空闲时,才可以发送。
* - 多路访问(Multiple Access)
* 指多个设备可以同时访问介质,一个设备发送的帧也可以被多个设备接收。
*
* 注:真实的以太网使用 CSMA/CD(带有冲突检测的载波侦听多路存取)方案与指数退避方法共享传输介质
*/
NodeContainer csmaNodes;
// 创建一个 LAN,p2p 的一个节点作为必要节点,除此之外还有三个额外节点(nCsma==3)
csmaNodes.Add(p2pNodes.Get(1));
csmaNodes.Create(nCsma);
// p2p
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
NetDeviceContainer p2pDevices;
p2pDevices = pointToPoint.Install(p2pNodes);
// CSMA
CsmaHelper csma;
csma.SetChannelAttribute("DataRate", StringValue("100Mbps"));
csma.SetChannelAttribute("Delay", TimeValue(NanoSeconds(6560)));
NetDeviceContainer csmaDevices;
csmaDevices = csma.Install(csmaNodes);
// 配置协议注意:p2p 的 1 号节点在 CSMA 网络里面,不要重复安装协议
InternetStackHelper stack;
stack.Install(p2pNodes.Get(0));
stack.Install(csmaNodes);
// 这个是配置 p2p 的 IP 地址
Ipv4AddressHelper address;
address.SetBase("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer p2pInterfaces;
p2pInterfaces = address.Assign(p2pDevices);
// 这个是配置 CSMA 的 IP 地址
address.SetBase("10.1.2.0", "255.255.255.0");
Ipv4InterfaceContainer csmaInterfaces;
csmaInterfaces = address.Assign(csmaDevices);
// 设置 echo 服务器
UdpEchoServerHelper echoServer(9);
// 安装在 CSMA 最后一个节点上
ApplicationContainer serverApps = echoServer.Install(csmaNodes.Get(nCsma));
serverApps.Start(Seconds(1.0));
serverApps.Stop(Seconds(10.0));
// 设置 echo 客户端
UdpEchoClientHelper echoClient(csmaInterfaces.GetAddress(nCsma), 9);
echoClient.SetAttribute("MaxPackets", UintegerValue(1));
echoClient.SetAttribute("Interval", TimeValue(Seconds(1.0)));
echoClient.SetAttribute("PacketSize", UintegerValue(1024));
// 安装在 p2p 第一个节点上
ApplicationContainer clientApps = echoClient.Install(p2pNodes.Get(0));
clientApps.Start(Seconds(2.0));
clientApps.Stop(Seconds(10.0));
// 基本上,每个节点的行为就像是一个 OSPF(开放式最短路径优先)路由器,下面是进行设置
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
// 启用 pcap 跟踪
pointToPoint.EnablePcapAll("second");
// 最后一个参数是启用混杂模式,可以嗅探整个网络所有的数据包(而不是单纯本网络设备发出或接收的)
// 注意,CSMA 的第二个网络并不参与直接的数据包传输,但是他可以嗅探到
csma.EnablePcap("second", csmaDevices.Get(1), true);
Simulator::Run();
Simulator::Destroy();
return 0;
}3. p2p + CSMA + WiFi
// SPDX-License-Identifier: GPL-2.0-only
#include "ns3/applications-module.h"
#include "ns3/core-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
#include "ns3/mobility-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/ssid.h"
#include "ns3/yans-wifi-helper.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
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("ThirdScriptExample");
// 这个函数将输出移动节点的坐标,我们自己的追踪源
void
CourseChange(std::string context, Ptr<const MobilityModel> model)
{
Vector position = model->GetPosition();
NS_LOG_UNCOND(context << " x = " << position.x << ", y = " << position.y);
}
int
main(int argc, char* argv[])
{
// 是否开启日志
bool verbose = true;
// CSMA 额外节点个数
uint32_t nCsma = 3;
// WiFi 额外节点个数
uint32_t nWifi = 3;
// 是否开启 pcap 追踪
bool tracing = false;
CommandLine cmd(__FILE__);
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.
// 18 个节点的基本限制是由于网格位置分配器的配置造成的;
// 如果提供的节点超过 18 个,网格布局就会超出边界框。
if (nWifi > 18)
{
std::cout << "nWifi should be 18 or less; otherwise grid layout exceeds the bounding box"
<< std::endl;
return 1;
}
if (verbose)
{
LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);
}
// 创建两个 p2p 节点
NodeContainer p2pNodes;
p2pNodes.Create(2);
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));
NetDeviceContainer p2pDevices;
p2pDevices = pointToPoint.Install(p2pNodes);
// 其中一个 p2p 加入了 CSMA,并创建了 nCsma 个 CSMA 节点
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);
// 创建 nWifi 个 WiFi 节点,并将另一个 p2p 节点设置为 WiFi 的 AP(无线接入点)
NodeContainer wifiStaNodes;
wifiStaNodes.Create(nWifi);
NodeContainer wifiApNode = p2pNodes.Get(0);
/**
* 构建 WiFi 设备之间互联的信道(使用默认的 PHY 层配置)
* 由这个创建的所有 PHY 层对象共享相同的底层通道
*/
YansWifiChannelHelper channel = YansWifiChannelHelper::Default();
YansWifiPhyHelper phy;
phy.SetChannel(channel.Create());
// 接下来是 MAC 层
WifiMacHelper mac;
// 创建 802.11 服务集标识符(SSID)对象
Ssid ssid = Ssid("ns-3-ssid");
/**
* 默认情况下,WifiHelper 使用的标准是 802.11ax(商业上称为 Wi-Fi 6)
* 并且配置了兼容的速率控制算法
*/
WifiHelper wifi;
// 使用 NetDevice 容器来存储这些 WifiNetDevice 对象
NetDeviceContainer staDevices;
/**
* 当标准新于 802.11n 时,QosSupported 默认为 true
* - 这两种配置的结合意味着,下一个创建的 MAC 实例将是
* 一个具有 QoS 感知的非 AP 站(STA),并且在 infrastructure BSS(即带有 AP 的 BSS)中。
* 注:
* - BSS 基本服务集(Basic Service Set),BSS=STA+AP+传输媒介。
* - STA 工作站(Station)
* - ActiveProbing 设置为 false,这意味着由该辅助程序创建的 MAC 将不会发送探测请求,站点将侦听
* AP 信标。
*/
//
mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid), "ActiveProbing", BooleanValue(false));
staDevices = wifi.Install(phy, mac, wifiStaNodes);
NetDeviceContainer apDevices;
mac.SetType("ns3::ApWifiMac", "Ssid", SsidValue(ssid));
apDevices = wifi.Install(phy, mac, wifiApNode);
/**
* 接着,我们想让 STA 节点是移动的,在一个边界框内游荡;同时 AP 是静止的
*/
MobilityHelper mobility;
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/de/d2e/classns3_1_1_grid_position_allocator.html
*
* 在矩形二维网格上分配位置
* - MinX 布局开始的 X 坐标
* - MinY 布局开始的 Y 坐标
* - DeltaX 两个 X 连续位置之间的 X 间隔
* - DeltaY 两个 Y 连续位置之间的 Y 间隔
* - GridWidth 在一条线上布局的对象数
* - LayoutType 布局类型(RowFirst|ColumnFirst)
*/
mobility.SetPositionAllocator("ns3::GridPositionAllocator",
"MinX",
DoubleValue(0.0),
"MinY",
DoubleValue(0.0),
"DeltaX",
DoubleValue(5.0),
"DeltaY",
DoubleValue(10.0),
"GridWidth",
UintegerValue(3),
"LayoutType",
StringValue("RowFirst"));
/**
* https://www.nsnam.org/docs/release/3.40/doxygen/d0/d59/classns3_1_1_random_walk2d_mobility_model.html
*
* 二维随机游走模型
* - Bounds 设置了边界
*/
mobility.SetMobilityModel("ns3::RandomWalk2dMobilityModel",
"Bounds",
RectangleValue(Rectangle(-50, 50, -50, 50)));
mobility.Install(wifiStaNodes);
// AP 是固定位置的
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(wifiApNode);
// 安装协议栈
InternetStackHelper stack;
stack.Install(csmaNodes);
stack.Install(wifiApNode);
stack.Install(wifiStaNodes);
// 分配 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);
// 设置 echo 服务器,安装在 CSMA 最后一个节点上
UdpEchoServerHelper echoServer(9);
ApplicationContainer serverApps = echoServer.Install(csmaNodes.Get(nCsma));
serverApps.Start(Seconds(1.0));
serverApps.Stop(Seconds(10.0));
// 设置 echo 客户端,安装在 WiFi STA 最后一个节点上
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));
// 由于构建了一个互联网络,因此需要启用互联网络路由
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
// 但由于我们要求 AP 生成信标(beacons),他会无限期生成下去,所以我们要告诉模拟器停止的时间
Simulator::Stop(Seconds(10.0));
if (tracing)
{
// 在两个 p2p 节点上开启混杂(监视)模式
phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
pointToPoint.EnablePcapAll("third");
phy.EnablePcap("third", apDevices.Get(0));
csma.EnablePcap("third", csmaDevices.Get(0), true);
}
/**
* 下面这部分,将用于输出移动设备的坐标
* 我们跟踪 WiFi 最后一个节点的坐标
* 我们将该节点的追踪源和我们的追踪源联系起来,以便于我们能够打印它的位置
*/
std::ostringstream oss;
oss << "/NodeList/" << wifiStaNodes.Get(nWifi - 1)->GetId()
<< "/$ns3::MobilityModel/CourseChange";
Config::Connect(oss.str(), MakeCallback(&CourseChange));
// 运行
Simulator::Run();
Simulator::Destroy();
return 0;
}参考资料:
【ns-3】零基础安装教程 https://blog.csdn.net/Graduate2015/article/details/129027889