// 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
新问题新在哪里,左边的5、6、7、0结点不再依靠有线网络进行通信,而是组成了一个10.1.3.0的无线网络。现在我们对问题做出表述:p2p链接上信道延迟为2ms,数据传输速率5Mbps,有线局域网CSMA信道延迟6560ns,数据传输速率100Mbps,wifi网络的传播延迟与损失保持默认,4号结点作为服务器,最后一个wifi结点作为客户端。需求明确,让我们开始吧!
不难发现,除了左侧的WiFi网段,其余部分和我们之前做的Second脚本很是相似,那么就相似的内容我们就简单说说咯。
#include "ns3/core-module.h"
#include "ns3/applications-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/netanim-module.h"
这部分的头文件我们都接触过的,如有遗忘主动学习呐~
#include "ns3/mobility-module.h"
#include "ns3/ssid.h"
#include "ns3/yans-wifi-helper.h"
#include "ns3/wifi-module.h"
这三个头文件是第一次看到,让我们逐一分析:
1. Mobility:是否记得运行上节Second脚本时终端出现的一些提示(下图)。相信你可以看懂,他提示我们,这些节点没有一个移动模型,所以使用了固定位置模型来作为他们的Mobility。Mobility模块在NS-3中的意义是提供一种机制,使得可以模拟移动网络中节点的移动性,以评估和研究移动网络的性能、路由协议、覆盖范围、信号强度以及移动应用和服务的行为。它对于移动网络的研究、设计和优化非常重要。
2. Ssid:(Service Set Identifier)是无线网络中的一个重要概念,它是一个用于标识无线局域网(WLAN)的名称。SSID可以看作是无线网络的名称。在这里我们需要使用ssid划定服务集,使AP点和移动点处于一个服务集,只有这样他们才能通信。
3. Yans-wifi-helper:我们使用YansWifiChannelHelper和YansWifiPhyHelper定义无线网络的信道属性。为什么交Yans呢,Yet Another Network Simulator,可见这个模块属于NS3的扩展模块,属于NS3的一部分,使针对WiFi网络的模拟需求而开发的模块。
最后一个 wifi模块就不赘述了,毕竟third脚本是要和wifi打交道,要是连wifi本身都不提及,是不是有点不太合适呢?
using namespace ns3;
using namespace std;
NS_LOG_COMPONENT_DEFINE("My_New_Third");
命名空间和日志组件定义就是如此,毫无新意~
bool verbose = true;
if(verbose)
{
LogComponentEnable("UdpEchoClientApplication",LOG_LEVEL_INFO);
LogComponentEnable("UdpEchoServerApplication",LOG_LEVEL_INFO);
}
这部分的逻辑是不是很熟悉,没错,我们还是定义一把“锁”去控制回显信息是否可见。
uint32_t nWifi = 3;
uint32_t nCsma = 3;
bool tracing = false;
根据题目要求,我们定义了两个变量,分别记录左侧wifi节点数和右侧csma结点数(p2p肯定只有俩鸭),tracing是什么呢?脚本运行后会产生很多输出文件和trace信息,可有时候我们不太希望生成新的输出文件,为了解决这一问题,这里是由tracing变量控制是否输出文件。
CommandLine cmd(__FILE__);
cmd.AddValue("nWifi","Number of wifi STA devices",nWifi);
cmd.AddValue("nCsma","Number of CSMA devices",nCsma);
cmd.AddValue("verbose","Tell echo applications to log if true",verbose);
cmd.AddValue("tracing","Enable pcap tracing",tracing);
cmd.Parse(argc,argv);
老规矩,点开文件找变量太麻烦,我们直接通过终端临时改变变量。这里将四个变量都加入到了cmd能够控制的Value中。
官方的脚本旨在为我们提供一个学习代码的思路和习惯,其考虑到环境中按照特定分布方式布置结点时,过多wifi结点会出现超出画面的情况,因此官方加入了节点限制:
if(nWifi > 18)
{
cout << "nWifi should be 18 or less; otherwise grid layout exceeds the bounding box" <
NodeContainer p2pNodes;
p2pNodes.Create(2);
PointToPointHelper pointToPoint;
pointToPoint.SetChannelAttribute("Delay",StringValue("2ms"));
pointToPoint.SetDeviceAttribute("DataRate",StringValue("5Mbps"));
NetDeviceContainer p2pDevices = pointToPoint.Install(p2pNodes);
NodeContainer csmaNodes;
csmaNodes.Add(p2pNodes.Get(1));
csmaNodes.Create(nCsma);
CsmaHelper csma;
csma.SetChannelAttribute("Delay",TimeValue(NanoSeconds(6560)));
csma.SetChannelAttribute("DataRate",StringValue("100Mbps"));
NetDeviceContainer csmaDevices = csma.Install(csmaNodes);
哈哈,很开心能一下子粘这么一大段代码,因为这些我们都非常熟悉,不是吗?首先搭建一个p2p拓扑,然后搭建右侧的CSMA 拓扑,别忘了先把1号节点并入CSMA网段,这时候我们要清楚CSMA结点的编号关系:CSMAList:1(0)、2(1)、3(2)、4(3),括号里的是在CSMA结点容器中各结点的编号,括号外是结点在拓扑中的编号(注意我们先执行了Add把1号点加入了CSMA)。加入节点后分别使用助手类定义了两个网段相关属性。剩下的wifi拓扑还没布置,可不能忘了:
NodeContainer wifiStaNodes;
wifiStaNodes.Create(nWifi);
NodeContainer wifiAPNode = p2pNodes.Get(0);
这里不光定义了WiFi结点容器,还单独为AP结点定义了一个容器,将0号结点加入到AP点容器中。在定义wifi网络的物理层和链路层时,我们首先要了解NS3中wifi结点的组成方式:
对于wifi结点,其物理层和链路层功能分别由WifiMac和Wifiphy配置,而WifiNetDevice本身没有什么实质作用,只起到一个链接上下层的作用。NetDeveice+Mac+Phy三层网络结构在无线节点中比较常用,这对我们而言确实是个新知识点。
那么就将学到的拿来实践一下吧。
YansWifiChannelHelper channel = YansWifiChannelHelper::Default();
YansWifiPhyHelper phy;
phy.SetChannel(channel.Create());
这里你可能会奇怪,为什么channel需要调用一下Default,而phy不需要呢。实际上在教材中,phy也进行了Default调用,可是当我们在ns3.39输入时,他会告诉我们phy没有这个函数,让我们找一下源码:
YansWifiChannelHelper::Default()
{
YansWifiChannelHelper helper;
helper.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
helper.AddPropagationLoss("ns3::LogDistancePropagationLossModel");
return helper;
}
YansWifiPhyHelper::YansWifiPhyHelper()
: WifiPhyHelper(1), // YANS phy is not used for 11be devices
m_channel(nullptr)
{
m_phy.at(0).SetTypeId("ns3::YansWifiPhy");
SetInterferenceHelper("ns3::InterferenceHelper");
SetErrorRateModel("ns3::TableBasedErrorRateModel");
}
phy在构造的时候就已经设置好了误码率模型等参数,我们只需要进行“安装信道”就好了。
在这里有必要说明一点:这部分Channel使用的Default方法设置了两个比较重要的属性,传播延迟Propagation Delay和损耗模型Propagation Loss,这两个属性加上最后的移动模型决定了分组在无线信道中的传播延迟和接收功率。
信道配置完毕,下一步进行链路层配置,这时我们需要用到WifiMacHelper和WifiHelper两个助手,前者指定节点的种类,使用后者为WifiNetDevice安装mac和phy。此外我们在头文件引用到的ssid也要出场了,他为ap结点和移动节点定义了服务集标识。
WifiHelper wifi;
WifiMacHelper mac;
Ssid ssid = Ssid("ns-3-ssid");
mac.SetType("ns3::StaWifiMac",
"Ssid",SsidValue(ssid),
"ActiveProbing",BooleanValue(false));
NetDeviceContainer staDevice = wifi.Install(phy,mac,wifiStaNodes);
mac.SetType("ns3::ApWifiMac",
"Ssid",SsidValue(ssid));
NetDeviceContainer apDevice = wifi.Install(phy,mac,wifiAPNode);
上面的代码中,ssid定义了名为"ns-3-ssid"的标识,mac的第一次SetType设置了移动结点的相关属性,分别将phy、mac、和结点容器送入wifi的install方法,定义一个设备容器去接收Install的返回值。随后mac开始对AP点属性进行定义,这时候会发现AP点MAC并没有定义"AectiveProbing"。"AectiveProbing"属性通常用于移动设备(如移动终端或客户端)的MAC层,用于探测和选择最佳的接入点。而接入点本身不需要进行主动探测和选择,因为它通常是固定的、提供服务的设备。
很好,最后让我们来布置移动模型。值得注意的是,移动模型一般在wifi设备安装完毕后再进行配置,原因在于不同Wifi结点类型需要不同的移动模型,同时要注意移动节点和AP接入点的区别。在这里重点使用Mobility的SetPositionAllocator、SetMobilityModel以及必不可少的Install。
MobilityHelper mobility;
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"));
mobility.SetMobilityModel("ns3::RandomWalk2dMobilityModel",
"Bounds",RectangleValue(Rectangle(-50,50,-50,50)));
mobility.Install(wifiStaNodes);
首先定义移动节点的运动模型,SetPositionAllocator实现结点在空间中的分布方式,通过查找官方手册position-allocator.cc我们可以看到NS3内置了多种分布方式,除了程序中用到的GridPositionAllocator,还有如RandomRectanglePositionAllocator、RandomBoxPositionAllocator等分布方式,每种方式都有其对应的属性变量。类似地,SetMobolity定义了结点的运动方式,NS3内置多种移动模型,如随机游走、恒速、恒加速度等。上面代码将wifi网络中移动节点进行网格分布,从(0,0)坐标开始,节点间横向间隔5个单位,纵向间隔10个单位,行优先排列且每行最多有3个结点,运动模型为2维平面随机游走模型,活动范围在边长为50的矩形。
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(wifiAPNode);
类似地,定义AP接入点的移动模型,就别让它乱跑了,既然它都不乱跑了,那就不需要其他复杂的设置了。在这里温馨提示大家,不要对自动补全太自信了,SetPositionAllocator、SetMobilityModel现在看区别很大,找bug的时候真的要非半条命。
InternetStackHelper stack;
stack.Install(csmaNodes);
stack.Install(wifiAPNode);
stack.Install(wifiStaNodes);
定义协议栈,安装到各个结点容器中,没什么复杂的,毕竟两个p2p结点已经加入到了其他容器中。
Ipv4AddressHelper address;
address.SetBase("10.1.1.0","255.255.255.0");
Ipv4InterfaceContainer p2pInterfaces = address.Assign(p2pDevices);
address.SetBase("10.1.2.0","255.255.255.0");
Ipv4InterfaceContainer csmaInterfaces = address.Assign(csmaDevices);
address.SetBase("10.1.3.0","255.255.255.0");
Ipv4InterfaceContainer staInterfaces = address.Assign(staDevice);
Ipv4InterfaceContainer apInterfaces = address.Assign(apDevice);
使用地址助手设置对应网段的信息,使用接口容器去接受地址的部署结果。这一步进展十分顺利~
UdpEchoServerHelper echoServer(9);
ApplicationContainer appServer = echoServer.Install(csmaNodes.Get(nCsma));
appServer.Start(Seconds(1.0));
appServer.Stop(Seconds(10.0));
问题来了:谁是服务器,就题目而言,我们希望4号结点时服务器,此时nCsma值为3,而4号结点在csmaNodes容器中的编号是多少呢,是2。忘记的话,我们再复盘一下:CSMAList:1(0)、2(1)、3(2)、4(3),括号里的是在CSMA结点容器中各结点的编号,括号外是结点在拓扑中的编号。因为1号结点是后加入csmaNodes的。
UdpEchoClientHelper echoClient(csmaInterfaces.GetAddress(nCsma),9);
echoClient.SetAttribute("MaxPackets",UintegerValue(1));
echoClient.SetAttribute("Interval",TimeValue(Seconds(1.0)));
echoClient.SetAttribute("PacketSize",UintegerValue(1024));
ApplicationContainer appClient = echoClient.Install(wifiStaNodes.Get(nWifi-1));
appClient.Start(Seconds(2.0));
appClient.Stop(Seconds(10.0));
根据题目,最后一个wifi结点,也就是结点7作为客户端,而WiFiStaList中:5(0)、6(1)、7(2),nWifi值3,所以定义客户端时wifiStaNodes.Get(nWifi-1)。也许会问,0号点难道不在容器里吗,0号可是AP点,有自己的“单人间”
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
对于结点0,在p2p网段和wifi网段都有ip地址,对于结点1,在p2p网段和csma网段都有ip地址,此类双模节点在Second脚本也有所接触。ns3有线网络最常用路由协议的就是全局路由:开放式最短路径优先(OSPF),通过PopulateRoutingTables()完成。
Simulator::Stop(Seconds(10.0));
if(tracing)
{
phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
pointToPoint.EnablePcapAll("new_third");
phy.EnablePcap("new_third",staDevice.Get(0),false);
csma.EnablePcap("new_third",csmaNodes.Get(0)->GetId(),false);
}
Simulator::Run();
Simulator::Destroy();
return 0;
这里设置了一个暂停时间,而且,他来了他来了他来了,tracing决定了是否输出文件。不同的Enable输出方式我们先前已经有所了解,但这里出现了不熟悉的身影:SetPcapDataLinkType,查看下源码:
void
WifiPhyHelper::SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
{
switch (dlt)
{
case DLT_IEEE802_11:
m_pcapDlt = PcapHelper::DLT_IEEE802_11;
return;
case DLT_PRISM_HEADER:
m_pcapDlt = PcapHelper::DLT_PRISM_HEADER;
return;
case DLT_IEEE802_11_RADIO:
m_pcapDlt = PcapHelper::DLT_IEEE802_11_RADIO;
return;
default:
NS_ABORT_MSG("WifiPhyHelper::SetPcapFormat(): Unexpected format");
}
}
大概解读下,这里是设置输出不同数据链路类型的pcap文件
依我看没什么事儿就RADIO吧。
./ns3 run 'scratch/new_third.cc --tracing=true' --cwd=scratch/new_Tomorrow/Third_output
让我们运行一下自己的程序,观察终端的输出,还有输出到对应文件夹的各类文件,使用Netanim软件可以观察网络的通信过程,验证一下我们的工作。
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/applications-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/internet-module.h"
#include "ns3/wifi-module.h"
// wifi模块
#include "ns3/ssid.h"
#include "ns3/mobility-module.h"
// 移动模块
#include "ns3/csma-module.h"
// csma模块
#include "ns3/yans-wifi-helper.h"
#include "ns3/netanim-module.h"
using namespace ns3;
using namespace std;
NS_LOG_COMPONENT_DEFINE("My_New_Third");
int
main(int argc,char *argv[])
{
/****************准备阶段********************************/
uint32_t nWifi = 3;
uint32_t nCsma = 3;
//定义拓扑中两个网段里的节点数量
bool tracing = true;
bool verbose = true;
//定义两把锁子,决定功能的启停
CommandLine cmd(__FILE__);
//定义命令行变量,决定是否能够通过终端控制变量
cmd.AddValue("nWifi","Number of wifi STA devices",nWifi);
cmd.AddValue("nCsma","Number of CSMA devices",nCsma);
cmd.AddValue("verbose","Tell echo applications to log if true",verbose);
cmd.AddValue("tracing","Enable pcap tracing",tracing);
cmd.Parse(argc,argv);
//可以通过终端来控制上述四个变量的变化
if(nWifi > 18)
{
cout << "nWifi should be 18 or less; otherwise grid layout exceeds the bounding box" <决定了分组的传播延迟和接收功率
//移动模型一般在wifi设备安装完毕后再进行配置,原因在于不同Wifi结点类型需要不同的移动模型
//与mac一样,要注意区分ap和移动点
MobilityHelper mobility;
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"));
mobility.SetMobilityModel("ns3::RandomWalk2dMobilityModel",
"Bounds",RectangleValue(Rectangle(-50,50,-50,50)));
mobility.Install(wifiStaNodes);
//把上面定义好的网格分布、随机游走模型安装在移动结点里
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(wifiAPNode);
//把重新定义的静止模型安装到AP接入点里
/***************网络拓扑总算搭建完成了****************************/
/***************开始对拓扑进行网络部署****************************/
InternetStackHelper stack;
stack.Install(csmaNodes);
stack.Install(wifiAPNode);
stack.Install(wifiStaNodes);
//定义协议栈并安装到结点容器中,p2p的两个结点已经分别加入到了wifiAP和csmaNodes里
Ipv4AddressHelper address;
//先把p2p所在的10.1.1.0网段部署好
address.SetBase("10.1.1.0","255.255.255.0");
Ipv4InterfaceContainer p2pInterfaces = address.Assign(p2pDevices);
//把csma所在的10.1.2.0网段部署好
address.SetBase("10.1.2.0","255.255.255.0");
Ipv4InterfaceContainer csmaInterfaces = address.Assign(csmaDevices);
//最后部署wifi的10.1.3.0网段
address.SetBase("10.1.3.0","255.255.255.0");
Ipv4InterfaceContainer staInterfaces = address.Assign(staDevice);
Ipv4InterfaceContainer apInterfaces = address.Assign(apDevice);
// address.Assign(staDevice);
// address.Assign(apDevice);
//分别配置网络接口
/********************网络部署完毕****************************/
/******************开始配置应用程序****************************/
UdpEchoServerHelper echoServer(9);
ApplicationContainer appServer = echoServer.Install(csmaNodes.Get(nCsma));
//使右侧CSMA中最后一个结点作为服务器
appServer.Start(Seconds(1.0));
appServer.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 appClient = echoClient.Install(wifiStaNodes.Get(nWifi-1));
appClient.Start(Seconds(2.0));
appClient.Stop(Seconds(10.0));
/******************应用程序配置完成****************************/
/********************全局路由问题****************************/
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
/*
对于结点0,在p2p网段和wifi网段都有ip地址,
对于结点1,在p2p网段和csma网段都有ip地址
自己所有的两个网络设备地址分配的时候属于不同地址,
此时需要这种连接节点具有路由功能
ns3有线网络最常用路由协议的就是全局路由:开放式最短路径优先(OSPF)
通过PopulateRoutingTables()完成
*/
/********************路由问题解决****************************/
/*******************仿真与数据追踪****************************/
Simulator::Stop(Seconds(10.0));
if(tracing)
{
phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
//设置不同数据链路类型
/*
DLT_IEEE802_11:
值:PcapHelper::DLT_IEEE802_11
含义:指定数据链路类型为 IEEE 802.11 无线局域网数据包的头部格式。
DLT_PRISM_HEADER:
值:PcapHelper::DLT_PRISM_HEADER
含义:指定在数据包中包含 Prism 监听模式信息的数据链路类型。
DLT_IEEE802_11_RADIO:
值:PcapHelper::DLT_IEEE802_11_RADIO
含义:指定在数据包中包含 Radiotap 链路层信息的数据链路类型。
*/
pointToPoint.EnablePcapAll("new_third");
phy.EnablePcap("new_third",staDevice.Get(0),false);
csma.EnablePcap("new_third",csmaNodes.Get(0)->GetId(),false);
//获取pcap文件的三种方式
}
AnimationInterface anim("third_go.xml");
cout << csmaNodes.Get(0)->GetId();
Simulator::Run();
Simulator::Destroy();
return 0;
}
这篇文章写了蛮长时间,其中结点的编号问题,地址的分配问题花了不少时间去思考,也担心自己的错误理解带偏大家,如有错误还请大家及时指正。
记得,明天是个大晴天~