基于NS2的组播路由协议实现及仿真

网络仿真实验:基于NS2的组播路由协议实现及仿真

ns2基本语法

对于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示例

2组播实现过程分析

在发送者和每一接收者之间实现点对多点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包。它提高了数据传送效率。减少了骨干网络出现拥塞的可能性。

单播(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的延迟

 

你可能感兴趣的:(基于NS2的组播路由协议实现及仿真)