茹新宇,刘 渊
(1.江南大学 数字媒体学院,江苏 无锡 214122;2.江苏联合职业技术学院 无锡交通分院,江苏 无锡 214151)
仿真所固有的低成本、虚拟化优势,使其成为极具吸引力的重要科研手段[1]。网络仿真目前已是互联网算法性能、协议拓扑最经济快捷的评价方法之一。网络仿真器主要通过部署虚拟环境,允许对特定节点或路径模拟其网络行为,并提供仿真结果,以便研究、参考或改进。借助仿真的高灵活度和可扩展性,不同网络实体的协议算法、概念模型及其拓扑架构可在虚拟环境中搭建、测试并实施。现有工具,如QualNet、OPNET[2]和REAL等,由于使用条款、实施成本及代码开源等因素而远未普及。鉴于现场布置及情景再现等客观条件,新网络仿真器NS3应运而生。作为NS2的继任者,它运用全新理念来解决和减轻NS2存在的问题,现已大量部署于实验场景。它支持当前主流协议,为实验提供仿真结果。
文中对NS3进行了深入探究,重点阐述了其TCP实现过程,并提出了具有前瞻性的研究方向。
NS系列仿真器是由UC Berkeley开发的优秀网络仿真软件,使用经典的Unix语言作为环境工具,在GNU/Linux平台下开发[3]。为便于安装、维护及更新,如今已被移植进Windows/Cygwin,BSD及Mac OSX等系统。NS2作为一款开源软件,支持多种协议且易于扩展,但同时也存在较多问题。如它采用分裂对象模型,在OTcl中编写脚本,仿真结果由NAM实现可视化,却无法单纯从C++运行。另外,网络层抽象仿真,结果跟踪困难,需借助解析文件提取,模型之间互操作及耦合性不足,分析工具“各自为政”。
目前仍在大规模开发的NS3项目始于2006年,它广泛汲取主流仿真器NS2、YANS和GTNets的技术经验,用C++语言实现,兼容时下流行的Python,目前已发展至3.26稳定版本。NS2部分模型已移植进NS3,使之在功能实现、版本更新、用户体验等方面都具有良好表现[4]。其核心及各功能模块由C++代码完成[5],对外提供丰富的扩展接口[6],可按需更改或增减模块。NS3包含网络组件模拟接口,拥有事件调度器,可通过执行相关事件,模拟真实通信“行为”。同时它还具备完美的跟踪机制,便于用户解析数据、传输过程及分析结果,详细介绍如表1所示。
表1 NS3仿真器的优点与特点
NS2及其后继者NS3具有相同背景、概念和类似目标,是一离散事件模拟器。虽然目前NS2仍有很大用户群,但由于NS3的特征及设计优势,使之日趋成为NS2的可靠替代品。它可较好地规避前者存在的问题,其协议和场景均用C++编写。第三方软件依赖和源代码构建过程截然不同并优于NS2,系统集成度较好。两者的部分特征对比如表2所示。
表3总结了现NS2已有模型与NS3计划或已开发模型间的对比[7]。
表2 NS2与NS3的部分特征对比
表3 NS2与NS3的项目模型对比
NS3由一系列层次分明的功能模块拼接而成,其组织结构及模块间的基本依赖关系如图1所示。
图1 NS3的基本模块体系结构
2.1.1常用模块
Core:内核模块,是NS3基本机制的实现,如智能指针(Ptr)、属性(attribute)、回调(callback)、随机变量(random variable)、日志(logging)、追踪(tracing)和事件调度(event scheduler)等内容。
Network:数据分组(packet)模块。
Internet:关于TCP/IPv4/6的相关协议族的实现,包括TCP/IPv4/6、ARP、UDP及邻居发现等相关协议。
Topolopy-read:读取指定轨迹文件数据,按指定格式生成相应网络拓扑。
Protocol Status:协议框架模块,收集、统计和分析数据。
另有,Tools:统计工具(包括gnuplot作图接口);ApplicatioNS:应用模块;Mobility:移动模块;Visualizer:可视化工具;Netanim:动画演示器;Propagation:传播模型模块;Flow-monitor:流量监控模块等。
2.1.2典型模块
CSMA:基于IEEE802.3以太网,包括MAC层、物理层和媒体信道。
Point-to-point:实现网络间的点对点通信。
Wifi:基于IEEE802.11a/b/g无线网,包括AdHoc。
其他还有,Mesh:基于IEEE802.11s的无线网络;Wimax:基于IEEE802.16的无线城域网;LTE:3GPP通用移动通信系统(UMTS)技术长期演进;UAN:水声通信网络;MPI:并行分布式离散事件标准信息传递接口;Click:集成可编程模块化路由;Openflow:仿真交换机;Emu:集成实验床和虚拟机环境等。
2.1.3最新技术
Waf是基于Python框架开发的编译工具,NS3自身及将要执行的仿真代码都由Waf编译运行。Scratch目录存放用户脚本,运行案例可拷至该目录。Example举例如何使用NS3,内含许多模块的使用。Doc目录是帮助文档,可使用./waf--doxygen编译本地Doxygen文档。Build编译目录,内含编译文件时使用的共享库和头文件(build/NS3)。Src是NS3的源码目录。
模块中的wscript文件结构固定,用来注册模块中的源码和使用其他模块情况。Model目录包含模块代码的.cc和.h文件。Helper目录存放模块对应的helper类代码的源文件。Test目录包含原模块测试代码,而examples目录存放应用该模块的实例代码。Doc是帮助文档,bindings目录则被模块用来绑定Python语言。
NS3以较低层次的抽象来构造组件,模拟真实环境[8],其基本对象是节点、应用、信道和网络设备等,由类表示或实现。
(1)节点(Node):网络上所连接的基本计算单元或终端都抽象为节点,在C++中由Node类描述(如Nodeontainer类),用于追踪一组节点指针。用户可通过对节点添加应用、协议和网卡等进行二次开发[9]。
(2)应用(Application):被仿真的用户程序抽象为应用。NS3以“Time”为参数,记录接收和发送时间。在C++中抽象成Application类表示,实现时需继承该类,将其部署在节点,驱动仿真器运行。由应用程序生成和回显仿真数据包的客户/服务器端程序集,如Application类称为“UdpEchoClient Application”和“UdpEchoServer Application”。
(3)信道(Channel):基本的通信子网被抽象为信道。在C++中由Channel类描述,提供管理通信子网对象和节点连接至它们的各种方法。它可模拟简单线缆、无线网,甚至以太网交换机。NS3包括Csma Channel、Point To Point Channel以及Wifi Channel等信道。
(4)网络设备(NetDevice):硬件设备和软件驱动的抽象表示,由C++的NetDevice类实现。通过绑定与类型匹配的信道,提供管理节点和信道对象连接。与信道对应,NetDevice也分为Csma NetDevice、Point To Point NetDevice和Wifi NetDevice等[10]。若需要一个所有被创建的NetDevice对象列表,则需用一个NetDeviceContainer对象来存放。
(5)分组(Packet):每个分组包含字节缓冲器、标签和元数据。缓冲器是头部和尾部的逐位串行表示,标签则是任意用户提供的数据结构的集合,而元数据可用以描述已序列化的头和尾的类型。
(6)套接字(Socket):作为应用程序与网络堆栈间的接口。NS3提供了两种类型的套接字API,一是NS3API,二是使用本机API服务来提供类似POSIX的API,作为整个应用程序进程的一部分。
(7)拓扑帮助器(Topology Helper):NS3用Topology Helper类来整合大量分立步骤,使其成为一简单易用的操作。由拓扑生成器调用底层核心完成节点、网络设备、MAC地址、信道及协议栈等创建与配置。它可实现多节点连接及多子网联网、分配IP地址等功能。如Topology Reader Helper类可简化配置并使用通用Topology Reader。Internet Stack Helper是个安装Point To Point Helper对象和点到点网络设备的网络协议栈的拓扑生成器类。
(8)典型容器助手(typical containers and helpers)。NS3分容器类和助手类,NodeContainer、NetDevice Container和Ipv4AddressContainer是不同容器类,而InternetStackHelper、WifiHelper、MobilityHelper和OlsrHelper则属不同助手类。
NS3体系结构类似OSI模型,便于对网络层次及协议仿真研究。数据按TCP/IP形式发送,以类方式实现。事件由节点触发,当数据包到达节点,仿真器按事件预定的执行时间排序队列、按步处理。数据包传递时,通过指针交互完成转发,传输过程如图2所示[11]。
图2 NS3数据包传输过程
首先应用层创建数据包并通过套接字传送至传输层(用套接字指针取代应用层指向该数据包)。在该层完成TCP或UDP头的封装后转交至网络层[12]。由该层判断数据包的目的地址,若非当前节点则查询路由,将数据包交至网络设备层。该层查询ARP,将数据包转至指定接口,由该口将包发送至信道。信道通过入队、链路延迟处理、TTL检验及出队等完成数据包的发送。若出现队列已满或TTL生命期结束,则丢弃该包。当数据包经过信道传输到达目的节点后,再由网络设备层开始逐层上传,最后通过端口找到API套接字,并通过该套接字将包交由应用模块处理。综上所述,NS3的数据包传输过程与物理网络类似,其仿真可信度较高。
NS3从拓扑建立开始,定义所选模型,而后通过设置参数、赋予地址来配置模型并执行代码。仿真生成的Pcap包文件可采用trace格式跟踪输出,最后用Wireshark等工具跟踪结果并分析数据,或由Net Anim来实现可视化输出。其代码创建过程如图3所示。
图3 NS3代码体系结构
使用NS3进行网络仿真时,一般需要4步:
(1)选择或开发相应模块;
(2)编写仿真脚本(C++或Python)。包括:生成节点(如网卡、应用程序和协议栈等);安装网络设备(如CSMA、WiFi);安装协议(一般TCP/IP及应用层协议);其他配置(如节点移动或能量管理等);
(3)启动仿真器;
(4)仿真结果分析(包括输出网络场景和数据图像等)。
NS3有两类跟踪:一是用Logging系统直接将执行过程显示在命令行,有助于调试仿真脚本;另外常用Tracing系统将采集的数据直接存于一文件中,以便后期分析处理。
Logging系统从低到高可分7个等级,高的包含低的消息。通过.cc文件对程序添加记录,通过环境变量修改系统等级,随后终端运行。而Tracing系统则存储大量信息,它包含3个基本概念:跟踪源(Tracing Sources)、跟踪宿(Tracing Sink)以及两者的连接机制。该系统基于回调函数收集统计信息,当某跟踪源产生一新事件,可通过回调了解其内部正在发生的模拟情形及设备运行状况。
4.2.1TCP类及其相互作用
TCP类在网络模型中与IPv4/6协议一起驻留,实现彼此通信的多个类与网络层交互,并向应用层提供可靠的数据传送。下面列出NS3中关于TCP实现的主要类别以及它们之间的相互作用,如图4所示[13]。
TcpSocket:这个抽象类包含TCP套接字的基本属性。
TcpSocketBase:此类为应用程序层提供了关键的TCP特性和一个套接字接口。它从TcpSocket继承,用作所有TCP变体的基类。
Tcp Header:此类定义了TCP段的头。
TcpTxBuffer:此类提供一个缓冲区,发送方在发送和确认之前,缓冲从应用程序接收的所有数据。
TcpRxBuffer:该类实现一个缓冲区,用于接收从网络层收到的数据,然后将其传递给应用程序。
TcpL4Protocol:是TCP套接字和网络层接口类,负责向网络层收发数据包,并且负责传入数据校验和验证。
除了上述主类外,NS3还包含了基于继承TCP套接字的子类,可以实现多种变体。如Tcp Tahoe、Tcp Reno和Tcp NewReno及Tcp Westwood等。
4.2.2全局变量
现有的变体都是基于继承TCP套接字的同一类实现的,它包括以下各全局变量,用以实现拥塞控制算法及其带宽估计等。
m_cWnd是一uint32_t型变量,用于表示拥塞窗口。发送方用来确定可进入网络而不致链路过载的字节数。当发生丢包时,使用m_cWnd来估计带宽。
m_ssThresh也是uint32_t型变量,用于标记慢启动结束门限阈值,其值取决于丢包时的链路带宽估计。
m_initialcWnd是指定m_cWnd初值的uint32_t变量。
m_inFastRec是指示快速恢复开始和结束的布尔型变量。
m_prevAckNo类型为SequenceNumber32,用以保存最后接收到的ACK号。
m_accountedFor为uint32_t型变量,其在丢包时可跟踪DUP ACK段数量,并在估计带宽时使用。
m_lastAck是前一ACK到达时间的双精度浮点型变量。
m_currentBW表示当前带宽估计的双精度浮点型变量。
m_minRtt是指定的最小RTT的时间类型变量。
m_lastBW是双精度浮点型变量,其在通过Tustin滤波器之后,保存最后所估计的带宽值。
m_lastSampleBW是双精度浮点型变量,其保存测量带宽的最后一个样本值。
m_ackedSegments是整型变量,它保存当前RTT期间已确认的段的总数。
m_IsCount是一个布尔型变量,用于指示m_acked段计数过程的开始。
m_bwEstimateEvent是指定带宽采样事件类型EventId的变量。
图4 TCP类图一览
4.2.3算法实现
NS3支持多种类型的TCP算法的实现,这些实现继承自src/network目录下的一些通用类,这样用户就能以最少的代价实现自己的算法。这里首先描述聚合的概念,在NS2中已广泛使用的继承和多态被用来扩展协议模型,如RenoTcpAgent就是通过重写方法继承自TcpAgent的,这促成了NS3的对象聚合系统。然后再介绍两个重要的抽象基类:class TcpSocket,这在src/internet/model/tcp-socket. {cc,h}中定义。这个类主要用来管理能被不同算法重用的一些属性。例如,属性InitialCwnd可用于从TcpSocket类派生的任何实现。而class TcpSocketFactory则由协议的第4层实例使用,以创建正确的TCP套接字类型。
目前对于NS3,有四种方式实现TCP:NS3自带的TCP实现;支持直接代码执行(DCE);支持网络模拟通讯座(NSC);虚拟机与NS3的组合。鉴于复杂程度,文中只介绍第一种实现方式。NS3自带的TCP模型具有建立连接和关闭逻辑的双向TCP功能。支持多种拥塞控制算法,如NewReno、Westwood、Hybla和HighSpeed等,暂不支持多路TCP和TCP SACK算法。
以前的拥塞控制被认为是通过继承父类的独立的TCP,每个拥塞控制(如NewReno)是TcpSocketBase的子类,需修改一些继承的方法。但从NS3.25开始,TCP模块经过了重新设计,其中拥塞避免、快速重传与恢复算法已修改,并完成了在TcpSocketBase内的合并。架构被重做,以避免之前的继承,让每个拥塞控制都具有一个单独的类,并且建立一个接口,用以在TcpSocketBase和拥塞模块之间交换数据,可以为创建和执行自动化测试创造更好的环境。一般在src/applications/helper和src/network/helper中定义帮助函数,通过NS3使用套接字,在应用层设置TCP的使用,具体步骤及方法如下:
(1)创建TCP接收器。
// Create a packet sink on the star "hub" to receive these packets
uint16_t port=50000;
AddresssinkLocalAddress(InetSocketAddress
(Ipv4Address::GetAny (), port));
PacketSinkHelper sinkHelper("ns3::TcpSocketFactory", sinkLocalAddress);
ApplicationContainer sinkApp=sinkHelper.Install
(serverNode);
sinkApp.Start (Seconds (1.0));
sinkApp.Stop (Seconds (10.0));
类似的,以下代码片段将OnOffApplication流量源配置为使用TCP:
//Create the OnOff applications to send TCP to the server
OnOffHelper clientHelper ("ns3::TcpSocketFactory", Address ());
这里已指定了抽象基类TcpSocketFactory的TypeId。NS3如何判断脚本需要的是它自带的TCP模型还是其他来源,当网络堆栈添加到节点时,聚合到该节点的默认TCP实现是NS3TCP。因此,默认情况下,在使用NS3helper API时,聚合到具有Internet堆栈的节点的TCP是NS3TCP。
(2)配置TCP行为。
通过NS3属性,系统导出参数,记录在TcpSocket类的Doxygen中,例如最大段大小是可设置的属性。要在创建任何Internet堆栈相关对象之前设置默认套接字的类型,可在仿真程序的顶部放置以下语句:
Config::SetDefault ("ns3::TcpL4Protocol::SocketType",StringValue ("ns3::TcpNewReno"));
(3)绑定Socket套接字。
对于用户,希望有一个指向实际套接字的指针,需要用函数Bind()来绑定套接字。设置选项等可在每个套接字的基础上完成,TCP的套接字可通过使用Socket::CreateSocket()的方法进行创建。传递给CreateSocket()的TypeId必须是类型ns3::SocketFactory。因此需要通过与底层TcpL4Protocol对象相关联的属性来完成底层套接字类型的配置,也可通过属性配置系统完成。在下面的示例中,访问节点容器“n0n1”以获取第零个元素,并在此节点上创建绑定套接字。
// Create and bind the socket...
TypeId tid =TypeId::LookupByName("ns3::TcpNewReno");
Config::Set ("/NodeList/*/$ns3::TcpL4Protocol/SocketType", TypeIdValue (tid));
Ptr
(n0n1.Get(0), TcpSocketFactory::GetTypeId ( ));
上面节点号的“*”通配符被传递给属性配置系统,以后所有节点上的套接字都被设置为NewReno,而不仅仅是节点'n0n1.Get(0)'了。如果想要将其限制为仅指定的节点,则必须执行以下操作:
// Create and bind the socket...
TypeId tid=TypeId::LookupByName
("ns3::TcpNewReno");
std::stringstream nodeId;
nodeId<
std::string specificNode="/NodeList/"+nodeId.str()+"/$ns3::TcpL4Protocol/SocketType";
Config::Set (specificNode, TypeIdValue (tid));
Ptr
(n0n1.Get(0),TcpSocketFactory::GetTypeId ());
一旦创建了TCP套接字,就需要遵循传统的套接字逻辑以及connect()和send()(对于客户端)或bind()、listen()和accept()(对于服务器端)。有关在NS3中使用套接字的信息,可以参阅Sockets API相关文献[14]。
若要对已存在算法进行验证,通常TCP测试从一个名为TcpGeneralTest的类继承,该类提供了涉及TCP对象测试场景的操作设置,其位于src/internet/test目录下。有关编写新测试的更多信息,请参阅有关TCP测试文献。一些测试存于src/test/ns3tcp目录,其具有Internet模块之外的依赖关系。更多信息可在NS3官网的Wiki页面上找到。
NS3具有更好的开发环境(包括COM类接口聚合与查询、自动内存管理及对象回调等核心功能),便于仿真全新的复杂模型。它克服了NS2的诸多缺陷和明显弱点,越来越多的网络仿真功能现已逐渐推行并植入到NS3中,使之适用于更多场景。文中结合项目开发经验,从不同角度和深度对新一代网络仿真器NS3作了较为全面的阐述与介绍,对其体系结构和主要功能模块进行了深入剖析,尤其对NS3的TCP实现机制作了重点探究。为NS3的使用及研发者提供了较好的理论指导意义和应用参考价值,为网络相关课题的后续研究及深入学习提供了强有力的支持。目前,NS3的一些新功能正在积极开发之中,如节点多接口处理、IP寻址以及更多的因特网协议设计,包括更详细的802.11模型等都非常值得期待,NS3最终必将会取代NS2。
[1] 梁军学,林昭文,马 严.未来互联网试验平台[J].计算机学报,2013,36(7):1364-1374.
[2] 袁 晓,蔡志平,刘书昊,等.大规模网络仿真软件及其仿真技术[J].计算机技术与发展,2014,24(7):9-12.
[3] 马浩然.基于NS3的分布式消息系统Kafka的仿真实现[J].软件,2015,36(1):94-99.
[4] FONT J L,IIGO P,DOMNGUEZ M,et al.Analysis of source code metrics from ns-2and ns-3network simulators[J].Simulation Modelling Practice & Theory,2011,19(5):1330-1346.
[5] RILEY G F,HENDERSON T R.Modeling & tools for network simulation[M].Berlin:Springer,2010.
[6] DAS B,SUBUDHI B,PATI B B. Cooperative formation control of autonomous underwater vehicles:an overview[J].International Journal of Automation and Computing,2016,13(3):199-225.
[7] RACHNA C,SHWETA S,RITA K,et al.A study of comparison of network simulator-3and network simulator-2[J].International Journal of Computer Science and Information Technologies,2012,3(1):3085-3092.
[8] 闵圣天,曾文序,李满荣,等.基于NS3的网络协议分析与模拟[J].福建电脑,2014,30(2):99-100.
[9] 张登银,张保峰.新型网络模拟器NS-3研究[J].计算机技术与发展,2009,19(11):80-84.
[10] 栾 俊,李太浩.基于NS-3的WiFi场景仿真[J].农业网络信息,2012(1):18-20.
[11] 李广荣.基于NS-3的虚实网络结合系统的设计与实现[D].哈尔滨:哈尔滨工业大学,2015.
[12] 杨鸣亮,张嘉毅,孙 振,等.关于NS3中GRE仿真的研究[J].电子测量技术,2011,34(1):22-26.
[13] GANGADHAR S,NGUYEN T A N,UMAPATHI G,et al.TCP Westwood(+) protocol implementation in ns-3[C]//Proceedings of the6th international ICST conference on simulation tools and techniques.Brussels, Belgium:ICST,2013:167-175.
[14] 袁鹏飞,郑 涛,杨李冬,等.一种基于CAPPROBE带宽估计的TCP Westwood算法[J].厦门大学学报:自然科学版,2014,53(4):469-476.