对于ns2安装好了之后,接下来的就就是了解ns2建立仿真的基本语法了。
在NS中,整个模拟过程是由一个名为simulator的Tcl类来定义和控制的,Simulator类提供了一系列对模拟进行配置的接口,这其中包括选择“事件调试器”的接口。进行模拟通常要首先创建一个Simulator类的实例对象,并调用该对象的一系列方法来创建节点(Node)、拓扑(Topology)等模拟所必需的对象。Simulator类提供了一些与建立模拟有关的方法。
Set ns [new Simulator] #建立模拟对象的一个实例
$ns halt #停止或暂停Scheduler
$ns run #开始Scheduler
$ns at
$ns use-scheduler Heap #让NS使用heap结构的事件调试器
节点(node)是网络拓扑的重要组成部分。
网络拓扑的另一部分-链路(link)。每进行一次模拟都需要建立一个Simulator类的实例来控制模拟的过程,Simulator类提供了一些实例过程来创建和管理节点,我们首先介绍Simulator类的这些与节点有关的实例过程,然后介绍Node类的主要部件-分类器(Classifier类)的有关内容。
建立一个节点的基本方法是调用Simulator类的node过程。
Set ns [new Simulator]
$ns node
图1.classfier
为什么说-addressType设置节点的地址类型。(hierarchical)这种地址结构在模拟组播业务时比较方便,可以按照组播树来设置节点的地址。
图2.node hierarchical
关于节点属性的配置可以使用命令:
$ns_node-config –reset
分类器(classifier)的作用:
收到一个分组后,节点需要检查分组的某些域,然后寻找与这个域的值相匹配的接收者。他是从逻辑上匹配一个分组,并基于匹配的结果把该分组传递给相应的对象。
1. address classifier功能是按照分组的目的地址进行匹配,用来支持单播分组转发。
2. port classifier功能是按照分组的目的端口进行匹配,将分组传递给相应的Agent对象。
3. replicator与普通的classifier不同,它不使用classify()函数,它的作用是生成一个分组的多份拷贝,并把这些拷贝转发给slot表中的所有对象。在组播分组转发时,一个分组需要被转发给多个目标对象,生成分组拷贝的工作就是由replicator完成的。
代理代表了网络层分组的起点和终点,并被用于实现如TCP和UDP等网络协议。Agent类是由C++和Otcl共同实现的。
1. UDP
UDP Agent的工作过程是这样的:从应用层程序接收数据块,如果数据块太长,UDP Agent负责把它们分段,然后把分段后的数据封装为UDP分组。NS中的UDP分组包含一个单调增加的序列号以及一个RTP时间戳(真实网络中的UDP分组不包含序列号和时间戳),序列号和时间戳不计算在分组的长度内,它们主要用于trace文件中,一些使用UDP的应用层程序也会用到它们。而对于Null这个Agent,它们通常和UDP配合使用,作为数据的接收者。
set ns [new Simulator]
set n0 [$ns node]
set n1 [$ns node]
$ns duplex-link $n0 $n1 5Mb 2ms DropTail
set udp0 [new Agent/UDP]
$ns attach-agent $n0 $udp0
set cbr0 [new Application/Traffic/CBR]
$cbr0 attach-agent $udp0
$udp0 set packetSize_ 536
set null0 [new Agent/Null]
$ns attach-agent $n1 $null0
$ns connect $udp0 $null0
$ns at 1.0 "$cbr0 start"
2. TCP
NS中有两类TCP代理:单向代理和双向代理。单向代理包括一系列TCP发送者和接收者(TCPSink)。双向代理本身即可以作为发送者也可以作为接收者,双向TCP代理目前主要包括BayFullTcp和FullTcp两类。
set ns [new Simulator]
set n0 [$ns node]
set n1 [$ns node]
$ns duplex-link $n0 $n1 5Mb 2ms DropTail
set tcp [new Agent/TCP] #创建TCP发送者
set sink [new Agent/TCPSink] #创建TCP接收者
$ns attach-agent $n0 $tcp #将发送者连接到$n0节点上
$ns attach-agent $n1 $sink #将接收者连接到$n1节点上
$ns connect $udp0 $sink #建立TCP连接
set ftp [new Application/FTP] #建立一个FTP应用
$ftp attach-agent $tcp #将FTP和TCP发送者联系起来,FTP作为TCP的数据源
$ns at 1.0 "$ftp start"
应用层:
应用层程序构建在运输层代理之上,它分为两大类:流量发生器(traffic generator)udp和应用模拟器(simulated application)tcp。
1. 将运输层代理绑定到节点上
2. 将应用层程序连接到运输层代理上
3. 通过系统调用使用运输层代理
4. 运输层代理对应应用层程序的回调
CBR_Traffic按照一个确定的速率产生数据。分组长度为一常数值。可以选择是否对分组发送时间间隔产生随机抖动。
可设置的参数有:
rate_ 发送速率,bit/s
packetSize_ 数据分组的长度B
random_ 标志位,表示是否需要在分组的发送时间上加入随机噪声。
maxpkts_ 最大发送的分组数量(缺省为268435456)
TrafficTrace按照一个trace文件产生数据。Trace文件中的每条记录包含2个32bit的字段,第1个字段是到下一分组产生的时间间隔,以微秒为单位,第2个字段是下一个分组的长度,以字节为单位。
set tfile [new Tracefile]
$tfile filename example-trace
set t1[new Application/Traffic/Trace]
$t1 attach-tracefile $tfile
set t2[new Application/Traffic/Trace]
$t2 attach-tracefile $tfile
图3.trace字段
Gwk内建函数
图4.gwk内建函数
图5.gnuplot函数
图6.gnuplot示例
在发送者和每一接收者之间实现点对多点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包。它提高了数据传送效率。减少了骨干网络出现拥塞的可能性。
单播(Unicast)传输:在发送者和每一接收者之间实现点对点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也必须相应的复制多份的相同数据包。如果有大量主机希望获得数据包的同一份拷贝时,将导致发送者负担沉重、延迟长、网络拥塞;为保证一定的服务质量需增加硬件和带宽。
说了这么多费话,下面开始我们组播测试:
首先我们设定我们的模拟计划:
图1.网络拓扑
我们想利用如上图所示的拓扑结构图来进行模拟。
主要任务有:
1. 测试源节点9的业务发送量
2. 测试9-11的延迟时间
3. 测试10-0链路的吞吐量
4. 测试汇点0的负载
5. 出于实验的要求,我们应该还描绘出组播树,当然在ns2中,利用nam工具可以图形显示数据发送的过程,显示组播树。
基于以上ns2的学习,可以很容易了解对于模拟仿真我们大体分为三步
1. 编写tcl仿真程序
2. 编写awk数据分析文件
3. 利用gnuplot图形工具,生成图形。
当然为了更好的了解组播的特点,我们特别的也进行了单播的模拟,进而进行比较,可以很直观的看出组播的特点。
组播程序:
#使节点支持组播
#创建NS模拟器对象与组播组
set ns [new Simulator -multicast on]
$ns color 1 Red
set group [Node allocaddr]
#定义trace文件
set ft [open mulicast.tr w]
$ns trace-all $ft
#设定name输出文件,并将该文件与动画输出联系在一起
set fn [open mulicast.nam w]
$ns namtrace-all $fn
#创建finish过程,在NS运行结束时做必要的处理
proc finish {} {
#清空输出缓存,关闭nam输出文件
global ns ft fn
$ns flush-trace
close $ft
close $fn
exec nam mulicast.nam &
exit 0
}
#创建网络拓扑结构
#创建20个结点
for {set i 0} {$i<12} {incr i} {
set n($i) [$ns node]
}
#创建节点间的连接
$ns duplex-link $n(9) $n(8) 10Mb 10ms DropTail
$ns duplex-link $n(9) $n(7) 10Mb 10ms DropTail
$ns duplex-link $n(8) $n(10) 10Mb 10ms DropTail
$ns duplex-link $n(7) $n(10) 10Mb 10ms DropTail
$ns duplex-link $n(10) $n(0) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(6) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(4) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(5) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(3) 10Mb 10ms DropTail
$ns duplex-link $n(6) $n(2) 10Mb 10ms DropTail
$ns duplex-link $n(6) $n(1) 10Mb 10ms DropTail
$ns duplex-link $n(4) $n(11) 10Mb 10ms DropTail
#设置组播路由策略(BST模式)
#$ns mrtproto BST
#set the RP
#set RP_($group) $n(0)
#(或者DM模式)
DM set CacheMissMode dvmrp
set mproto DM
set mrthandle [$ns mrtproto $mproto]
#创建业务流
set udp1 [new Agent/UDP]
#设置结点10为服务器,负责发送数据包
$ns attach-agent $n(9) $udp1
$udp1 set dst_addr_ $group
$udp1 set dst_port_ 0
set src1 [new Application/Traffic/CBR]
$src1 set fid_ 1
$src1 set packetSize_ 210
$src1 set rate_ 448k
$src1 attach-agent $udp1
set rcvr [new Agent/LossMonitor]
#创建触发事件
$ns at 1.0 "$src1 start"
$ns at 1.5 "$n(1) join-group $rcvr $group"
$ns at 2.0 "$n(2) join-group $rcvr $group"
$ns at 2.5 "$n(3) join-group $rcvr $group"
$ns at 3.0 "$n(5) join-group $rcvr $group"
$ns at 3.5 "$n(4) join-group $rcvr $group"
$ns at 4.0 "$n(11) join-group $rcvr $group"
$ns at 5.0 "$src1 stop"
$ns at 5.0 "finish"
$ns run
下图为组播过程动态图形,从这里面可以看出对于每一个加入组播组的节点,我们都是发送1个颜色的数据包,也可以认为源节点10只发送了一个数据包。从下图中我们也可以发现组播路由树为9-7-10-0-6-1和9-7-10-0-6-2。
图2.组播过程
单播程序:
#比较简单的单播tcl程序
set ns [new Simulator]
set ft [open unicast.tr w]
$ns trace-all $ft
set fn [open unicast.nam w]
$ns namtrace-all $fn
$ns color 1 Red
$ns color 2 Green
$ns color 3 Black
$ns color 4 Blue
$ns color 5 Grey
$ns color 6 Yellow
proc finish {} {
global ns ft fn
$ns flush-trace
close $ft
close $fn
exec nam unicast.nam &
exit 0
}
#创建网络拓扑结构
for {set i 0} {$i<12} {incr i} {
set n($i) [$ns node]
}
$ns duplex-link $n(9) $n(8) 10Mb 10ms DropTail
$ns duplex-link $n(9) $n(7) 10Mb 10ms DropTail
$ns duplex-link $n(8) $n(10) 10Mb 10ms DropTail
$ns duplex-link $n(7) $n(10) 10Mb 10ms DropTail
$ns duplex-link $n(10) $n(0) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(6) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(4) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(5) 10Mb 10ms DropTail
$ns duplex-link $n(0) $n(3) 10Mb 10ms DropTail
$ns duplex-link $n(6) $n(2) 10Mb 10ms DropTail
$ns duplex-link $n(6) $n(1) 10Mb 10ms DropTail
$ns duplex-link $n(4) $n(11) 10Mb 10ms DropTail
#创建业务流
set udp1 [new Agent/UDP]
$ns attach-agent $n(9) $udp1
$udp1 set fid_ 1
set src1 [new Application/Traffic/CBR]
$src1 set packetSize_ 210
$src1 set rate_ 448k
$src1 attach-agent $udp1
set rcvr1 [new Agent/Null]
$ns attach-agent $n(1) $rcvr1
$ns connect $udp1 $rcvr1
set udp2 [new Agent/UDP]
$ns attach-agent $n(9) $udp2
$udp2 set fid_ 2
set src2 [new Application/Traffic/CBR]
$src2 set packetSize_ 210
$src2 set rate_ 448k
$src2 attach-agent $udp2
set rcvr2 [new Agent/Null]
$ns attach-agent $n(2) $rcvr2
$ns connect $udp2 $rcvr2
set udp3 [new Agent/UDP]
$ns attach-agent $n(9) $udp3
$udp3 set fid_ 3
set src3 [new Application/Traffic/CBR]
$src3 set packetSize_ 210
$src3 set rate_ 448k
$src3 attach-agent $udp3
set rcvr3 [new Agent/Null]
$ns attach-agent $n(3) $rcvr3
$ns connect $udp3 $rcvr3
set udp5 [new Agent/UDP]
$ns attach-agent $n(9) $udp5
$udp5 set fid_ 4
set src5 [new Application/Traffic/CBR]
$src5 set packetSize_ 210
$src5 set rate_ 448k
$src5 attach-agent $udp5
set rcvr5 [new Agent/Null]
$ns attach-agent $n(5) $rcvr5
$ns connect $udp5 $rcvr5
set udp4 [new Agent/UDP]
$ns attach-agent $n(9) $udp4
$udp4 set fid_ 5
set src4 [new Application/Traffic/CBR]
$src4 set packetSize_ 210
$src4 set rate_ 448k
$src4 attach-agent $udp4
set rcvr4 [new Agent/Null]
$ns attach-agent $n(4) $rcvr4
$ns connect $udp4 $rcvr4
set udp11 [new Agent/UDP]
$ns attach-agent $n(9) $udp11
$udp11 set fid_ 6
set src11 [new Application/Traffic/CBR]
$src11 set packetSize_ 210
$src11 set rate_ 448k
$src11 attach-agent $udp11
set rcvr11 [new Agent/Null]
$ns attach-agent $n(11) $rcvr11
$ns connect $udp11 $rcvr11
#创建触发事件
$ns at 1.5 "$src1 start"
$ns at 2.0 "$src2 start"
$ns at 2.5 "$src3 start"
$ns at 3.0 "$src5 start"
$ns at 3.5 "$src4 start"
$ns at 4.0 "$src11 start"
$ns at 5.0 "$src1 stop"
$ns at 5.0 "$src2 stop"
$ns at 5.0 "$src3 stop"
$ns at 5.0 "$src5 stop"
$ns at 5.0 "$src4 stop"
$ns at 5.0 "$src11 stop"
$ns at 5.0 "finish"
$ns run
比较单播与组播的图形,可以很清楚的看出来,在单播中对于每一个要发送数据的节点,都采用了从源点重新发送数据包的方法,从而我们可以看到到达每个节点的数据包都是采用不同的着色。
图3.单播过程
#组播汇合节点0的负载改变情况-awk程序
BEGIN{
init=0;
i=0;
}
{
action=$1;
time=$2;
fromnode=$3;
pktsize=$6;
#此计算组播节点0负载变化情况的方法感觉不很理想,呵呵
if(action=="-" && fromnode==0)
{
pkt_byte_sum[i+1]=pkt_byte_sum[i]+pktsize;
if(init==0)
{
start_time=time;
#start_time=0;
init=1;
}
end_time[i]=time;
i++;
}
}
END{
for(j=1;j
{
if(end_time[j]-start_time!=0){
sendsize=pkt_byte_sum[j]/(end_time[j]-start_time)*8/1000;
#kbps
printf("%f %f\n",end_time[j],sendsize);
}
}
}
从下图可以看出组播汇合节点0的负载改变情况,在单播中,即下图红线所示,节点0在加入多个接收节点时,节点0的负载不断上升,而对于组播则相对缓慢。显示了组播的特点。降低汇合点的负载。
图4.节点0负载
#组播源业务流发送量-awk程序
BEGIN{
init=0;
i=0;
}
{
action=$1;
time=$2;
fromnode=$3;
pktsize=$6;
if(action=="+" && fromnode==9)
{
pkt_byte_sum[i+1]=pkt_byte_sum[i]+pktsize;
#业务数据开始发送时间
if(init==0)
{
start_time=time;
init=1;
}
#当前截至时间
end_time[i]=time;
i++;
}
}
END{
#统计随时间变化的业务发送量
for(j=1;j
{ if(end_time[j]-start_time!=0){
sendsize=pkt_byte_sum[j]/(end_time[j]-start_time)*8/1000;
}
#kbps
printf("%f %f\n",end_time[j],sendsize);
}
}
下图是源节点业务流发送量,本例中源节点即为节点9,可以看到在单播过程中,即红线所示,其源点发送量是直线上升状态,也可以很容易理解,因为在单播过程中每加入一个节点,都要从源点新增一份数据进行发送。
而对于组播,即使不断有节点加入到组播组中,其源点发送量也不会改变,因为源点始终都是发送一份数据包。
图5.源节点9发送量
#节点10到节点0的链路吞吐量-awk程序
BEGIN{
#业务开始时间
start_time=1.0;
i=0;
}
{
action=$1;
time=$2;
fromnode=$3;
tonode=$4;
pktsize=$6;
if(action=="r" && fromnode==10 && tonode==0) {
#截至当前时间从节点10流向节点0的数据量
pkt_byte_sum[i+1]=pkt_byte_sum[i]+pktsize;
#当前截至时间
end_time[i+1]=time; i++;}}
END {
#统计随时间变化的链路吞吐量
for(j=1;j<=i;j++)
{ throughput=pkt_byte_sum[j]/(end_time[j]-start_time)*8/1000;
#kbps
printf("%f %f\n",end_time[j],throughput);}}
下图为节点10到节点0的链路吞吐量,在单播过程中,分析拓扑结构可以知道,当节点加入过程中,所有的数据包都是要经过节点10到0,所示单播会导致链路吞吐量急剧增加,而组播的特点使节点10到0始终都是传送一份数据包,即使多个节点加入组播组中也不会加大链路的吞吐量。
图6.节点9-10链路吞吐量
#节点9到节点1的延迟-awk程序
BEGIN{
highest_packet_id=0;
}
{ action=$1;
time=$2;
tonode=$4;
packet_id=$12;
#记录当前最大的数据包id
if(packet_id>highest_packet_id)
highest_packet_id=packet_id;
#记录数据包的开始发送时间
if(start_time[packet_id]==0)
start_time[packet_id]=time;
#记录数据包的接收时间
if(action=="r" && tonode==1)
end_time[packet_id]=time;
}
END{
#统计每个数据包的开始时间和延迟
for(packet_id=0;packet_id<=highest_packet_id;packet_id++)
{
packet_duration=end_time[packet_id]-start_time[packet_id];
if(packet_duration>0 )
{
printf("%f %f\n",start_time[packet_id],packet_duration);
}
}
}
下图为节点9到节点1的延迟,单播过程对于节点的延迟有很大的影响,而组播则几乎没有什么影响。
图7.节点9-1的延迟