Network Simulator 助教:赵俊民
课程目的 学习和使用网络模拟器NS,使大家对如何做网络模拟有个基本认识和了解 如何使用NS来发现和研究网络中的一些问题 提高大家的编程能力和分析解决问题的能力
网络研究的方法 分析(Analysis) 实验 网络实验床(testbed) Emulation 模拟(Simulation) 构建模型,数学分析如排队论,图论等 实验 网络实验床(testbed) Emulation Lab environment 模拟(Simulation) 模拟工具,如NS,OpNet,QualNet等
模拟能够做什么? 对现有的协议或算法进行研究,主要是它们的行为或性能进行评价 对新提出的协议或算法进行测试 对研究成果进行对比评价,找出各种的优点和不足
模拟的优点和缺点 优点 缺点 非常灵活,易于配置,并且成本比较低(不需要买设备) 可以研究大规模网络 容易获得研究成果 会忽略掉真实网络中一些重要细节 对产生的结果要仔细验证,有可能不适合于真实环境
NS简介 NS(Network Simulator)它是由LBNL ( Lawrence Berkeley National Laboratory ) 的网络研究小组开发的模拟工具。 NS具有可扩展、易配置、可编程等特点。 NS是面向对象的,离散事件驱动的网络环境模拟器,开发于UC Berkeley,它可以模拟各种IP网络环境。 NS实现了对许多网络协议的模拟,如TCP,UDP,和数据源发生器(traffic source)如FTP,WWW,Telnet,Web,CBR和VBR等。
NS简介(续) NS-2由C++和Otcl(加上面向对象特性的TCL脚本程序设计语言,开发于MIT)两种程序设计语言实现。 Ns前端运行Otcl Otcl:运行速度慢,容易修改 =>场景配置 Ns 用C++实现网络协议栈 C++:运行速度快,不容易更改 => 协议栈实现
NS的学习方法 掌握原理。NS离散事件模拟机制和分裂对象模型需要重点理解。 牢记节点结构。节点(node)是网络中最重要的组件,对有线和无线中节点结构要非常清楚,明白数据包是如何在节点中流动的。 整体上把握NS。了解组件的基本功能,不要太注重细节,当需要时候再理解清楚。 积极阅读源代码,采取的策略一般是整体到局部,从粗到细。 NS-2中用到的TCL很简单,不需要专门学习
NS的安装 Windows平台 Linux平台 下面以版本2.31为例 Cygwin+Ns-2 Vmware虚拟机+linux+Ns-2 注意:不同NS版本所能安装的linux版本是不同的,要核查清楚。 下面以版本2.31为例 选择安装环境Fedora Core 5
NS的安装(续) 首先获得NS安装包,参考下载地址http://www.isi.edu/nsnam/dist/ns-allinone-2.31.tar.gz 或其他网址 安装文件 # cd 要安装的目录 #tar zxvf ns-allinone-2.31.tar.gz #cd ns-allinone-2.31 # ./install 配置环境变量 当安装完毕时,会显示让你如何配置!
问题的解决 如果在安装过程中遇到问题,可以到http://www.isi.edu/nsnam/ns/ns-build.html Ns mailing list: ns-users@isi.edu 需要注意的是,在不同的系统下安装出的问题会有所不同 在www.google.com搜索
NS-2目录结构
NS2层次结构
NS命令的使用 当安装完毕后,在任何路径下,直接键入ns 如果要运行某个tcl程序(比如example.tcl,那么: ns example.tcl
Tcl与Tk编程环境概述 Tcl与Tk是安装在Unix/Linux环境下的两个包,它们一起构成了一套开发系统应用程序和图形用户界面接口(GUI)应用程序的环境。 Tcl的全称是Tool Command Language。 Tk是Tcl在X Window环境下的扩展,它包含了Tcl的全部的C库函数,以及支持X Window的窗口、控件等C库函数,为用户开发图形用户界面提供了方便。 Tcl是解释执行的脚本语言。它的实现依赖于Tcl内部的C函数库。添加新的C函数就可以扩充Tcl的命令和功能,是扩展性非常强的脚本程序设计语言。 Tcl解释器把用户输入的命令和程序语句进行初步分析,然后调用C函数库里的相应函数来执行,输出结果。 Tcl是无强制类型的脚本语言,一切变量,不论整型,浮点型等,都以字符串的形式存储。
Tcl基础 先看一个最简单的例子:hello world #Define a process named "helloworld" proc helloworld {} { puts "Hello world!!!" } # Call the process helloworld Helloworld
Tcl基础(cont.) # Writing a procedure called "test" proc test {} { set a 43 set b 27 set c [expr $a + $b] set d [expr [expr $a - $b] * $c] for {set k 0} {$k < 10} {incr k} {…………} } # Calling the "test" procedure created above test
Tcl命令和语句的执行过程 Tcl解释器分为两个主要模块,Tcl的Parser和Tcl的executer。 Tcl的executer的功能是:把Tcl的Parser处理过的命令或语句字符串向量进行分析。由于字符串向量的第一个字符串元素是命令的名称,那么executer匹配Tcl中C库函数的函数名,当匹配到的时候,把全部参数传递给匹配到的C函数来执行命令,然后返回结果。如果匹配不到则返回错误。 返回的结果都是字符串类型的,如果没有实际意义的返回结果,则返回空的字符串。
OTcl —— Tcl的面向对象扩展 Otcl是Tcl的面向对象(Object Oriented)的扩展,在Otcl中加入了类的概念。 对象是类的实例,它有自己的属性(成员变量,InstVar)和自己的内部操作(成员函数,InstProc)。 对象具有继承、封装、多态性和动态绑定等性质。 面向对象机制的加入使得原始的Tcl变得更加强大,更加方便使用。 虽然和C++中对象和类以及其他面向对象程序设计语言中的概念相同,但是具体实现和语法却存在很大的差别。
OTcl —— Tcl的面向对象扩展 # Create a class call "mom" and # add a member function call "greet" Class mom mom instproc greet {} { $self instvar age_ puts "$age_ years old mom say: How are you doing?" } # Create a child class of "mom" called "kid" # and overide the member function "greet“ Class kid -superclass mom kid instproc greet {} { $self instvar age_ puts "$age_ years old kid say: What's up, dude?" } # Create a mom and a kid object set each age set a [new mom] $a set age_ 45 set b [new kid] $b set age_ 15 # Calling member function "greet" of each object $a greet $b greet
学习TCL/OTCL参考资料 NS与网络模拟—第三章 http://netmedia.gist.ac.kr/~dulee/tclns.html ftp://ftp.tns.lcs.mit.edu/pub/otcl/doc/tutorial.html
NS模拟的例子 一个简单的有线模拟场景 一个简单的无线模拟场景 场景中由两个节点组成,一个节点向另一个节点发送ftp数据
有线模拟—场景示意图 ftp tcp 1mb,20ms n0 n1 sink ftp sec 1.0 4.0 5.0
一个简单有线模拟脚本(1) #建立一个Simulator对象实例 set ns [new Simulator] #打开一个out.tr的文件,用来记录模拟过程的trace数据 set tracefd [open out.tr w] $ns trace-all $tracefd #打开一个out.nam文件,用来记录nam的trace数据 set nf [open out.nam w] $ns namtrace-all $nf 详细讲一下每句话的含义
一个简单有线模拟脚本(2) #建立一个名为finish的过程,关闭两个trace文件,并执行nam程序 proc finish {} { global ns nf tracefd $ns flush-trace close $nf close $tracefd exec nam out.nam & exit 0 }
一个简单有线模拟脚本(3) set tcp [new Agent/TCP] $tcp set class_ 2 #新建两个node(节点),并赋给n0和n1 set n0 [$ns node] set n1 [$ns node] #在n0和n1建立一条双向的link(链路),该链路的带宽为1Mb/s,延迟为10ms,队列为DropTail $ns duplex-link $n0 $n1 1Mb 10ms DropTail #新建一个Tcp代理,并绑定到n0上 set tcp [new Agent/TCP] $tcp set class_ 2 $ns attach-agent $n0 $tcp
一个简单有线模拟脚本(4) #把tcp和sink两个agent连接起来 $ns connect $tcp $sink #新建一个Tcpsink,并绑定到n1上 set sink [new Agent/TCPSink] $ns attach-agent $n1 $sink #把tcp和sink两个agent连接起来 $ns connect $tcp $sink #新建一个ftp业务,并连接到tcp代理上 set ftp [new Application/FTP] $ftp attach-agent $tcp $ftp set type_ FTP
一个简单有线模拟脚本(5) #告知Simulator对象在1.0时启动ftp $ns at 1.0 "$ftp start" $ns at 4.0 "$ftp stop“ #告知Simulator对象在5.0调用finish过程 $ns at 5.0 “finish“ #开始模拟 $ns run
数据包通信图
NS网络组件模型 网络组件:构成网络的基本功能单位,包括简单网络组件和复合网络组件,一般可以从真实网络中找到对应体,如一个节点对象可以对应一个主机或路由器,一个链路对象对应两个主机相连的媒介。 所有的基本网络组件可以划分为2类,分类器(Classifier)和连接器(Connector)。它们都是NsObject的直接子类,也是所有基本网络组建的父类。 分类器(Classifier)的派生类组件对象包括地址分类器(AddrClassifier)和多播分类器(McastClassifier)等。 连接器(Connector)的派生类组件对象包括队列(Queue),延迟(Delay),各种代理(Agent),和追踪对象类(Trace)
分类器 分类器(classifier)是Ns-2基本网络组件的一个大类。它的基本派生类有地址分类器(AddrClassifier)和多播分类(McastClassifier)等。 拓扑结点(Node)是由一个结点入口对象和若干个分类器(Classifier)组成的一个复合对象。
单播节点(Node)
有线Multicast节点结构
连接器(connector) 连接器——Connector是Ns-2基本网络组件中的另一个大类。它的基本派生类对象包括缓冲队列(Queue)、延迟(Delay)、各种产生和处理数据包的代理(Agent)和对象的跟踪器(Trace)。 拓扑结点连接类(Link)是Ns-2中另一个主要的复合组件对象。一个结点和另一个结点之间的简单连接(simplex-link)是单向的。一个最基本的简单连接由一个连接入口、包缓冲队列、延迟处理对象、废弃处理对象和时间处理对象(TTL)组成。 一个结点的输出队列,是通过和这个结点相连的Link中的缓冲队列来实现的。
单向链路示意图
拓扑简单连接——Link 一个简单的拓扑结构中的2个结点之间的连接是单向的。 通过Simulator对象中的dublex-link成员函数可以在两个结点之间的2条互相的单向简单连接。 一个结点或交换设备在一个输出路径上的输出队列,是由在该路径上的简单连接(simplex-link)中的数据缓冲队列来实现的。 在Link中从数据缓冲队列(queue)中被弹出(dequeue)的包被传递到延迟处理对象(Delay)中进行延迟的模拟。 在Link中从数据缓冲队列(queue)中掉出(Dropped)的包,被送到废弃处理对象(一个Null Agent)中被释放。 TTL对象计算包在Link中被处理时的相关的时间参数。
加入Trace和Monitor的连接对象
代理(Agent)类的种类和说明 代理网络组件代表了在网络层中数据包的产生地和运输源头,同时也是各层网络中各种协议的实现。 Agent/TCP Agent/TCP/Reno Agent/UDP Agent/Tcpsink Agent/NULL …
包(Packet)类的结构和说明 在Ns-2中,一个“包”是由一个报头(Header)堆栈(Stack)和一个可选择的数据空间构成。 报头的格式,在模拟器对象(Simulator)创建的过程中创建好,同时,所有注册过的报头,比如一般的属性说明、IP报头,Tcp报头,RTP报头,都在包的报头堆栈中存储。 通过不同报头在包的报头堆栈中的偏移量(Offset),网络组件可以根据需要,来访问包中的不同报头。这就符合上面的机制,在报头格式创建的时候,无论这个注册过的报头是否需要,都被加到报头堆栈中。 通常,一个包只含有报头堆栈部分,而没有数据空间。这是因为,在模拟的环境中,传输实际的数据是没有意义的。
包(Packet)类的结构和说明
应用程序(application) 应用程序层是建立在传输代理上的应用程序的模拟。 Ns-2中有两种类型的“应用程序” 数据源发生器(Traffic Generator) Exponential On/Off Pareto On/Off CBR 模拟的应用程序(Simulated applications) Application/FTP Application/Telnet
一个无线仿真的例子(1) Scenario 2 mobile nodes moving within 500m x 500m flat topology using DSDV ad hoc routing protocol Random Waypoint mobility model TCP traffic
一个无线模拟的例子(2) # Define Global Variables # create simulator set ns [new Simulator] # create flat topology in 500m x 500m area set topo [new Topography] $topo load_flatgrid 500 500 # Define wireless ns trace # ns trace set tracefd [open out.tr w] $ns trace-all $tracefd set namtrace [open out.nam w] $ns_ namtrace-all-wireless $namtrace 500 500
一个无线模拟的例子(3) #Create god #Create wireless channel set god [create-god 2] $ns at 900.00 “$god setdist 1 2 1” #Create wireless channel set thechan [new Channel/WirelessChannel
GOD (General Operations Director) Stores all-pairs Dijkstra shortest path lengths Allows comparison of path length with optimal Automatically generated, contained in scenario file set god [create-god <no of mnodes>] $god set-dist <from> <to> <#hops> 迪杰斯特拉
一个无线模拟的例子(4) $ns node-config \ -adhocRouting DSDV \ -llType LL \ # Define how a mobile node is configured $ns node-config \ -adhocRouting DSDV \ -llType LL \ -macType Mac/802_11 \ -ifqLen 50 \ -ifqType Queue/DropTail/PriQueue \ -antType Antenna/OmniAntenna \
-propType Propagation/TwoRayGround \ -phyType Phy/WirelessPhy \ -channel $thechan \ -topoInstance $topo -agentTrace ON \ -routerTrace OFF \ -macTrace OFF \ -movementTrace OFF
一个无线模拟的例子(4) for {set i 0} {$i < 2} {incr i} { #Use “for” loop to create 2 nodes for {set i 0} {$i < 2} {incr i} { set node($i) [$ns node] # disable random motion $node($i) random-motion 0 }
一个无线模拟的例子(5) #Define node movement model #Mobile Movement Generator $node_(0) set X_ 10.0 $node_(0) set Y_ 400.0 $node_(0) set Z_ 0.0 $node_(0) setdest 400.0 10.0 1.0 #Random movement $node_(0) start
一个无线模拟的例子(6) #Define traffic model(tcp) set tcp1 [new Agent/TCP/Newreno] set sink1 [new Agent/TCPSink] $ns_ attach-agent $node_(0) $tcp1 $ns_ attach-agent $node_(2) $sink1 $ns_ connect $tcp1 $sink1 $tcp1 set fid_ 1 set ftp1 [new Application/FTP] $ftp1 attach-agent $tcp1 $ns_ at 10.0 "$ftp1 start"
一个无线模拟的例子(7) # Tell nodes when the simulation ends for {set i 0} {$i < 3 } {incr i} { $ns_ at 200.0 "$node_($i) reset" } #Tell ns/nam the simulation stop time $ns_ at 200.0 “$ns_ nam-end-wireless 200.00” $ns_ at 200.00 “$ns_ halt” #Start your simulation $ns_ run
数据流
无线节点的结构 Classifier: Forwarding Node Agent: Protocol Entity Node Entry LL: Link layer object IFQ: Interface queue MAC: Mac object PHY: Net interface port classifier Node protocol agent 255 addr classifier routing agent defaulttarget_ LL ARP LL LL IFQ IFQ droped: LL (1 packet only, drop due to arp), IFQ MAC: collision detection PHY: Carier Sense threshold, Receive Threshold. Packets above CSThresh are able to cause a collision. IFQ: can be any queue, droptail or priority, etc. MAC MAC Propagation and antenna models PHY PHY MobileNode Radio propagation/ antenna models Prop/ant CHANNEL
无线节点组件 分类器 路由代理 defaulttarget_ 指向路由代理对象 255 is the port id assigned for rtagent_ 路由代理 Ad hoc routing protocol, e.g., AODV, DSDV, DSR; or directed diffusion
无线节点组件 Link Layer ARP Interface queue (IFQ) Same as LAN, but with a separate ARP module Looks up IP-to-MAC mappings using ARP ARP Resolves IP address to hardware (MAC) address Broadcasts ARP query Interface queue (IFQ) Gives priority to routing protocol packets Has packet filtering (search and remove) capacity
无线节点组件 MAC Network interface (PHY) 802.11 IEEE RTS/CTS/DATA/ACK for unicast Sends DATA directly for broadcast Network interface (PHY) Used by MobileNode to access channel Stamps outgoing packets with meta-data Interface with radio/antenna models
无线节点组件 Radio Propagation Model Antenna Friss-space model: attenuation at near distance Two-ray ground reflection model: attenuation at far distance Shadowing model: probabilistic Antenna Omni-directional, unity-gain
无线Channel Duplicate packets to all mobile nodes attached to the channel except the sender It is the receiver’s responsibility to decide if it will accept the packet Collision is handled at individual receiver O(N2) computation!
NS脚本的写法
Trace数据分析 在上面的两个脚本中,定义了两个输出文件,一是out.tr,一是out.nam。下面介绍这两种输出文件的作用及其格式,以及如何从他们中得到所想要的信息。
Nam文件 nam out_nam_file 如何打开nam文件 演示动画命令 用来动画演示整个模拟过程,如数据包的发送,包的的丢弃 set nam_file_var [open out_nam_file w] $ns namtrace-all $file_var 演示动画命令 nam out_nam_file 用来动画演示整个模拟过程,如数据包的发送,包的的丢弃
Nam文件格式样例 …. V -t * -v 1.0a5 -a 0 A -t * -n 1 -p 0 -o 0xffffffff -c 31 -a 1 A -t * -h 1 -m 2147483647 -s 0 c -t * -i 1 -n Blue n -t * -a 0 -s 0 -S UP -v circle -c black -i black n -t * -a 1 -s 1 -S UP -v circle -c black -i black n -t * -a 2 -s 2 -S UP -v circle -c black -i black l -t * -s 0 -d 1 -S UP -r 1700000 -D 0.02 -c black -o right l -t * -s 2 -d 0 -S UP -r 2000000 -D 0.01 -c black -o right q -t * -s 1 -d 0 -a 0.5 q -t * -s 0 -d 1 -a 0.5 + -t 0.5 -s 2 -d 0 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 0 ------- null} - -t 0.5 -s 2 -d 0 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 0 ------- null} h -t 0.5 -s 2 -d 0 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 -1 ------- null} r -t 0.51016 -s 2 -d 0 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 0 ------- null} + -t 0.51016 -s 0 -d 1 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 0 ------- null} - -t 0.51016 -s 0 -d 1 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 0 ------- null} h -t 0.51016 -s 0 -d 1 -p tcp -e 40 -c 1 -i 0 -a 1 -x {2.0 1.0 -1 ------- null}
trace数据文件 用来记录整个模拟过程中所发生的事件,每个包被发送,转发,接收的相关信息 打开数据Trace文件 set data_file_var [open out_data_file w] $ns trace-all $data_file_var 在分析一个trace文件前,一定要注意trace格式,无线和有线的生成的trace文件格式是不同的!
trace文件格式示例 event time from node to pkt type size flags fid src addr 1 2 3 4 5 6 7 8 9 10 11 12 event time from node to pkt type size flags fid src addr dst seq num id r : receive (at to node) +: enqueue (at queue) : dequeue (at queue) d : drop (at queue) node.port …. + 0.884 1 2 cbr 1000 ------- 2 1.0 3.1 98 98 - 0.884 1 2 cbr 1000 ------- 2 1.0 3.1 98 98 r 0.89 1 2 cbr 1000 ------- 2 1.0 3.1 97 97 + 0.89 2 3 cbr 1000 ------- 2 1.0 3.1 97 97 - 0.89 2 3 cbr 1000 ------- 2 1.0 3.1 97 97 r 0.890706 2 3 cbr 1000 ------- 2 1.0 3.1 94 94 + 0.892 1 2 cbr 1000 ------- 2 1.0 3.1 99 99 - 0.892 1 2 cbr 1000 ------- 2 1.0 3.1 99 99 r 0.898 1 2 cbr 1000 ------- 2 1.0 3.1 98 98 + 0.898 2 3 cbr 1000 ------- 2 1.0 3.1 98 98 - 0.898 2 3 cbr 1000 ------- 2 1.0 3.1 98 98 r 0.898706 2 3 cbr 1000 ------- 2 1.0 3.1 95 95
有关trace格式的请参照 http://nsnam.isi.edu/nsnam/index.php/NS-2_Trace_Formats
分析trace数据的工具 数据处理 绘图工具 使用Shell文本处理命令,如 cut, join, sed, grep等命令 脚本语言:awk,perl,python等 绘图工具 gnuplot xgraph
从Trace中能够得到什么? 端到端的吞吐率:指一个s/d的吞吐率 端到端的延迟,指一个包从一个源发到目的地所需要的时间 丢包率 抖动,等等
主要学习资料 NS与网络模拟 NS by example http://nile.wpi.edu/NS/ ns_tutorial http://www.isi.edu/nsnam/ns/tutorial/ 中文论坛-百思论坛 http://www.baisi.net/forum-440-1.html
第二讲
NS体系结构(重点理解) 分裂对象模型 C++ implement the simulation model Implement data path per packet processing, core of ns fast to run, detailed, complete control OTcl (Tcl script language with Object-oriented extensions developed at MIT ) Simulation scenario configurations Periodic or triggered action Manipulating existing C++ objects fast to write and change
为什么要用两种编程语言来实现NS? 模拟器有两方面的事情需要做 一方面,具体协议的模拟和实现:需要一种程序设计语言,它需要很有效率的处理字节,报头等信息,需要应用合适的算法在大量的数据集合上进行操作。为了实现这个任务,程序内部模块的运行速度是非常重要的,而运行模拟环境的时间、寻找和修复bug的时间,重新编译和运行的时间(run-around time)就显得不是很重要了。 另一方面,许多网络中的研究工作都围绕着网络组件和环境的具体参数的设置和改变而进行的,需要在短时间内快速的开发和模拟出所需要的网络环境,并且方便修改和发现、修复程序中的Bug。在这种任务中,run-around time就显得很重要了,因为模拟环境的建立和参数信息的配置只需要运行一次。
为什么是C++和OTCL来实现? 为了满足以上两种不同任务的需要,NS的设计实现使用了两种程序设计语言,C++和Otcl。 C++程序模块的运行速度非常快,是强制类型的程序设计语言(变量严格定义整型,浮点型和字符、字符串类型),容易实现精确的、复杂的算法,但是修改和发现、修正bug所花费的时间要长一些。该特性正好用于解决第一个方面的问题。 Otcl是脚本程序编写语言,是无强制类型的,比较简单,容易实现和修改,容易发现和修正bug,虽然它的运行速度和C++的模块相比要慢很多。该特性正好用于解决第二方面的问题。
什么时候用C++,什么时候用Otcl? 下面情况用OTcl来实现: 对模拟环境的配置、建立和在模拟中只运行 一次的地方。如果可能,用Otcl脚本来随意操作已经存在的C++对象。 下面情况用C++来实现: 如果你需要在模拟中对每一或大量数据包进行处理。如果你想修改已存在的C++对象的属性和功能。 注意并没有严格的界限
NS的用户视图
NS-2的C++和Otcl类对应示意图
C++和Otcl类对应关系 如何联系起来的,下一节详细讲解一下!
C++和Otcl类对应关系(cont.) NS构件(独立的功能单位)通常由相互关联的两个类来实现,一个由C++类实现,一个由Otcl类实现;TclCL把这两张语言中的对象和变量联系起来,充当“glue”的角色。 所有C++类(编译类)都是从类TclObject一级级继承出来的,而所有Otcl类(解释类)都是从SplitObject一级级继承出来的。 分裂对象模型中Otcl类称为解释类,把对应的C++类称为编译类,这两个类互为“影像类”;同样地,称Otcl对象为解释对象,称C++对象为编译对象,这两个对象互为“影像对象”。 大家记住在NS中,
C++和Otcl类对应关系(cont.) NS每当实例化一个构件时,都会同时创建一个Otcl中的对象和一个对应的C++对象,并且这两个对象可以互操作。例如,在前面的例子中,通过 “new Agent/TCP” 创建了一个Otcl对象,也相应地在C++中创建了一个tcp对象
NS-2的总体体系结构
NS-2的类体系结构(部分)
NS-2的类基础框架 Tcl类:C++代码与Tcl代码之间的接口 TclObject类:所有编译对象的基类 TclClass类:定义了解释类的类层次,并允许用户实例化TclObject,与TclObject一一对应 TclCommand类:封装了C++代码和Tcl代码相互调用命令的方法 EmbeddedTcl类:封装了装载更高级别的内置命令的方法 InstVar类:访问C++成员变量,如Otcl变量方法
Tcl类简介及功能实现 获得Tcl解释器的引用,来调用解释器的功能。 用来调用Otcl命令函数 传递/获得Otcl命令运行的结果 报告错误/退出模拟器的运行 在解释器内部的用来保存生成对象地址的哈西表 获得Tcl解释器的句柄,来对解释器进行直接访问。
用来调用Otcl命令函数 可以在获得了Tcl解释器的引用之后,通过这个引用来调用Otcl中的控制台命令。 tcl.eval(char* s);执行字符串s,并在tcl的结果变量中保存执行结果。 tcl.evalc(char* s);首先把s存储进tcl中的命令缓冲区中,然后再执行该s命令,并且在结果变量中返回结果。 tcl.eval();执行已经存储进tcl命令缓冲区中的命令,并且在结果变量中返回结果。 tcl.evalf(const char* s,…);该函数类似于c语言中的printf命令,可以进行字符串的过滤,执行同eval()。 tcl.buffer()返回tcl中的命令缓冲区。
传递/获得Otcl命令运行的结果 当Tcl解释器执行用户指定的控制台字符串命令之后,解释器在自己内部的结果字符串变量中保存命令的执行结果。 可以通过tcl.result(const char* s);命令来修改命令执行后的结果。 也可以通过tcl.result();命令来获得命令执行后的结果字符串。
解释器内部的保存生成对象地址的哈西表 在Ns-2中的Tcl命令解释器里保存了一个存储对象地址的哈西表。 Ns-2为每一个在模拟过程中生成的TclObjet类及派生类的对象在该哈西表中保存了一个指针,方便访问。 Tcl.enter(TclObjet* o);在哈西表中加入一个对象。 Tcl.lookup(char* s);在哈西表中查询名字为s的对象并返回。 Tcl.remove(TclObjet* o);在哈西表中删除该对象的地址。
TclObject类的功能和实现 创建/清除模拟器组件的对象。 实现从C++类成员变量到OTcl类成员变量的绑定(Binding)。 实现变量的跟踪(Tracing)。 实现从C++类的成员函数到Otcl类的成员函数之间的一一对应(通过该类的command()函数)。
创建/清除模拟器组件的对象 在TclObject的对象名空间里获取一个新创建对象的唯一的句柄。 执行该新建对象的构造函数。在此过程中,该对象的构造函数会首先调用其基类的构造函数。所有最终基类为TclObject的类的对象最终都会执行TclObject类的构造函数,这个构造函数创建一个内部的C++对象(Shadow Object),完成变量绑定、成员函数和命令的对应。 当内部的C++对象创建完毕后,ns调用所有派生类的构造函数,来进一步完成变量的绑定和命令的对应,完成对象的初始化工作。 当对象创建完毕后,ns把对象的指针添加到TclObject类中的对象哈希表中。 把cmd{}函数设置为对应的Otcl对象中的一个成员函数。
从C++类成员变量到OTcl类成员变量的绑定 变量绑定的目的,是用户通过修改和设置脚本中的Otcl对象中的成员变量时,该对象对应的“影子”C++对象中的成员变量也相应的变化,保持一直。变量绑定是TclObjet类中的成员函数。变量绑定在构造函数中完成。 ASRMAgent::ASRMAgent() { bind(“pdistance_”,&pdistance_); bind(“requestor_”,&requestor_); bind_time(“lastSent_”,&lastSessSent_”); bind_bw(“ctrlLimit_”,&ctrlBWLimit_); bind_bool(“running_”,&running_); }
两种对象成员函数之间的一一对应 类似于变量绑定,Otcl对象中也要创建相应的成员函数,来达到调用对应的C++对象中的成员函数的目的。 这种让成员函数之间一一对应的机制,是通过在该对象中的command()函数来实现的。由于command()是TclObjet的成员函数,所以它的派生类每个对象都可以重写该函数。 Int ASRMAgent::command(int argc,const char* const* argv) { Tcl& tcl=Tcl::instance(); if(argc==3) { if(strcmp(argv[1],”distance?”)==0) {…………} } return (SRMAgent::command(argc,argv); }
TclClass类的功能和实现 这是一个纯虚基类,只能生成派生类后才能使用。 这个类的作用是实现从C++类的对象到Otcl类的对象之间的桥梁。这个类一般被声明为static类型,并且声明该类后就创建该类的一个对象。 每一个Otcl类和它对应的“影子”C++类之间,都存在一个TclClass类的派生类,来完成对象之间的通信桥梁工作。所以,TclClass类和它的派生类又叫做ns中的Linkage。 Static class RenoTcpClass::public TclClass { Public: RenoTcpClass:TclClass(“Agent/Tcp/Reno”){} TclObjet* create(int argc,const char* const* argv) { return (new RenoTcpAgent()); } }class_reno;
TclCommand类 是创建能够被Tcl命令解释器解释执行的普通命令的类。其作用是创建新的(不存在原始Tcl命令库中的)Tcl命令,通过继承该类,重写该类的command()函数而实现新命令的创建。 具体创建新的Tcl命令的过程,举例: 在控制台上输入:% hi this is ns [ns-version] 在控制台上输出:Hello world,this is ns 2.0a12 下面谈谈具体创建新Tcl命令的方法。
TclCommand类 1、从TclCommand类中派生新的命令类。 Class say_hello : public TclCommand { Public: say_hello(); //构造函数 int command(int argc,const char* const* argv); //核心命令函数 }; 2、执行基类的构造函数,以新命令名为参数传入。 say_hello() : TclCommand(“hi”) {}
TclCommand类 3、重写基类的Command()函数,来实现具体要完成的命令操作。 #include<iostream.h> Int say_hello :: command(int argc,const char* const* argv) { cout<<“hello,world:”; for(int i=1;i<argc;i++) cout<<‘ ‘<<argv[I]; cout<<‘\n’; return TCL_OK; }
TclCommand类 4、在init_misc(void)函数中实例化该类: new say_hello; 通过以上4个步骤,就创建了新的命令——“hi”。在控制台上输入“hi”回车,就可以得到期望的执行效果了。 命令的实例化都在init_misc(void)函数中,该函数的位置是~ns/misc.cc。 Init_misc(void)在ns初始化的时候被Tcl_AppInit(void)函数所调用。
EmbededTcl类 用户对通过对文件~tclcl/tcl-object.tcl进行修改,或是修改、增加tcl/lib的文件来对ns进行扩展。 对于新文件的装载是由类EmbeddedTcl的对象来完成的。 Tcl脚本其实就是由char类型数据组成的文本文件,所以类Embedded的构造函数可以用char*型指针指向脚本代码,并将此指针值赋与成员变量 code_。
InstVar类 类InstVar定义了实现绑定机制的方法: 隐式,当创建新解释对象时,构造函数创建与其对应的编译对象,编译对象的构造函数将它的成员变量与解释对象的实例变量绑定; 显式,定义一个实现绑定变量的命令,然后通过方法cmd调用。 基类InstVar派生五个子类:InstVarReal,InstVarTime,InstVarBandwidth,InstVarInt, InstVarBool。分别用来绑定real型,time型,bandwidth型,integer型,和boolean型变量。
模拟器Otcl类——Simulator 通常情况下,模拟器模拟工作的开始,就是通过创建一个Simulator类的实例后开始的。 一个Simulator类的实例表示了一个模拟的环境(拓扑结构,事件的发生顺序,需要模拟的功能等)。这是一个Otcl类,没有对应的C++类。 Simulator类中定义了大量的配置网络模拟环境、创建拓扑结构、安排模拟事件的函数,用于脚本模拟网络的环境。 同时,Simulator类中还保存了生成的拓扑结点(Node)的列表,用户每生成一个结点就在该列表中添加该结点的一个地址,方便访问。 在创建模拟器对象时,在构造函数中同时也创建一个该模拟器的事件调度器(Event Scheduler)。
事件调度机制—NS的心脏 NS是一个事件(event)驱动的模拟器,之所以称为事件驱动,是因为它以事件为基本处理对象。事件主要包括两种类型:packet和timer,它们都以Event类为基础。
事件调度器的实现 事件调度程序的用户主要是模拟数据包处理、数据包传输延迟的网络组件和需要定时器(Timer)的网络组件。 生成事件的网络组件往往就是在这个事件到来的时候处理(handle)该事件的组件。 通常,数据包在网络拓扑组件中通过send(Packet* p)和recv(Packet* p,Handler* h)函数来传递。 事件调度器分为2种,真实时间调度器和非真实时间调度器。真实时间调度器是为了实现模拟器和真实的网络之间进行通信所设计的,目前正在开发中,有试用版。 非真实时间调度器有三种,分别通过三种不同的数据结构来实现。简单链表、堆和日历。其中日历事件调度器是默认的调度器。
事件调度器的图示
NS模拟过程
一个简单的例子(NS by example) 目的用来说明C++和OTCL是如何连接起来的 设计一个简单的Agent—MyAgent. 在NS中,agent代表了网络层分组的起点和终点,并被用于实现TCP,UDP,DSR,AODV等网络协议。Agent类作为基类,由c++和Otcl共同实现,c++部分在~ns/agent.{cc,h} ,otcl 部分代码在位于~ns/tcl/lib/ns-agent.tcl。显然我们设计的MyAgent应从Agent类继承,但这个简单的Agent类不创建和发送包! 详细的请参照NS by example
MyAgent和MyAgentClass类定义
例子(cont.) MyAgent:必须从Agent类继承,从TclObject类派生而来,Agent基类中定义了一些NS所需要的接口。 MyAgentClass:是从TclClass派生,充当连接器类。NS根据MyAgentClass产生MyAgent对应的OTCL类Agent/MyAgentOtcl,并且将这个类与MyAgent类连接起来。 当NS开始运行时,它首先执行静态变量class my_agent的构造函数,这时创建了一个MyAgentClass实例。在这个过程中, MyAgent 对应的OTcl类Agent/MyAgentOtcl的成员函数和成员变量在OTCL space中产生。当用户在OTCL空间,使用命令new Agent/MyAgentOtcl创建一个OTCL实例时候,NS通过调用 “MyAgentClass::create” 创建一个“MyAgent”实例。.
例子(cont.) 变量绑定 C++成员变量只能在C++中访问,Otcl成员变量只能在Otcl脚本中访问。在NS中,通过变量绑定,可以使解释对象和编译对象的成员变量访问相同的数据。
例子(cont.) NS中支持五种变量类型的绑定,绑定函数如下: bind(): real or integer variables bind_time(): time variable bind_bw(): bandwidth variable bind_bool(): boolean variable 当你导出一个C++变量时,通常在 “ns-2/tcl/lib/ns-lib.tcl” 中设置初值,否则在你创建一个新的对象的时候会产生警告信息。
例子(cont.) 从c++向otcl导出命令
例子(cont.) Command方法 在Otcl对象中调用对应的C++对象的方法,NS通过command()函数实现的。对于每个TclObject,ns为其Otcl中的解释对象建立一个实例过程,cmd{}。过程cmd{}调用编译类对象方法command(),并将cmd{}参数作为一个参数数组传递给command()方法 在tcl脚本中调用这个命令 set myagent [new Agent/MyAgentOtcl]), $myagent call-my-priv-func
例子(cont.) 从c++中对象中调用OTcl命令,首先应获得Tcl解释器对象的应用,然后调用解释器的相关函数。
学习样例 NS by example 向NS添加一个新的Application和Agent,给出详细的步骤,大家应该仔细学习。 http://nile.wpi.edu/NS/ NS与网络模拟 给出一个简单的MFlood路由协议实现及其完整的NS做研究的步骤,不得不看! …
文档资料 www.isi.edu/nsnam NS官方文档 进入Web resources->mailing lists->ns-users ,可以找到一些问题的解答,也可以进行提问 在Subscribing to Ns-users 中,可以登记你的邮箱,以后通过邮箱直接发问,也可以通过邮箱收到其他人的问题和解答 NS官方文档 Ns-doc3的前三章是一些基本的东西,如ns的体系结构等。其余章节是针对某一部分,可以进行实际工作时边做边看
七、简单的扩展 扩展ns最简单的方法是在~tclcl/tcl-object.tcl中加入Otcl代码,或在目录~ns/tcl/lib下加入新Tcl脚本。 若是在~ns/tcl/lib下加入新脚本,则必须在~ns/tcl/lib/ns-lib.tcl中加入对脚本的引用,语句为: source tcl/<filemame> 在启动ns时,~ns/tcl/lib/ns-lib.tcl自动引用这个新脚本,类似于C程序中的“#include “filename” ”
进一步扩展 首先,将脚本转为类EmbeddedTcl对象: tclsh bin/tcl-expand.tcl tcl/lib/ns-lib.tcl | \ ../Tcl/tcl2c++ et_ns_lib > gen/ns_tclsh.cc ~ns/ns_tclsh.cc中的Tcl_AppInit函数通过Tcl::Init()调用~ns/tcl/lib/ns-lib.tcl,实现装载的语句是: et_ns_lib.load();