开源网络模拟器ns-3(架构与实践)_第1页
开源网络模拟器ns-3(架构与实践)_第2页
开源网络模拟器ns-3(架构与实践)_第3页
开源网络模拟器ns-3(架构与实践)_第4页
开源网络模拟器ns-3(架构与实践)_第5页
已阅读5页,还剩186页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

开源网络模拟器ns-3架构与实践目录\h第1篇全局之观\h第1章概述\h1.1什么是ns-3\h1.2Hello,world\h1.3ns-3社区\h1.4ns-3简史\h1.5写作习惯\h第2章通览\h2.1下载、编译与运行\h2.2源代码的目录结构\h2.3再谈编译:“./wafconfigure”命令\h2.4初识脚本\h2.4.1点对点有线网络:first脚本\h2.4.2Wi-Fi无线网络:third脚本\h2.4.3Python脚本\h2.5再探脚本:常用技术解析\h2.5.1脚本助理:助手类\h2.5.2参数输入:属性变量\h2.5.3数据输出:trace变量\h2.5.4行为控制:命令行\h2.5.5计划事件:Schedule函数\h2.5.6回调函数:Callback类\h2.5.7辅助信息:Log系统\h2.6网络是如何模拟的\h2.7信息入口:结点类\h2.8ns-3与编程之夏\h第2篇设计之道\h第3章对象模型架构\h第4章Ptr智能指针\h4.1设计原理\h4.2使用实例\h4.3适用范围\h第5章对象模型的基石:元信息\h5.1什么是元信息\h5.2元信息存储:IidManager类\h5.3元信息管理接口:TypeId类\h5.4TypeId的使用\h第6章Object类\h6.1对象聚合\h6.2Object的创建与获取\h第7章ObjectBase类\h7.1属性配置\h7.1.1创建属性\h7.1.2属性类型\h7.1.3ConfigStore\h7.1.4全局属性\h7.2trace变量\h7.2.1创建trace\h7.2.2trace类型\h第3篇模拟之路\h第8章应用层\h8.1架构与脚本\h8.2内置应用:Application\h8.2.1分组产生器\h8.2.2Internet应用\h8.2.3应用层trace\h8.3自定义应用层协议\h8.3.1Socket原理与使用\h8.3.2Application类\h第9章传输层\h9.1架构与脚本\h9.2TCP\h9.2.1属性与trace\h9.2.2TCP分组头结构\h9.2.3TCP连接建立与关闭\h9.2.4窗口管理\h9.2.5拥塞控制\h9.3UDP\h9.3.1属性与trace\h9.3.2UDP分组头结构\h9.3.3广播与多播\h9.4传输层协议开发\h9.5示例脚本\h9.6非发行版的传输层协议\h第10章网络层\h10.1架构与脚本\h10.2脚本配置与trace\h10.3IP分组头结构\h10.4IP地址分配\h10.4.1手动分配\h10.4.2自动分配\h10.5路由协议\h10.5.1列表路由\h10.5.2打印路由表\h10.5.3静态路由\h10.5.4全局路由\h10.6网络层协议开发\h10.6.1隧道协议\h10.6.2路由协议\h第11章链路与物理层\h11.1有线网络:PPP与CSMA\h11.1.1架构与脚本\h11.1.2属性与trace\h11.1.3队列与错误模型\h11.2无线局域网:Wi-Fi\h11.2.1架构与脚本\h11.2.2属性与trace\h11.3蜂窝网:LTE\h11.3.1网络架构\h11.3.2示例脚本1:无线接入网\h11.3.3示例脚本2:核心网\h11.3.4属性与trace\h11.4其他无线网络模块简介\h第12章数据收集与统计\h12.1一个例子\h12.2数据收集\h12.3数据统计\h12.4局限\h第13章与物理网络交互\h13.1TapBridge:模拟网络+物理结点\h13.2FdNetDevice:模拟结点+物理网络\h第14章网络分组Packet类\h14.1分组结构\h14.2分组创建\h14.3分组头与分组尾\h14.4分段与重组\h14.5标签:Tag类\h附录\h附录A网络地址\h附录B第三方ns-3项目第1篇全局之观题西林壁苏轼(宋)横看成岭侧成峰,远近高低各不同。不识庐山真面目,只缘身在此山中。第1章概述1.1什么是ns-3ns-3(networksimulator)是一款由C++语言编写的开源项目,主要运行平台是GUN/Linux(如CentOS、Ubuntu、Fedora等)。虽然Windows用户也可使用Cygwin或VisualStudio运行ns-3,但不能使用某些功能(如与物理网络交互)。ns-3同时也是一款自由软件。任何组织和个人均可免费下载、使用和修改ns-3源代码。ns-3主要用于模拟计算机网络。它可以在一台计算机上模拟物理世界中各种类型和规模的网络结构。ns-3并没有一个图形用户界面。要使用其进行网络模拟,用户需要经过下载源代码、编译源代码、编写模拟脚本和运行模拟脚本4个基本步骤(在ns-3中,把用于构建虚拟网络的程序叫作模拟脚本)。从这点上讲,ns-3其实更像是一个程序库。它提供各种用于网络模拟的应用程序接口(ApplicationProgrammingInterface,API)。用户在模拟脚本中调用这些API来构建自己的虚拟网络结构。目前ns-3的模拟脚本可以支持C++和Python两种程序语言。那么ns-3是如何模拟出一个物理网络的呢?这就要从网络的组成说起。大体来讲,一个计算机网络可以分为以下两个部分。●由若干结点和连接这些结点的信道所组成的网络拓扑。●运行在结点和信道中的网络协议。首先,在ns-3模拟的虚拟网络中,网络拓扑中的结点和信道被抽象成各种C++类。结点和信道的连接操作被抽象成不同C++对象之间的关联。通过这种抽象,可以在ns-3程序中模拟出各种类型的网络拓扑,如有线网络中的点对点协议(PointtoPointProtocol,PPP)与总线网络,无线网络中的无线局域网(基于IEEE802.11系列标准)与长期演进技术(Long-TermEvolution,LTE)等。这些网络拓扑可以包含几十、几百甚至上千个网络结点。其次,对于网络协议的模拟,ns-3使用了一种名叫离散事件(DiscreteEvent)的模拟技术。简单地说,这种技术就是把物理世界中一个连续的过程抽象成了虚拟世界中的一系列离散的事件。这种技术使得ns-3可以非常逼真地模拟物理世界中的各种网络协议,如应用层的各种分组产生器、传输层的TCP和UDP、网络层的IPv4和IPv6协议、链路层和物理层的PPP、IEEE802.11a/b/g/n和LTE协议等(见图1-1)。图1-1ns-3支持的网络协议只能模拟物理网络还远远不够。为了帮助用户更加便捷地进行网络模拟,ns-3还提供了一系列的辅助功能。例如,trace生成功能使用户可以直接通过第三方软件(wireshark、tcpdump)对ns-3产生的数据进行分析。移动模块可以为结点自动分配起始位置和移动轨迹。此外,ns-3所构建的虚拟网络还可以与物理网络环境高度融合。一方面,ns-3中的虚拟结点可以利用物理网络收发数据;另一方面,物理结点也可利用ns-3构建的虚拟信道收发数据包。这样,用户就可以直接使用ns-3模拟大规模网络,进而对真实的网络协议代码进行测试,达到节省开发成本的目的。至此可以看出,ns-3主要用于模拟网络拓扑和运行其中的网络协议。其关注的是协议行为。其他诸如结点内部的硬件延迟、CPU使用率等指标是不属于ns-3能力范畴的。自2008年7月首个版本(ns-3.1)发布以来,ns-3一直保持着每年2~3个版本的发布速度\h[1]。截至本书完稿时,ns-3已发布至第29版本(ns-3.29)。目前,ns-3已被广泛应用在第五代移动通信(5G)、物联网、软件定义网络、数据中心等计算机网络的前沿研究领域。1.2Hello,world在屏幕上输出“Hello,world”一直是各种编程语言的标准示例程序。ns-3也不例外。它自带了一个名为“hello-simulator”的C++模拟脚本,用以向屏幕上输出“HelloSimulator”字样。先来看一下这个脚本的实际运行效果。下面的第一行代码是在Linux命令行中使用waf命令执行hello-simulator脚本(waf是ns-3项目中最常用的命令。它负责C++源代码编译和脚本运行)。第二行代码是执行结果,即在屏幕中显示“HelloSimulator”。hello-simulator脚本的C++源代码如下。可以看出,ns-3脚本和其他的C++程序一样,都有一个作为起始入口main函数。这里的NS_LOG_UNCOND是一个ns-3中的宏定义。它的作用就是打印括号中的字符串。此外,整个ns-3项目的源代码都受ns3名字空间保护。这样可以将ns-3项目与非ns-3项目隔离,便于与其他项目整合。这个例子中的main函数只有一行代码。在真正的模拟脚本中,构建网络拓扑、配置参数、数据生成等操作均需要在main函数中完成。1.3ns-3社区在自由软件领域,社区(community)的重要性不言而喻。一个成熟的社区环境是自由软件健康发展的必要保障。同时,社区也是开发者、用户工作、学习和交流的平台。ns-3组织并未对其社区做出任何官方定义。为了便于读者理解,编者结合ns-3文档和多年的开发经验,把ns-3社区划分为核心社区和延展社区两部分(见图1-2)。图1-2ns-3社区组成核心社区包括围绕ns-3项目本身的一系列参与方,如ns-3用户、开发者和维护者等。延展社区是ns-3项目的外延,包括与ns-3紧密相关的学术会议、面向学生群体的实习项目和其他一些ns-3的关联项目。延展社区对ns-3的普及和可持续发展至关重要。ns-3联盟(ns-3Consortium)是由华盛顿大学和法国国家信息与自动化研究所(INRIA)于2012年10月创立的一个组织机构。它负责社区的统一规划、组织协调、资金筹集等一系列长期性战略问题,为ns-3社区的发展提供基础支持。ns-3是一个由不同模块(model)所组成的项目。每一个模块负责实现一个重要网络功能。例如,LTE模块负责实现LTE网络协议栈。Internet模块负责实现TCP/IP协议栈。Wi-Fi模块负责实现IEEE802.11系列协议。在核心社区中,每一个模块都有至少一名维护者。该模块的未来扩展、补丁发布、文档更新及邮件列表答疑等事项均由该维护者负责。这些维护者大多是该模块的主要开发人员。文档系统和邮件列表系统则是用户、开发者和维护者进行问题讨论、技术交流的主要平台。首先来看一看ns-3文档系统。它主要包含以下几部分内容[1]。●ns-3tutorial:ns-3的基本概念和使用方法。适合地毯式阅读以了解基本概念。●ns-3manual:深入讲解ns-3的架构、设计原理和ns-3核心模块中的主要技术细节。适合想进一步了解ns-3原理的读者。●Modellibrary:由各个模块的开发者编写而成的文档,分别讲解每个模块的背景知识、设计架构以及使用方法。适合在编写某一模块的脚本或二次开发时使用。●Doxygen:从源代码中生成的在线文档。用户可以在Doxygen网页中浏览源代码、查询ns-3中C++类、函数和变量的定义,了解类与类之间的继承关系等程序细节信息。Doxygen是一个很好的工具网站,在编写模拟脚本和进行扩展开发的过程中非常有用。●ns-3wiki主页:安装指南、代码提交流程、版本发布等辅助性信息[2]。ns-3wiki主页同时也维护着一些没有整合入主程序的模块与补丁。读者可以从中查找自己所需的相关代码,避免重复工作。如果在这些文档中无法找到解决方案,则读者可以尝试在邮件列表发问。ns-3社区成员遍布世界各地。人们主要通过邮件列表沟通交流。这也是很多自由软件项目所常用的方式。ns-3社区中使用频率最高的邮件列表有两个:Googlens-3用户组和ns-3开发者邮件列表[3]。前者是用户就ns-3安装、脚本编写、模块原理等使用问题进行咨询、讨论的平台。用户组里经常有各个模块的维护者对相关问题进行答疑。后者则是开发者交流意见的主战场。开发者们在此会探讨一些新功能的实现、新版本发布、bug修改等技术问题。这里需要特别提醒读者的是,在邮件列表发送邮件之前一定要先做好自己的功课,确保查阅文档、wiki主页和搜索邮件列表历史都没有得到满意的答案之后再发问。这也是每一个自由软件人的基本素质体现。再来看看ns-3延展社区。首先是学术会议。与ns-3直接相关的学术会议主要有两个:ns-3研讨会(ns-3workshop)和EAISimutools。这两个会议中的论文往往涉及一些ns-3的新功能。此外,ns-3研讨会每年还会提供培训课程[4]。这些课程涉及范围很广,从基本概念到新扩展,且往往配有视频和幻灯片,与ns-3文档形成很好的互补。其次是夏季项目。ns-3的夏季项目主要面向擅长程序设计的学生群体,意在培养未来潜在的开发者。ns-3夏季项目主要有以下3个。●Google编程之夏(GoogleSummerofCode):Google公司主办的年度带薪程序设计项目[5]。●欧洲航天局太空编程之夏(SummerofCodeinSpace):欧洲航天局举办的面向航天航空领域的年度带薪程序设计项目[6]。●夏季指导项目(MentoredSummerProjects):ns-3组织自己推出的不带薪指导项目。主要针对没有入选前两个编程之夏项目,但仍有意愿为ns-3社区贡献代码的学生[7]。参与这3个项目的学生会在ns-3资深开发人员的指导下完成指定课题。今天很多ns-3模块最初就是从这些夏季编程项目衍生而来的。编程之夏对ns-3的后续发展帮助很大。对此感兴趣的朋友可以跟踪编者维护的“编程之夏”微信公众号(微信号:codesummer,二维码见本书勒口。)。编者在2.9节还会对这一项目做更为详细的介绍。最后是关联项目。ns-3关联项目分为两类:由ns-3组织维护和由外部机构维护。由ns-3组织维护的关联项目有以下几个。●DirectCodeExecution:一个能够让Linux协议直接运行在ns-3中的软件框架。●Netanim:一款能够以动画显示模拟过程的软件。●Bake:一个方便用户一次性安装多个ns-3关联项目的工具程序。此外,也有很多ns-3项目由外部机构开发[8]。比较著名的有西班牙CTTC研究所开发的LTE模拟器[9]和芬兰MagisterSolution公司开发的卫星通信模拟器SNS3[10]等。由于ns-3主程序库有严格的测试程序,因此这些项目目前尚未或仅部分并入ns-3发行版。但其均与ns-3社区保持有密切联系。附录B列举了几个开发状态比较活跃、且符合目前计算机网络研究热点的第三方ns-3项目。1.4ns-3简史ns系列共有3款模拟器:ns-1、ns-2和ns-3。ns-2向前兼容ns-1。而ns-3则是一款全新的模拟器,不向前兼容。尽管如此,ns-3和其前身依然有着深厚的历史渊源。ns-3的历史最早可追溯至1988年发布的REAL模拟器[11]。该模拟器由当时还在加州大学伯克利分校攻读博士的SrinivasanKeshav编写。20世纪90年代中期,REAL被拓展成为ns-1。1996年,ns-1的tcl代码被OTcl语言代替,进而演化成ns-2模拟器[12]。此后的10年间,计算机网络技术突飞猛进,ns-2也迎来了黄金的发展期。先后有美国国防高级研究计划局(DARPA)、南加州大学信息科学研究所(USC/ISI)等多所大型研究机构参与到ns-2项目的开发中来。但是,随着时间的推移,ns-2软件架构中的一些问题也逐渐暴露出来,如其缺乏对真实数据包和多接口结点的支持、不友好的网络结点IP地址设计、C++和OTcl分离式对象模型的性能问题和整个ns-2项目分支过多导致的碎片化等。2005年2月22日,华盛顿大学的TomHenderson(本书序的作者)在ns-2开发者邮件列表中发表了标题为“newns-2developmentdiscussiononthislist”的帖子[13],意在讨论ns-2的未来发展。讨论中Tom所列出的一些重要议题对日后ns-3的定位产生了深远影响。这些议题包含以下几个重要条目。●重新审视网络结点的软件架构。●更好地与其他开源软件(如Ethereal,即现在的Wiredshark)整合。●更好的可扩展性,如重新审视ns-2的分离式对象模型。●IPv6支持。●让用户在纯模拟、虚拟实验床和物理网络之间灵活迁移。●更容易让学生使用。今天,这些议题很多已经在ns-3中实现。此后,从2005年2月至2006年7月,关于ns-3项目的前期讨论就一直在ns-2邮件列表进行着。除了Tom,当时还在法国尼斯大学攻读博士学位的MathieuLacage是这些讨论中的另一个关键人物。Mathieu当时正在基于ns-2实现一个802.11模块。因为不满于ns-2的某些架构设计(如网络结点地址),他于2006年1月发布了一个叫作Yans的模拟器。Yans拥有全新设计的内核,完全用C++实现,并放弃了ns-2中分离式的对象模型。Yans的设计理念和Tom早先提出的议题不谋而合。两股力量聚合在一起。2006年7月3日,Tom在ns-2邮件列表宣布ns-3项目正式成立。ns-3重用了部分Yans的代码。Mathieu成为ns-3早期开发者之一。有趣的是,ns-3项目正式发布后立即引来了广泛的讨论。这些讨论既包括使用Java还是C++、是否向前兼容ns-2等这类基础性问题,也包括文档系统生成工具、脚本语言选择等全新功能的讨论。这些讨论促进了ns-3项目的进一步完善。2008年7月,ns-3首个正式版本发布(ns-3.1)。到2018年11月为止,10年时间,ns-3已连续发布29个版本。而ns-2目前则基本处于维护状态,其最新版本至今还停留在2011年发布的ns-2.35。今天,ns-2开发者邮件列表继续被ns-3组织所使用着,只不过已鲜有与ns-2相关的讨论。1.5写作习惯为了避免阅读时的混淆,这一小节列举了本书在写作用词方面的一些习惯。1.文档英文为了便于读者查阅文档,本书对manual、tutorial和modellibrary三个文档名不进行翻译。2.函数表示函数的表示形式是“函数名()”。函数的形参列表只有在需要特殊说明时才会添加。3.分组名称分组(packet)在不同的协议层有不同的叫法。例如,人们一般习惯把传输层分组称为报文(segment),把网络层分组称为数据报(datagram),把链路层分组称为帧(frame)。为了便于叙述,本书中统一使用分组这个名称。4.默认目录2.4节及以后所有目录、文件的默认根目录均为ns-3主目录(如ns-3.28/)。\h[1]2017年除外。第2章通览2.1下载、编译与运行ns-3项目是以源代码的形式发布的,因此需要先从ns-3官网下载源代码包并在本地进行编译,之后才能运行ns-3脚本。最常见的ns-3源代码包叫作一体包(ns-3-allinone,简称allinone包)。ns-3官方支持的allinone包下载方式有以下3种。●压缩文件方式:直接下载allineone包的tar压缩文件。●版本控制方式:使用分布式版本控制工具mercurial从ns-3仓库中下载。这种方式可以让用户充分利用mercurial等工具的各种版本控制功能,并且可以下载ns-3开发版(ns-3-dev)。如果没有特殊需求,这种方式应该是最便捷的,也是ns-3tutorial中推荐的下载途径。●bake:ns-3自带的源代码包管理工具。bake可以完成从依赖包检查、allinone包下载与编译的一系列操作。为了避免重复,本书只列举mercurial版本控制下载方式的主要流程,使用的操作系统是Ubuntu,选择的是ns-3.28发行版的allinone包。读者可以在ns-3wiki主页\h[1]中找到其他两种方式的详细介绍[2]。1.第一步:下载依赖包ns-3的成功编译需要依赖一些第三方程序,如gcc/g++编译器、mercurial工具、Python等。读者可以在ns-3安装wiki主页中找到不同操作系统所需的依赖包。2.第二步:下载辅助Python脚本这是一些用来协助ns-3下载和编译的Python脚本。这些脚本需要通过mercurial版本控制工具从ns-3的远端服务器(也就是mercurial仓库)中下载。ns-3项目mercurial仓库的地址是/。这里面包含了各种历史发行版和一些开发者的私人代码仓库。3.第三步:下载allinone源代码包使用download.py脚本下载指定的ns-3发行版。下载后的allinone目录如下所示。可以看到,allinone包不仅包含ns-3主项目(ns-3.28/),还包含ns-3源代码包管理工具bake(bake/)、可视化模拟工具NetAnim(netanim/)和Python绑定工具PyBindGen(pybindgen/)这些辅助项目。这种集成多个项目打包发布的方法非常类似于PC行业中的一体机(All-in-OnePC)概念。这也是allinone包名字的由来。除了ns-3.28这种发行版,ns-3还提供开发版的allinone包(download.py在不指定版本号的情况下下载的就是开发版包ns-3-dev)。开发版包含一些正在开发的功能,没有经过完整的测试。除非是准备向ns-3提交代码或必须要使用某个新特性这种特殊情况,建议读者选择更为稳定的发行版本。4.第四步:编译ns-3使用Waf工具来编译源代码。但Waf只能编译ns-3主项目。对于第一次下载ns-3源代码包的用户,一个比较好的方法是运行ns-3-allinone目录下的build.py脚本。这样可以一次性编译ns-3和其他辅助项目。为了后续演示需要,这里添加了一个可选参数“enable-examples”。这个参数可以让build.py同时编译ns-3自带的网络模拟示例脚本。编译完成后的信息显示如下。“Modulesbuilt”部分列出了已完成编译的模块。“Modulesnotbuilt”部分是未编译的模块。这些模块一般需要第三方库的支持,可以在后续按需添加。如果编译出错,则有可能是缺失依赖包。请读者返回第一步检查是否有依赖包没有安装。5.第五步:运行运行ns-3脚本需要用到Waf工具。前3个步骤都是在ns-3-allinone目录下执行命令。现在需要进入ns-3.28目录,然后在该目录下运行第1章中提到的hello-simulator的示例脚本。如果示例脚本在屏幕上打印出了“HelloSimulator”字符串,则表明安装成功。如果出错,则检查一下第三步中是否添加了“--enable-examples”参数。如果用户改动了现有脚本或编写了一个新的脚本,则需要对ns-3项目重新编译。方法也很简单,直接在ns-3.28目录下运行“./waf”命令编译即可。实际上,waf是ns-3运行脚本、重新编译时最常用到的命令。biuld.py一般只在第一次下载ns-3代码库时使用。鉴于waf命令的普遍性,编者结合ns-3文档与自身使用经验列举了两个使用小技巧,供读者参考。小技巧1:在ns-3.28的其他目录中运行waf。waf命令的一个缺陷是只能在ns-3.28目录下执行。假设用户目前正在“ns-3.28/src/internet/model/”目录下查看TCP源代码,想要运行“ns-3.28/examples/tcp/tcp-bulk-send.cc”脚本,则需要执行下面的代码。这样很不方便。ns-3tutorial使用了shell函数来简化这个过程。“hgroot”表示ns-3.28的绝对路径。读者也可以把这个函数添加在Linux的“.bashrc”文件中,以便下次继续使用。小技巧2:保存waf输出结果。有时用户需要使用标准C++输出来记录一些信息。保存这些信息到指定文件可以使用如下命令。NS_LOG输出是ns-3中的一系列宏定义,用于打印一些调试信息。关于这部分的内容请参考2.5.7节。最后,为了让用户能够完全验证ns-3是否能够正常工作,ns-3项目自带了test.py脚本(在ns-3.28目录下)。通过运行ns-3中自带的测试用例,test.py可以对ns-3的各个模块进行逐一测试。2.2源代码的目录结构在ns-3主目录中,读者需要关心的子目录有以下3个。●examples目录:ns-3自带的示例脚本。这些脚本是很好的参考资源。除了examples目录,各个模块自己还带有一些示例脚本(src/<模块名>/examples/)。例如,src/lte/examples目录中就包含大量的LTE模拟脚本。●build目录:包含ns-3编译后的目标文件以及可执行文件。ns-3自带的waf命令知道build目录中各种可执行文件的路径。这也是“./waf--run<脚本文件名>”可以直接执行脚本,而不必指定脚本路径的原因\h[2]。例如,下面两行代码的效果是等同的。●src/:ns-3各个模块的源代码。子目录名即为模块名。例如,内核模块的源代码目录是src/core/。实现TCP/IP的internet模块源代码目录是src/internet/。实现LTE协议的lte模块源代码目录是src/lte/。每一个模块目录的结构也基本相同(见表2-1)。例如,src/internet/的子目录结构如下所示。表2-1ns-3模块的子目录结构ns-3各个模块的子目录均遵从表2-1这种结构。2.3再谈编译:“./wafconfigure”命令ns-3-allinone目录下的build.py脚本一般只在首次编译ns-3源代码时使用。后续的编译和运行脚本等操作都需要在ns-3目录下完成(见图2-1),使用的命令是waf。图2-1ns-3编译与运行流程图下面继续以ns-3.28为例讲解图2-1中的编译和运行流程。当首次编译完成后,就可以使用ns-3.28目录下的waf命令运行脚本了(左侧流程)。但是,更多的时候需要编辑脚本、修改源代码或设置配置选项(“./wafconfigure”命令),这时就需要对代码进行重新编译(右侧流程)。在右侧流程中,对于修改源代码的情况,可以直接使用最后一步中的“./waf--run<脚本名>”命令一起进行编译和运行。而如果想要修改配置选项,或根本就是通过waf命令来第一次编译ns-3项目,则必须首先调用“./wafconfigure”对ns-3项目进行配置,然后再调用waf来编译和运行。下面重点讲一讲配置选项,因为它控制着waf命令的编译行为。ns-3中通常需要用户去修改的配置选项有以下3个(见表2-2)。表2-2wafconfigure选项在默认情况下,“./waf”在编译时不包括示例脚本和测试脚本,采用的编译模式是debug。1.“--enable-examples”配置选项如果用户要运行ns-3自带的示例脚本,就必须在配置时添加enable-examples选项。例如,hello-simulator脚本的源文件在ns-3.28/examples/tutorial目录下。若开启enable-examples选项,则编译完成后waf会在“ns-3.28/build/”目录下生成一个同名可执行文件。这样就可以直接在运行时使用脚本名(如“./waf--runhello-simulator”),而不用指定脚本源文件路径了。对于那些不想在编译时包括如此多的示例脚本(从减少编译时间角度考虑),但又想运行自己编写脚本的用户来说,一个迂回的方法是把新脚本放在ns-3.28/scratch目录中。这个目录默认包含在waf编译范围之内,因此不需要额外的配置选项支持。2.“--enable-tests”配置选项若要用test.py脚本对ns-3进行测试,则需要在配置时开启此选项。3.“--build-profile”配置选项ns-3有以下两种编译模式:调试模式(debug)与优化模式(optimized)。默认使用调试模式。用户可以在配置选项中将其切换为优化模式。对于普通用户来说,调试和优化模式最直观的区别在于输出信息的不同。ns-3中大量使用的NS_LOG等输出语句(2.5.7节)只有在调试模式下才会起作用。例如,hello-simulator脚本中的“NS_LOG_UNCOND("HelloSimulator");”语句只有在调试模式时才会在屏幕上打印信息。总体来说,调试模式适用于调试脚本或开发新协议时使用。优化模式适用于大量测试时开启,以节省开销。当前的编译模式可以通过“./waf--check-profile”命令得到。其实,定义配置选项是ns-3编译的必备步骤。这点从build.py的内部实现就可以看出。2.4初识脚本本节以后所有目录、文件的默认根目录均为ns-3.28/。2.4.1点对点有线网络:first脚本本节选取了ns-3官方提供的一个名为first的C++模拟脚本(examples/tutorial/first.cc)。这个脚本创建了一个包含两个结点的有线网络(见图2-2)。其链路层使用点对点协议(Point-To-PointProtocol,PPP)传输分组。为方便读者理解,编者在脚本代码中增添了一些注释,并对代码格式做了微调。first脚本也是本书会经常用到的一个示例程序。图2-2first模拟脚本网络拓扑示意图一个完整的ns-3脚本可以按照编写顺序分为以下几个部分。1.头文件ns-3是一个由不同模块组成的程序库。模拟脚本通过调用各个模块提供的API进行网络模拟。而每个模块的API都被统一存放在“<模块名>-module.h”中。这样在添加头文件时,只需要知道所需模块名即可。读者可以在build/ns3目录下(编译后)找到这些头文件。Core模块和network模块头文件是所有脚本必须包括的。它们分别定义了ns-3的核心功能(如模拟事件、事件调度等)和基本网络组件(如网络结点、分组和地址等)。Internet和application模块尽管不是必需的,但也是大部分脚本经常会用到的模块。其中Internet模块定义了TCP/IP协议栈。Application模块定义了应用层的分组收发模型(如贪婪模型、ON/OFF模型等)。如果脚本中使用非ns-3库的函数,则其头文件也需要在这一步中添加。2.名字空间整个ns-3源代码都受“ns3”名字空间保护。这样可以将ns-3项目与非ns-3项目隔离,起到很好的保护作用,也便于与其他项目整合。因此每一个脚本的开始都需要声明名字空间。在ns-3中使用标准库函数时需要添加std名字空间,如“std::cout”“std::min()”等。3.NS_LOG_COMPONENT_DEFINE下面这行代码的作用是允许在该脚本中使用Log系统中的宏定义打印辅助信息,如打印调试信息的NS_LOG_DEBUG宏和打印错误信息的NS_LOG_ERROR宏等。尽管这是一个可选步骤,但编者还是建议在脚本中添加这一行代码。因为这些辅助信息无论对调试代码和了解模拟流程都非常有用。2.5.7节将会进一步讲解Log系统的使用方法。4.main()函数中的准备工作从这一步开始,后续的操作都需要在main()函数中完成。不过在正式开始编辑网络模拟场景之前,还需要做一些准备工作,如下面代码中所示的读取命令行参数,设置最小模拟时间单元、开启Log组件等。这些步骤都是可选的。由于不影响本节的后续阅读,因此暂且跳过这些步骤的解释。读者可以在2.5节看到这些技术的进一步介绍。5.创建网络拓扑有了前面4步的准备工作,现在可以正式开始搭建网络拓扑了。在物理网络中,一个网络拓扑由若干结点和连接这些结点的信道组成。在ns-3中,结点和信道被分别抽象为Node、Channel以及结点中连接信道的网络设备NetDevice类这3个C++类。不同类型的信道对应不同的NetDevice和Channel子类。例如,对于first脚本中的点对点(PPP)信道来说,其对应的网络设备类是PointToPointNetDevice,信道类是PointToPonitChannel。一般来说,NetDevice主要负责实现链路层协议。Channel主要负责实现物理层协议。first脚本中的Node、Channel和NetDevice类的对象关系如图2-3所示。其中箭头代表分组的传输方向。图2-3first脚本C++类示意图(链路与物理层)下面看一看如何在脚本中构建图2-3中的结点结构和网络拓扑。可以看出,一个ns-3网络拓扑的建立经历了以下3个步骤。●创建结点:脚本中通常使用结点容器NodeContainer类来一次性创建多个Node对象。●配置信道属性:注意这里只是配置信道属性,并非真正的创建信道对象。这一步中用到了ns-3中的助手类(PointToPointHelper)和属性这两个重要概念。助手类屏蔽了很多实现细节。它可以帮助用户更为简便地在脚本中创建网络拓扑。而属性本质上就是C++类中的一个变量。这个变量的取值往往影响着网络的性能或行为(如本例中的PPP传输速率和传播延迟)。●创建信道并连接结点:PointToPointHelper::Install()函数内部分别创建了两个PPP网络设备对象(PointToPointNetDevice)和一个PPP信道对象(PointToPonitChannel)。这两个网络设备对象被分别安装在两个结点中,然后又共同连接至同一个信道对象。Install()函数的返回值是一个网络设备容器NetDeviceContainer对象。这个容器包含了为NodeContainer中所有结点所分配的NetDevice对象。例如,nodes.Get(0)结点中的网络设备是devices.Get(0)。尽管只有几行代码,但其背后执行的操作却有很多,如结点、网络设备和信道3个对象之间的关联关系建立,为网络设备分配MAC地址,为信道设置队列模型等。这些复杂的操作全被PointToPointHelper助手类所屏蔽。可以说,助手类非常类似于软件的图形界面,通过屏蔽实现细节,降低编写模拟脚本的复杂度。ns-3的所有模块都为用户提供了丰富的助手类。2.5节将会对助手类、属性等关键概念做更为全面的介绍。到目前为止,用户已经成功构建起了由结点和信道组成的网络拓扑。换句话说,这两个结点可以进行物理层和链路层通信了。但要运行应用程序,还需要安装上层协议栈。6.安装TCP/IP协议族前一步中所说的上层协议,其实指的就是TCP/IP协议族。ns-3中的TCP/IP协议族主要包括传输层的TCP和UDP,网络层的IP(IPv4与IPv6)、ICMP(ICMPv4与ICMPv6)和一系列的路由协议。为结点安装TCP/IP协议栈的助手类是InternetStackHelper。安装了TCP/IP协议栈的结点还不能直接通信,还需要为结点的网络设备分配IP地址。在first脚本中,这部分操作是通过Ipv4AddressHelper(分配IPv4地址)或Ipv6AddressHelper(分配IPv6地址)完成的。在这部分代码中,助手类Ipv4addressHelper以为起始地址、以为网络掩码,分别向两个主机分配了和两个IP地址。添加TCP/IP协议栈后的first脚本网络拓扑结构如图2-4所示。可以看出,ns-3结点中的协议栈采用的是TCP/IP参考模型。IpL4Protocol是传输层UDP和TCP的基类。Ipv4是网络层IPv4协议的基类(IPv6协议的基类是Ipv6)。链路层基类为NetDevice。物理层基类是Channel。为网络设备NetDevice所分配的IPv4地址保存在IPv4接口Ipv4Interface对象中。一个结点可以安装有多个NetDevice(如同时拥有LTE和Wi-Fi网络设备的多模移动终端),连接到多个Channel,且拥有多个IP地址。图2-4first脚本C++类示意图(TCP/IP协议栈)至此,first模拟脚本已经建立了一个由两个结点组成的有线网络。每个结点从物理层到传输层分别安装了PPP和TCP/IP协议族。从现在开始,两个结点可以运行各种基于TCP/IP的应用进行通信了。7.安装应用程序ns-3中的应用是对物理世界中应用程序内部网络通信功能的抽象,即模拟分组的发送及接收行为。ns-3应用层协议对应的C++类是Application。其不同子类定义了不同的分组收发行为。例如,BulkSendApplication使用了贪婪分组发送模型。OnOffApplication使用了ON/OFF分组发送模型。而first模拟脚本选择的是ns-3中一种叫作UdpEcho的应用程序。其示例代码如下。在这段代码中,服务器端助手类UdpEchoServerHelper在结点1中创建了一个服务器端应用echoServer。echoServer自模拟启动后1.0s开始监听并接收9号端口的数据。同样,客户端助手类UdpEchoClientHelper在结点0中创建了一个客户端应用echoClient。echoClient在模拟启动后2.0s向结点1的9号端口发送一个1024B的UDP数据包。echoServer从9号端口接收到数据包后向echoClient返回一个相同大小的UDP数据包。两个应用均在模拟启动后10.0s停止。“MaxPackets”“Interval”“PacketSize”是UdpEchoClient类的3个属性,分别表示最大发送分组个数、分组发送间隔和分组负载字节大小。懂得套接字(socket)编程的读者可能会问:这段代码并没有调用UDP套接字编程的API,那么结点间的UDP连接是如何建立的呢?其实,ns-3的TCP/IP协议族的确提供了Berkeley套接字的主要API。只是这些API的调用都被封装在了两个应用的助手类中。例如,UdpEchoServerHelper实际上调用了UDP的bind()、RecvFrom()和sendTo()来监听、接收和发送UDP分组。UdpEchoClientHelper则通过调用bind()和connect()来建立与服务器端的UDP连接。这样,用户便可将精力放在配置应用参数上,而不必关注具体实现细节。助手类简化脚本的作用再一次得到了体现。与结点和网络设备容器类似,这里的应用也使用了应用容器。例如,serverApps容器保存着结点容器nodes中所有结点上安装的Application对象。在ns-3中,容器可以对同一属性的对象进行批量操作,从而进一步精简模拟脚本。8.数据生成产生数据是网络模拟的一个必备功能,否则人们就无法分析网络性能。属性配置和数据生成,一个输入一个输出,是ns-3脚本中两个非常重要的组成部分。由于是最简单的示例程序,因此,first模拟脚本并未涉及数据生成操作。我们将会在后面的无线网络脚本章节(2.4.2节)介绍这部分内容。9.启动与结束这是所有ns-3模拟脚本的最后一步。简单来说,Run()函数执行之前步骤中定义的所有操作。完成之后,Destroy()函数执行清除操作。ns-3是一个基于离散事件的模拟器。在ns-3中,任何网络行为都是一个事件(如分组的发送或接收操作)。Run()函数的任务就是按时间顺序执行这些事件,直到所有事件都执行完毕为止。例如,Run()会在1.0s执行服务端应用启动事件,在2.0s执行客户端应用发送UDP分组事件。最后,因为C++模拟脚本本质上是一个main()函数,所以程序最后需要返回零来告诉操作系统程序执行成功。现在在ns-3目录下运行first模拟脚本(需要在编译时启动enable-examples选项)。运行结果如下:输出信息正确显示了UdpEcho应用的行为:2s时,结点0()发送了一个大小为1024B的数据包给结点1()。经过0.00369s,结点1成功接收到数据包后返回一个相同大小的回执数据包。为什么是0.00369s?因为这是分组从结点0到结点1的传输延迟和传播延迟的总和\h[3]。根据first脚本的属性配置,PPP信道的传播延迟等于0.002s。结点0的传输延迟计算过程如下(结果精确到小数点后5位)。因此,总的单向延迟是0.00369s。1054是1024B负载、20BIPv4分组头、8BUDP分组头和2BPPP分组头的大小总和。总体来说,ns-3模拟脚本可以分为网络拓扑搭建、结点协议栈安装和应用程序设置三大部分,是对结点(Node类)、信道(Channel类)、网络设备(NetDevice类)和应用(Application)这4个核心抽象概念的操作。此外,助手类、存储容器、属性等技术的使用简化了脚本的编写复杂度,让用户可以专注于定义网络拓扑结构和配置应用参数上,而不必关注实现细节。2.4.2Wi-Fi无线网络:third脚本在本节中,读者将会看到如何使用ns-3脚本搭建一个无线网络。本节使用的例子是一个名为third的C++脚本(examples/tutorial/third.cc)。它模拟了一个包含点对点(PPP)和CSMA有线网络,以及Wi-Fi无线网络的混合场景(见图2-5)。图2-5third模拟脚本网络拓扑示意图为了避免重复,本节重点讲解那些与前一节有线网络脚本不同的步骤。1.头文件相比于first脚本,third脚本的头文件还包括Wi-Fi、移动和CSMA3个模块。2.名字空间请参考点对点有线网络章节。3.NS_LOG_COMPONENT_DEFINE请参考点对点有线网络章节。4.main()函数与first脚本不同,third脚本中定义并使用了一些命令行参数。下面以其中的“nWifi”为例简单地讲解这些参数的使用方法。这段代码中,“nWifi”参数控制着脚本中的无线结点数量。其对应的C++同名变量是非整型数nWifi。nWifi数值可以在waf命令中设置。5.创建网络拓扑third脚本的网络拓扑结构比first脚本略为复杂。它包含了以下3部分:PPP、CSMA网络和Wi-Fi网络。(1)PPP网络请读者参考点对点有线网络章节。(2)CSMA网络PPP和CSMA(CarrierSenseMultipleAccess)都属于有线网络技术。二者的区别在于PPP信道只能连接两个结点,是专属信道。而CSMA信道可以连接多个结点(总线型网络),需要结点竞争信道使用权。CSMA网络拓扑的创建过程与first脚本中的PPP网络拓扑非常相似。这里需要特别注意p2pNodes.Get(1)结点。它是结点容器p2pNodes中的第二个结点,有PointToPointNetDevice和CsmaNetdevice两个网络设备,是一个双模结点。这个结点的处理在路由设置一步中还要再次提及。(3)Wi-Fi网络third脚本的Wi-Fi无线网络由一个接入点(AccessPoint,AP)和nWifi个(默认为3)移动结点组成。其中AP也是一个双模结点。它安装有Wi-Fi和PPP两个网络设备。在介绍该部分脚本代码之前,先来看一看已构建好的AP和移动结点中网络设备和信道部分的C++类示意图(见图2-6)。这有助于读者更好地理解该部分的脚本操作。图2-6third脚本Wi-Fi网络部分C++类示意图在图2-6中,除了WifiNetDevice类(NetDevice的子类),Wi-Fi网络设备还包括链路(WifiMac)层与物理(WifiPhy)层部分。这里的WifiNetDevice只起一个连接上下层协议的桥梁作用,没有什么实质性功能。主要的Wi-Fi协议功能都集中在WifiMac和WifiPhy两个基类和其各种子类中实现。这种结构一方面可以把一个复杂功能模块化,有利于后续开发和维护;另一方面这其实更加符合物理网络的层次划分。这种“NetDevice+Mac+Phy”的3层网络设备结构在ns-3的无线结点中会经常用到。了解了Wi-Fi网络设备的内部构造,理解其脚本中的相关助手类操作就变得容易很多(这种内部架构和脚本操作结合讲解的方式在本书第3篇会大量采用)。按照逻辑关系,下文对third脚本这部分代码的先后顺序做了调整,使其更加规范和容易阅读。1)设置Channel和WifiPhy。这一步需要用到以下两个助手类:YansWifiChannelHelper和YansWifiPhyHelper。它们分别用于配置Wi-Fi网络的Channel和WifiPhy类(实际上是这两个类的子类)。示例代码如下:这两个C++类都有一些重要的参数需要配置。例如,Wi-FiChannel需要配置传播延迟(propagationdelay)和损耗模型(propagationloss)。这两个模型加上最后一步中的移动模型一起决定了一个分组在无线信道中的传播延迟和接收功率。WifiPhy需要配置误码率(errorrate)模型。如果没有特殊需求,这里使用默认配置即可(即调用本例中助手类的Default()函数)。2)设置WifiMac并在结点中安装NetDevice。这一步骤同样需要用到以下两个助手类:WifiMacHelper和WifiHelper。WifiMacHelper用于设置链路层WifiMac类。这里需要设置以下两个重要参数:WifiMac子类和服务集标识符(SSID)。前者决定了这个结点的种类——AP或移动结点。后者决定了一个结点所属的服务集。AP与移动结点的服务集必须一致才能通信。完成Wi-Fi信道和网络设备中各个组件的配置后,就可以使用助手类WifiHelper将Wi-Fi网络设备安装到指定结点中了。在本例中,WifiHelper还设置了WifiRemoteStationManager子类类型。WifiRemoteStationManager主要用于Wi-Fi的速率控制(ratecontrol)。最后,WifiHelper也是决定Wi-Fi协议类型的助手类。默认状态下,WifiHelper安装的协议种类是802.11a。可以通过WifiHelper的SetStandard()函数来选择其他Wi-Fi协议类型。3)设置移动模型移动模型是无线网络中一个必不可少的组成部分。在配置Wi-FiChannel和WifiPhy时,一个分组在Channel中的传播延迟和接收功率的大小是由传播延迟模型、损耗模型和移动模型共同决定的。传播延迟和损耗模型已在前步中配置,而移动模型一般在Wi-Fi网络设备安装至结点后配置。这是因为不同Wi-Fi结点类型需要采用不同的移动模型。ns-3移动模型使用笛卡儿坐标系标识结点位置。其助手类是MobilityHelper。与设置链路层类似,在Wi-Fi网络中设置移动模型时也需要区分AP与移动结点。先来看一看AP结点。AP结点是固定结点。third脚本为其使用的是固定位置移动模型ConstantPositionMobilityModel。这个模型的AP结点二维坐标是(0,0),即坐标原点。再来看一看移动结点。移动结点的移动模型设置分为以下两部分:初始位置分布和后续移动轨迹模型。前者定义了一个移动结点的初始坐标。后者定义了结点的移动路径。third脚本使用的初始位置分布器是GridPositionAllocator。这个分布器按照设置好的行列参数把结点等间距放置在一个二维笛卡儿坐标系中。third脚本使用的移动轨迹模型是RandomWalk2dMobilityModel。这个模型中的结点在一个指定大小的长方形区域内按照随机的速度(步行速度,默认取值范围是[2,4]m/s)和方向移动。图2-7是third脚本中AP结点和移动结点的初始位置(默认为3个移动结点)分布以及结点移动区域(虚线方框)的示意图。图2-7third脚本结点初始位置与移动区域6.安装TCP/IP协议族third脚本为所有结点都安装了TCP/IP协议栈。安装细节请参考点对点有线网络章节。7.安装应用程序请参考点对点有线网络章节。8.设置路由当ns-3的多个有线网络子网间存在通信时,我们就需要在脚本中设置路由协议。例如,前文在创建CSMA网络时曾提到一个p2pNodes.Get(1)结点。这个结点有PointToPointNetDevice和CsmaNetdevice两个网络设备,分别连接PPP网络和CSMA网络。在third脚本的地址分配中,这两个网络属于不同的子网。这就需要连接两个子网的p2pNodes.Get(1)结点具有路由功能,这样才能正确地转发从PPP子网发往CSMA子网的分组。反之亦然。ns-3有线网络最常用的路由协议之一是全局路由。全局路由通过开放式最短路径优先(OSPF)路由算法计算有线网络拓扑中每两个结点的最短路径,并为每个结点生成路由表。对于IPv4协议,全局路由设置一般是通过在脚本中调用助手类Ipv4GlobalRoutingHelper的PopulateRoutingTables()函数完成的。9.数据追踪模拟的重要目的之一是分析网络性能。获取实验数据是分析网络性能的重要前提。ns-3为用户提供了丰富的数据追踪与收集功能。在ns-3模拟脚本中,收集到的数据经常以pcap或ASCII文本的格式保存在指定文件中。这部分代码内容往往放在脚本最后。我们来逐行解读third脚本中的数据收集部分代码。这里的pointTopoint是一个点对点信道PointToPointHelper助手类对象。EnablePcapAll()函数的作用是收集这个信道上所有结点的链路层分组收发记录。记录文件的格式是pcap。“third”是文件名前缀。EnablePcapAll()函数对文件名的命名规则是“前缀名-<结点标号>-<网络设备标号>”(如下所示)。例如,third-0-0.pcap文件保存的是由pointTopoint所创建信道上第一个结点中第一个NetDevice上的分组收发记录,即本例中AP结点中点对点网络设备上的分组通信记录。我们用tcpdump命令打印third-0-0.pcap文件。内容显示如下。可以看到分组的链路类型是PPP。在时间2.008151s时,AP结点的PPP网络设备收到了无线结点发往有线结点的一个UDP分组。下面这行代码使用YansWifiPhyHelper的EnablePcap()函数打印AP结点中Wi-Fi网络设备的物理层分组收发信息。依据之前的命名规则,这个函数会产生一个名为third-0-1.pcap的文件。通过tcpdump可以看到,AP结点首先发送的是信标帧(Beacon)。接下来,AP结点收到了3个移动结点发来的关联请求帧(AssocRequest)。这些帧的SSID值(ns-3-ssid)都是相同的。最后下面这行代码记录了一个有线结点中CSMA网络设备的分组收发信息。其格式与PPP网络设备产生文件差别不大。这里就不再赘述了。除了tcpdump,pcap文件也可以用Wireshark软件打开。此外,ns-3还支持ASCII文本格式的记录文件。例如,下面这三行代码就是上述程序的ASCII版本。产生的文本扩展名是“*.tr”(tr是单词trace的缩写)。下面是AP结点中点对点网络设备上部分ASCII格式的分组收发记录。“+”“-”和“r”分别代表产生、发送和接收。EnablePcapAll()、EnableAsciiAll()、EnablePcap()、EnableAscii()是很多网络设备助手类经常会用到的4个数据产生函数。10.启动与结束请读者参考点对点有线网络章节。最后在ns-3目录下运行third模拟脚本。输出结果如下。2.4.3Python脚本尽管ns-3所有模块均用C++语言编写,但得益于Python绑定生成器PyBindGen[14],也可以使用Python语言来编写ns-3脚本。ns-3的Python脚本可以调用C++中的API,实现与C++脚本几乎相同的功能。这其中的原理其实很简单(对于这部分内容读者只需理解主要过程即可)。首先,ns-3通过GCC-XML和PyGccXml这两个工具扫描每个模块的C++源代码,然后生成一个Python脚本(位于src/<模块名>/bindings目录)。这个脚本记录了C++源代码的类、方法和变量等细节情况。其次,PyBindGen根据这些Python脚本生成C++文件(位于build/src/<模块名>/bindings目录)。这些C++文件就是连接Python脚本与ns-3中C++源代码的桥梁。这一切都是由ns-3自动完成的。除非是改动ns-3源代码,一般情况下用户是不需要自己去执行Python绑定的。由于Python与C++脚本的结构完全相同,为了避免重复,本节重点讲解Python脚本与C++脚本在使用上的差异。1.运行Python脚本2.在Python脚本中调用C++函数在Python脚本中调用C++函数需要指定模块路径。格式为“ns.<模块名>.<C++函数名>”。Python脚本对函数参数的使用与C++脚本相同。ns-3自带的一些Python脚本是这方面很好的参考资料。3.Trace变量目前为止本书还没有介绍过trace变量的概念。不熟悉trace的Python用户可以先阅读2.5节。Python脚本目前还不支持为一个trace变量关联回调函数。一个迂回的方法是在C++代码中添加一个新函数。这个新函数被用来关联trace变量和回调函数,然后对这个新函数进行Python绑定。这样就可以在Python脚本中直接调用这个新函数来绑定trace。这其实就是在脚本中实现助手类的ASCII或Pcaptrace文件创建函数(如EnablePcap()和EnableAscii())。2.5再探脚本:常用技术解析2.5.1脚本助理:助手类ns-3中有一类特殊的C++类。它们的类名都以Helper后缀结束。这就是助手类。助手类的设计初衷是通过屏蔽实现细节来降低脚本编写的复杂度。在前文中,first与third脚本中的助手类完成了从网络拓扑构建、结点协议栈安装、属性配置到数据生成等一系列操作。实际上ns-3脚本中的绝大部分操作都可以通过助手类完成。ns-3每一个模块都有自己的助手类。这些助手类的源代码位于src/<模块名>/helper目录。例如,为结点安装TCP/IP协议栈的InternetStackHelper助手类就在src/internet/helper/internet-stack-helper.h”头文件中定义。表2-3从每个网络协议层中分别选取了3个助手类代表。这些助手类并没有一个统一的基类。本书第3篇讲解模块时将会分别对其进行介绍。表2-3ns-3助手类例子2.5.2参数输入:属性变量1.属性在ns-3中,属性(attribute)是网络模拟中的用户可配置参数。例如,first脚本中点对点(PPP)网络设备的传输速率和信道传播延迟。从程序角度来讲,属性其实就是C++类中的一个变量。例如,上述传输速率和传播延迟就分别是PointToPointNetDevice类和PointToPointChannel类的私有成员变量。由于这些变量的取值往往影响着网络性能和模拟结果,因此需要一种方法让用户在脚本中对其进行配置。这种方法即为属性系统(attributesystem,见图2-8)。属性系统的任务就是把这些内部的私有成员变量变成外部可配置的参数(即属性)。这样,只需要一个脚本就可以模拟多个拓扑相同但配置不同的网络场景。例如,通过设置不同的传输速率和传播延迟可以模拟出高带宽低延迟和低带宽高延迟两种应用场景。图2-8属性系统属性变量和下节将要讲到的trace变量是两个紧密相连功能。它们的后台实现都是基于一套系统,可以将其看作ns-3模拟脚本的输入和输出。2.配置属性ns-3中配置对象属性的办法有很多。编者根据单次可配置属性的数量和新属性值的作用时间范围把它们划分成以下3类(见图2-9)。图2-9属性配置方法分类(1)第一类:助手类、命令行和Config::SetDefault()助手类、命令行和Config名字空间的SetDefault()函数可以一次性对多个对象中的同名属性进行配置。例如,前文中的first脚本就是通过助手类来设置结点中的DataRate和Delay属性。需要注意的是,这3种方法只能在对象创建之前使用。例如,在下面的代码中,每一个结点有两个PPP网络设备,其传输速率分别被设置为5Mbps和2.5Mbps。可以看到,助手类设置传输速率的操作总是在创建网络设备对象之前完成。这种使用顺序上的要求是因为上述3种方法所配置的其实是一个属性的默认值。创建一个对象时,其属性变量就会被初始化为这个默认值。而一旦对象创建完成,这个对象的属性值就不再受默认值所制约。换句话说,属性默认值仅在对象创建时起作用。除了使用顺序,助手类配置属性还需要注意以下两点。首先是函数名称。在ns-3中,几乎所有的助手类都提供了一个名叫SetAttribute()的函数(或类似名称的函数,如PointToPointHelper的SetChannelAttribute()和SetDeviceAttribute()。具体的函数名需要读者自行查阅相关模块的助手类定义)。这个函数的作用是为所有由这个助手类创建的对象配置新的属性值。其参数一般有以下两个,即属性名和新属性默认值。例如,前例中使用的属性名是DataRate。属性默认值分别是5Mbps和2.5Mbps。其次是属性值的表示。ns-3中表示一个属性时必须要指定其所属的类型。例如,前例中的属性类型是字符串型StringValue。这里的StringValue其实是一个C++类。类似这样表示属性类型的C++类在ns-3中还有很多,如表示非整型属性的UintegerValue类、布尔型属性的BooleanValue类、时间属性TimeValue和数据速率属性DataRateValue等。由于所有属性类型都可以转换成StringValue,为了简便起见,脚本中一些非字符串类型的属性值也常常用StringValue表示。这就是为什么本例中的DataRate属性用的是StringValue,而不是其原始类型DataRateValue。读者可以在第7章中找到属性类型的更多使用细节。限于篇幅,本节只讲解了使用较为普遍的助手类方法。命令行配置属性的方法会在2.5.4节中提及。Config::SetDefault()的使用方法和后文将要讲到Config::Set()函数非常类似。这里不再赘述。读者可以在ns-3目录下的examples子目录中找到更多关于这些方法的示例脚本。(2)第二类:ObjectBase::SetAttribute()函数ObjectBase类中的SetAttribute()函数提供的是一种更为精准的属性配置方法(这里读者暂时不需要理解ObjectBase类的含义,只需知道它是ns-3中绝大部分表示网络元素类的基类即可。Node、Application、NetDevice和Channel等C++类都是它的子类)。这个函数一次只能修改一个属性的取值,并且针对的是已创建对象中的属性(即在对象创建之后使用,与第一类方法相反)。例如,如果需要在模拟过程中改变一个网络设备的传输速率,第一类中的3种方法就不起作用了。这时需要使用ObjectBase类的SetAttribute()函数。示例代码方法如下。这里的Ptr是ns-3中的智能指针。读者暂且把它理解成普通的C++指针即可。例如,我们可以把“Ptr<NetDevice>dev0”简单理解成“NetDevice*dev0”。当然,使用SetAttribute()函数的前提之一是需要知道如何在脚本中提取指定的网络元素对象。关于这方面的内容会在2.7节中讨论。(3)第三类:Config::Set()Config是ns3名字空间中的一个嵌套名字空间。这个名字空间中的函数主要用于设置属性和trace变量。Config::Set()即是其中之一。这个函数可以被视作是前两类的综合体。它可以一次性配置多个已创建对象中的同名属性。这个函数的参数有以下两个,即属性命名空间路径(namespacepath)和属性值。前者用于唯一标识一个已创建对象中的属性。后者用于表示该属性的数值。在下面这段代码中,属性命名空间路径指向的是第0个结点对象中第0个网络设备对象的DataRate属性。这段代码的和第二类中的ObjectBase::SetAttribute()作用完全相同。可以看出,命名空间路径分为两部分:<对象配置路径>/<属性名>。对象配置路径(configpath)也可以一次性表示多个对象。只需要把前例中的下标替换成星号(*)即可。这种方法需要我们事先知道一个对象的配置路径。一个较为直接的方法是在Doxygen主页中查找。这点会在后文介绍。为了便于记忆,这里先介绍两个配置路径的普适原则。●对象路径从左到右,遵循范围从大到小的原则。●绝大部分的对象配置路径都从“/NodeList/”或“/ChannelList/”开始。其中NodeList存储了脚本中所有已创建的结点对象。ChannelList存储了脚本中所有已创建的信道对象。之所以以它们为查找起点,是因为几乎所有的属性都属于某一个网络元素对象。而网络元素对象要么依附于结点存在,要么依附于信道存在。●对于一个结点,访问其Application对象需要从“/NodeList/<结点序号>/ApplicationList/”开始。访问其NetDevice对象需要从“/NodeList/<结点序号>/DeviceList/”开始。相信读者在阅读了2.7节的Node内部信息存储结构之后会对这些规律有更为深刻的理解。3.读取属性ObjectBase::GetAttribute()函数可以被用来获取一个对象中的属性值。4.查找属性查找一个模块中属性的最简单办法就是查看该模块的示例代码。往往一些最基本的属性在示例代码中都有配置。此外,ns-3modellibrary中每个模块的使用指南里往往会包含一些常用属性的介绍。本书第3篇对一些主要模块的核心属性也做了相应总结。若要详细配置一个协议或开发新功能,就需要了解属性的通用查找方法。如本节第1部分中所述,属性本质上是一个C++类的变量。那么查找属性最直接的办法就是查看这个C++类的源代码。准确地说,是查看这个C++类的GetTypeId()函数。因为一个C++类的属性定义就是在这个函数中完成的(这里暂时不需要理解GetTypeId()背后的深层含义)。例如,在PointToPointNetDevice类的GetTypeId()函数中就可以看到传输速率属性DataRate的定义。读者还可以看到,该属性对应的成员变量是m_bps。另外,读者也可以在Doxygen主页上找到C++类的属性列表。首先在右上角搜索栏里直接输入C++类名,网页会自动弹出相匹配的搜索结果(见图2-10)。图2-10搜索结果选择后缀携带“ns3”的结果选项后,网页就会自动跳转到PointToPointNetDevice类的主页(见图2-11)。图2-11PointToPointNetDevice类的主页单击说明信息后的“More”会让网页再次跳转到“DetailDescription”部分。这时可以在“Attributes”部分查看到该类的所有属性信息(见图2-12)。图2-12类的属性信息“DetailDescription”部分的另一个有用信息是该类的对象配置路径。这个路径对属性配置(Config::Se

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论