ns-3脚本初识——点对点有线网络:first脚本

ns-3脚本初识——点对点有线网络:first脚本

示例first.cc所在位置为ns-3.29/examples/tutorial/,该目录为学习编写ns-3脚本的基础示例。

这个脚本创建了一个包含两个节点的有线网络,其链路层使用点对点协议(Point-To-PointProtocol,PPP)传输分组。

ps:本文所有文件和目录的默认根目录均为ns-3.29/。

1.代码规范

/* -*- 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
 */

在ns-3的每一个文件头看到相应的GNU法律条文。通常会在GPL内容的上方看到一个相关机构的版权声明,而在GPL内容的下方会有相应的作者列表。ns-3中好多代码在此都有作者列表和其联系方式(个人主页或邮箱等),这个信息使读者可以和原作者联系、交流,获得更多关于代码以外的信息。

2.头文件

#include "ns3/core-module.h"                   //core模块
#include "ns3/network-module.h"                //network模块
#include "ns3/internet-module.h"               //internet模块
#include "ns3/point-to-point-module.h"         //point-to-point模块
#include "ns3/applications-module.h"           //applications模块

ns-3是一个由不同模块组成的程序库。模拟脚本通过调用各个模块提供的API进行网络模拟,而每个模块的API都被统一存放在“<模块名>-module.h”中。因此,在添加头文件时只需要知道所需模块名即可。

Core模块和network模块头文件是所有脚本必须包括的,它们分别定义了ns-3的核心功能(如模拟事件,事件调度等)和基本网络组件(如网络节点,分组和地址等)。

Internet和applications模块也是大部分脚本会用到的模块,internet模块定义了TCP/IP协议栈,applications模块定义了应用层的分组收发模型。

如果脚本中使用非ns-3库的函数,则其头文件也要在此处添加。

3.命名空间

using namespace ns3;

C++用using来把ns-3命名空间引入到当前的(全局的)声明域中,这个声明就是说,你不用为了使用ns-3的代码而必须在所有的ns-3代码前打上ns3::作用域操作符。ns-3 工程是在一个叫做ns3的C++命名空间中实现的,这把所有与ns-3相关的声明集中在一个与全局命名空间相区别的命名空间中。因此,每一个脚本的开始都需要声明命名空间。但是,如果使用标准C++的内容可能就要加上std::前缀了,如“std::cout” “std::min()”等。

4.日志

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

该语句的功能是用特殊的名字定义一个日志组件。

5.主函数声明

main (int argc, char *argv[])
{
…
}

ns-3脚本和一个普通的C++程序一样,需要定义一个会被第一个执行的主函数。

6.主函数准备工作

  CommandLine cmd;                                     //命令行
  cmd.Parse (argc, argv);                              //读取命令行参数
  //代表着用户可以用命令行来访问代码中的全局变量和ns-3中的属性系统     
   
  Time::SetResolution (Time::NS);                      //最小时间单元:ns
  LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
  LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);

最后两行脚本是用来使2个日志组件生效的,它们将“UdpEcho”应用程序的客户端clients和服务器端server的日志级别设置为“INFO”级,这样,当仿真产生数据分组发送和接受时,对应的应用就会输出相应的日志信息到相关的日志模块。

7.创建网络拓扑

在ns-3中,节点和信道被分别抽象为Node,Channel以及节点中链接信道的网络设备NetDevice类这3个C++类。不同类型的信道对应不同的NetDevice和Channel子类。

例如,对于first脚本中的点对点(PPP)信道来说,对应的网络设备类是PointToPointNetDevice,信道类是PointToPointChannel。

一般来说,NetDevice主要负责实现链路层协议,Channel主要负责实现物理层协议。

first脚本中的Node,Channel和NetDevice类的对象关系如下图所示,其中箭头代表分组的传输方向。
ns-3脚本初识——点对点有线网络:first脚本_第1张图片

1)创建网络节点
  NodeContainer nodes;
  nodes.Create (2);

NodeContainer类是ns-3的一个Helper类(以后会另写其介绍),能一次操作多个节点,它包含许多方法。此处用的是Creat(unit32_t n),生成n个节点。其他方法可以在文档中详细查阅。

节点代表一台能够加入诸如协议栈,应用以及外设卡等的计算机。上面的第一行只是声明了一个名为”nodes”的NodeContainer。第二行创建了两个节点。在脚本中他们所代表的节点什么都没有做。构建拓扑的下一步是把节点连接到一个网络中。

2)配置信道属性
  PointToPointHelper pointToPoint; //PPP信道助手类
  //配置PPP信道属性
  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));//传输速率属性    
  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));//传播迟延属性

两个概念:
助手类Helper负责把网络设备连接到节点和信道,屏蔽了很多实现细节,可以帮助用户更为简洁地在脚本中创建网络拓扑。
属性本质上就是C++类中的一个变量。

第一行初始化类一个PointToPointHelper的对象pointToPoint。第二行从上层的角度告诉PointToPointHelper对象当创建一个PointToPointNetDevice对象时使用“5Mbit/s”来作为数据速率,“DataRate”是PointToPointNetDevice的一个属性。第三行则是使用“Delay”属性告诉PointToPointHelper使用“2ms”作为每一个被创建的点到点信道的传输时延值。

3)创建信道并连接节点
  NetDeviceContainer devices;                            //创建网络设备
  devices = pointToPoint.Install (nodes);                //连接节点与信道

PointToPointHelper::Install()函数内部分别创建类两个PPP网络设备对象(PoingToPointDevice)和一个PPP信道对象(PointToPointChannel)。这两个网络设备对象被分别安装在两个节点中,又公共同连接至同一个信道对象。Install()方法以一个NodeContainer 对象做为一个参数,当它被执行后,它会为每一个节点容器中的节点安装一个网络协议栈(TCP,UDP,IP等)。它的返回值是一个网络上设备容器 NetDeviceContainer对象,这个容器包含了为NodeContainer中所有节点所分配的NetDevice对象。例如,nodes.Get(0)节点中的网络设备是devices.Get(0)。

8.安装TCP/IP协议族

上一步2)中所说的上层协议值得就是TCP/IP协议族。ns-3中的TCP/IP协议族主要包括传输层的TCP和UDP,网络层的IP(IPv4与IPv6)和ICMP(ICMPv4与ICMPv6)以及一系列路由协议。

为节点安装TCP/IP协议栈的助手类是InternetStackHelper。

  InternetStackHelper stack;
  stack.Install (nodes);                //为nodes容器中的节点安装TCP/IP协议栈

安装了TCP/IP协议栈的节点还不能直接通信,还需要为节点的网络设备分配IP地址。在first脚本中,是通过IPv4AddressHelper(分配IPv4地址)完成的。

  Ipv4AddressHelper address;                       //为网络设备分配IP地址
  address.SetBase ("10.1.1.0", "255.255.255.0");

  Ipv4InterfaceContainer interfaces = address.Assign (devices);

以上代码中,助手类IPv4AddressHelper以10.1.1.0为起始地址,以255.255.255.0为子网掩码,分别向两个主机分配了10.1.1.1和10.1.1.2两个IP地址。Ipv4Interface对象将一个IP地址同一个设备关联起来。

底层ns-3系统事实上会记住所有分配的IP地址,如果无意导致了相同IP地址的产生,这将是一个致命的错误。

现在,已经有了一个安装了协议栈,并且配置了IP地址类的点到点的网络。接下来要运用它产生数据通信。

9.安装应用层

ns-3中的应用是对物理世界中应用程序内部网络通信功能的抽象,即模拟分组的发送和接收行为。

ns-3应用层协议对应的C++类是Application。它的不同子类定义了不同的分组收发行为。
例如,BulkSendApplication使用贪婪分组发送模型;OnOffApplication使用ON/OFF分组发送模型。
该first模拟脚本选择的是ns-3中一种叫做UdpEcho的应用程序:

  UdpEchoServerHelper echoServer (9);     //服务端设置端口号

这行代码声明了UdpEchoServerHelper。
必须告知助手服务器和客户端所共知的一个端口号(此处为9号端口),否则这个助手不能起任何作用。

应用对象需要一个时间参数来“开始”产生数据通信并且可能在一个可选的时间点“停止”。这些时间点是用ApplicationContainer的方法Start和Stop来设置的。

  //在节点1中安装服务端程序
  ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));
  serverApps.Start (Seconds (1.0));
  serverApps.Stop (Seconds (10.0));

第一行在节点1中创建了一个服务器端回显服务应用echoSever。echoSever在模拟自启动后1.0s时开始监听并接收9号端口的数据在接收到数据包后向echoClient返回一个相同大小的UDP数据包,并在10.0s停止。

echo客户端应用的设置与回显服务器端类似,也有一个UdpEchoClientHelper来管理。

  //配置客户端程序属性
  UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9);
  echoClient.SetAttribute ("MaxPackets", UintegerValue (1));
  echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));

interfaces.GetAddress (1)为服务器端地址即n1,9为端口号。
“MaxPackets”,“Interval”,"PacketSize"是UdpEchoClient类的3个属性。“MaxPackets”属性表示最大发送分组个数,告诉客户端我们所允许它在模拟期间所能发送的最大数据包个数。“Interval”属性表示分组发送间隔,告诉客户端在两个数据包之间要等待多长时间。“PacketSize”属性表示分组负载字节大小,告诉客户端它的数据包应该承载多少数据。

  //在节点0中安装客户端程序
  ApplicationContainer clientApps = echoClient.Install (nodes.Get (0));
  clientApps.Start (Seconds (2.0));
  clientApps.Stop (Seconds (10.0))

UdpEchoClientHelper助手类在节点0中创建了一个客户端echoClient。echoClient在模拟自启动后2.0s时开始向节点1的9号端口发送一个1024B的UDP数据包,并在10.0s停止。

10.数据生成

产生数据是网络模拟的一个必备功能,否则无法分析网络。属性配置和数据生成是ns-3脚本中两个非常重要的组成部分(一个输入一个输出)。由于first模拟脚本是最简单的示例程序,并未涉及数据生成操作。

11.启动与结束

下面我们所需要做的就是运行模拟器,这是用全局函数Simulator::Run来做到的:

  Simulator::Run ();

前面在模拟器中1.0s,2.0s,和10.0s时预设了事件的发生。当Simulator::Run被调用时,系统会开始遍历预设事件的列表并执行。首先它会在1.0s时运行事件,这个事件会使echo服务端应用生效。接下来仿真器会运行在2.0s时的事件,即让echo客户端应用开始发送数据包。接下来的事件就是服务端和客户端的Stop事件。当这些事件被执行后,就没有将来的事件来执行了,函数Simulator::Run会返回。整个模拟过程就结束了。

下面剩下的事情就是清理了。这个通过调用全局函数Simulator::Destroy来完成。

  Simulator::Destroy ();
  return 0;

12.上机实践

在ns-3.29(或其他的版本)目录下运行first模拟脚本(需要在编译时启动enable-examples选项,不然就将脚本放到scratch目录下)。

./waf --run first

cp examples/tutorial/first.cc  scratch/myfirst.cc  //将脚本复制到scratch目录下
./waf --run scratch/myfirst

运行结果如下:

Waf: Entering directory `/home/w/tarballs/ns-allinone-3.29/ns-3.29/build'
[1840/2459] Linking build/src/aodv/examples/ns3.29-aodv-debug
Waf: Leaving directory `/home/w/tarballs/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (3.985s)
At time 2s client sent 1024 bytes to 10.1.1.2 port 9
At time 2.00369s server received 1024 bytes from 10.1.1.1 port 49153
At time 2.00369s server sent 1024 bytes to 10.1.1.1 port 49153
At time 2.00737s client received 1024 bytes from 10.1.1.2 port 9

输出信息显示了UdpEcho应用的行为:2s时,节点0(10.1.1.1)发送了一个大小为1024B的数据包给节点1(10.1.1.2)。经过0.00369s,节点1成功接收到数据包后返回一个同样大小的数据包。再经过相同的时间,节点0接收到该返回数据包。

为什么是0.00369s?
这是分组从节点0到节点1的传输延迟(分组从节点发至信道的用时)和传播延迟(分组在信道中的用时)的总和。
由上面的第7步2),该脚本中PPP信道的传播延迟为0.002s。传输速率为5Mbps,节点0的传输延迟为(结果保留5位小数):传输延迟=分组大小/传输速率=(1054×8)/(5×10^6)=0.0016864s≈0.00169s
其中,1054是1024B负载,20B IPv4分组头,8B UDP分组头和2B PPP分组头大小的总和。因此,单向延迟是0.00369s。

输出可视化界面的代码如下:

./waf --run scratch/myfirst --vis

仿真截图如下:
ns-3脚本初识——点对点有线网络:first脚本_第2张图片zoom调整窗口大小,speed修改仿真进行的速度,snapshot用来抓屏,simulate按钮开始和结束仿真。鼠标放在节点上可以显示节点的网络设备等信息。单击Simulate仿真按钮开始仿真,在仿真过程中出现的绿色代表通信状态。

你可能感兴趣的:(ns-3示例脚本)