日志标题:ns教程中译版——第一章
发表时间:2005-6-8 20:02:47
这是最初由Marc Greis编写的ns2教程,浅显易懂,生动有趣,十分适合初学者入门,试着翻译一下,希望能以此督促自己学好ns。其中有错误或不地道的地方还请大家多多批评指正!
本教程的英文网址为:
http://www.isi.edu/nsnam/ns/tutorial/强烈推荐。
一、简介
本教程最初由Marc Greis编写,现在由VINT维护并加以扩充。
既然你找到这个网页,应该已经知道了NS是什么并且知道从哪里得到NS,如果你还不知道,我建议你去http://netweb.usc.edu/vint/或http://www.isi.edu/nsnam/ns/index.html看看。
另外,本教程介绍的是NS2,至于NS1,尽管在NS2中有向后兼容的库,但两者还是很不同。
教程的目的在于使NS和NAM新手更容易入门,为NS创建自己的仿真流程并逐渐为NS加入新功能。我发现了一些文档,它们对已经熟悉了NS基本特性的使用者很有帮助,但对于新手可能过于冗长以至于从中找到有用的信息十分困难。在这个教程中,我会帮助你练习几个简单的例子,然后逐渐学习NS更多的特性。最终的目标是,经过短时间的学习后,你能够有效的使用NS并能够找到更多的有用信息。为此目的我还会告诉你我是怎样找到教程里的这些信息的,因此你不但学到怎样使用NS,还学到如何使用它的文档。
Web可能是这类教程的最好载体了,因为它不仅可以为例子加入图片(甚至是动画),而且如果你没有时间自己敲入例子代码的话,你可以直接从网上下载(尽管我建议你亲自敲入至少最开始几个例子)。我选择了基于frame的网页设计,这样更利于浏览,如果你觉得frame占用了过多的空间,可以选择一个没有frame的版本浏览。
请理解我不能在这样一个简单的教程中给出一个完整的ns参考手册,或者一个完整的tcl或c++教程,同样关于ns安装上的一些问题,这里也无能为力,对于这些问题,有更好的资源,这些将在第二节讲到。
如果有任何建议、bug、问题、新的例子,都可以发email到ns的userlist mailto:ns-users@isi.edu 。
日志标题:ns教程中译版——第二章
发表时间:2005-6-8 20:02:05
二、寻找文档
在本节,我将给出一些和ns及相关包的文档资源,如果你知道其他相关资源,或这里哪个链接过期,请email到ns的userlist。
1. Ns和nam的文档资源
下面是在UCB的ns和nam主站的文档
Ns手册 http://www.isi.edu/nsnam/ns/ns-documentation.html
Html版本的ns手册 http://www.isi.edu/nsnam/ns/ns-documentation.html
http://www.isi.edu/nsnam/ns/ns-man.html
http://www.isi.edu/nsnam/nam/nam.ps
http://www.isi.edu/nsnam/nam/nam.html
http://www.isi.edu/nsnam/ns/tutorial/nam.txt
http://www.isi.edu/nsnam/ns/ns-documentation.html
http://www.isi.edu/nsnam/ns/ns-problems.html
http://www.isi.edu/nsnam/ns/ns-lists.html
http://www.isi.edu/nsnam/dist/archive/ns-users/
2. tcl文档
tcl相当简单,如果你学过其他一些编程语言,在学习的过程中就可以学会简单流程所需的大部分tcl知识,尽管如此,我还是给出一些有趣的网站链接,给那些不愿买一本tcl教课书的雄心勃勃的人。
http://www.elf.org/tcltk-man-html/contents.htm
ftp://ftp.scriptics.com/pub/welch
ftp://ftp.tns.lcs.mit.edu/pub/otcl/doc/tutorial.html
http://www.yahoo.com/Computers_and_Internet/Programming_Languages/Tcl_Tk/
3. C++文档
请注意,如果你不需要给ns加入新功能,你甚至不需要任何C++编程知识。
http://www.research.att.com/~bs/3rd.html
http://www.research.att.com/~bs/
http://www.swcp.com/~dodrill/cppdoc/cpplist.htm
http://www.cygnus.com/misc/wp/
http://www.yahoo.com/Computers_and_Internet/Programming_Languages/C_and_C__/
15:51 | 添加评论 | 阅读评论 (1) | 发送消息 | 固定链接 | 引用通告 (0) | 写入博客 | 学术研究
ns教程中译版第3、4章
日志标题:ns教程中译版——第三章
发表时间:2005-6-8 20:18:04
三、基础知识
1. 下载、安装ns和nam
可以从各个软件包开始构建ns,也可以下载一个allinone包进行构建,我建议你从allinone包开始,尤其是当你不确认到底需要哪些包的时候。Allinone包的缺点在于它的大小,由于包含了一些当你编译完ns和nam之后就不在需要的东西。尽管如此,allinone仍然是一个第一次实验的好的起点,之后你可以转而使用单独的包安装。
注意,allinone包只在unix系统下工作。
你可以从http://www.isi.edu/nsnam/ns/ns-build.html下载安装包,在安装过程中有任何问题,可以参考http://www.isi.edu/nsnam/ns/ns-problems.html,如果这也解决不了问题,去mailing list看看:http://www.isi.edu/nsnam/ns/ns-lists.html。
当安装完成,你必须确保路径指向“ns-allinone/bin”,在其中有连接指向ns-2目录和nam-1目录下有ns和nam的可执行程序,或者把路径直接设到含有ns和nam可执行程序的目录。
在某些系统中,你还必须确保ns可以找到libotcl.so。如果你安装了allinone,这个库应该在ns-allinone/otcl目录。在Solaris系统,你需要把这个目录加到'LD_LIBRARY_PATH'环境变量中。对于其它系统,参考http://www.isi.edu/nsnam/ns/ns-problems.html,mailing list,或你自己的unix社区。
2. 启动ns
使用'ns
其它的一切都取决于tcl脚本,它可能向标准输出打印一些信息,可能创建一个trace文件,或者启动nam观看仿真图形。这些方法将在后面陆续讨论。
3. 启动nam
可以使用'nam
日志标题:ns教程中译版——第四章
发表时间:2005-6-9 15:53:04
四、第一个TCL脚本
在这一部分,你将编写一个仿真一简单拓扑的TCL脚本。你将学会如何建立结点和连接,如何从一个结点向另一个结点发送数据,如何管理队列以及如何从仿真脚本中启动NAM显示你的仿真。
1、如何开始
我们将编写一个简单的“模版”以后还可以使用。你可以在任何文本编辑器中编辑TCL脚本,如JOE或EMACS。我建议你给这第一个脚本起名为EXAMPLE1.tcl。
首先,我们需要创建一个仿真器对象(simulator object),用下面的命令:
set ns [new Simulator]
现在打开一个可写文件,供保存nam trace的数据之用。
set nf [open out.nam w]
$ns namtrace-all $nf
第一行打开名字为“out.nam”的可写(w)文件,并分配它一个文件句柄nf。在第二行,我们告诉仿真器对象我们把所有仿真数据写入这个文件。
下一步加入“结束过程”:finish,来关闭跟踪文件并启动nam。
proc finish{}{
global ns nf
$ns flush-trace
close $nf
exec nam out.nam &
exit 0
}
实际上现在你不用完全明白上面的代码,当你看到上面代码做了什么,你就会比较清楚了。
下面一行告诉仿真器对象在仿真时间5.0秒后执行finish过程。
$ns at 5.0 "finish"
只看一下上面的代码你可能就知道了它的用处。ns为你提供了十分简单的方法来调度事件:使用at命令。
最后一行用来启动仿真。
$ns run
你现在就可以保存文件然后用命令ns example1.tcl来运行它,尽管你会得到一个错误信息:“ nam: empth trace file out.nam”,因为现在你还没有定义任何对象如节点或链路等。我们在第2节讲对象的定义,第3节讲事件的定义。
你必须使用本节的代码作为其它节的开始。可以从
http://www.isi.edu/nsnam/ns/tutorial/examples/template.tcl下载代码。
2、两个节点和一条链路
在这一节我们将定义一个十分简单的拓扑结构,它包含两个节点和一条连接他们的链路。下面两行定义了两个节点。注意,本节的代码应该加到$ns run之前,或者在$ns at 5.0 "finish"之前更好。
set n0 [$ns node]
set n1 [$ns node]
命令$ns node 创建一个新的节点对象。上面的代码创建两个节点并分别命名为n0和n1。
下面一行用于连接两个节点。
$ns duplex-link $n0 $n1 1Mb 10ms DropTail
这一行告诉仿真器对象用带宽为1Mbps的双工链路连接n0和n1节点,链路时延为10ms,使用DropTail队列。
现在可以保存文件并运行:ns example1.tcl。将自动启动nam并显示象下面的图形。
可以在http://www.isi.edu/nsnam/ns/tutorial/examples/example1a.tcl下载代码。
3、发送数据
当然这个例子还不十分令人满意,因为你只能看一看拓扑结构,但是没有任何事情发生,所以下面我们从n0向n1发送一些数据。在ns中,数据通常是从一个代理(agent)发往另一个代理。因此,下一步就是要创建两个代理对象,一个用于从n0发送数据,另一个用于在n1中接收数据。
#Create a UDP agent and attach it to node n0
set udp0 [new Agent/UDP]
$ns attach-agent $n0 $udp0
#Create a CBR traffic source and attach it to udp0
set cbr0 [new Application/Traffic/CBR]
$cbr0 set packetSize_ 500
$cbr0 set interval_ 0.005
$cbr0 attach-agent $udp0
这段代码创建了一个UDP代理并附加到节点n0上,然后为UDP代理附加一个CBR业务发生器。CBR表示恒定比特率(constant bit rate)。第7,8行很好理解,设定包大小为500字节,包的发送间隔为0.005秒。在ns手册中可以找到和每个代理相关的参数。http://www.isi.edu/nsnam/ns/doc/index.html
下面创建一个空代理(NULL Agent)作为业务流的终点,把它附加给n1
set null0 [new Agent/Null]
$ns attach-agent $n1 $null0
现在必须把两个代理连接起来:
$ns connect $udp0 $null0
现在我们要告诉CBR代理什么时候开始发送数据,什么时候结束。注意:最好把下面几行放在紧挨着$ns at 5.0 "finish"前面。
$ns at 0.5 "$cbr0 start"
$ns at 4.5 "$cbr0 stop"
现在可以保存文件并开始仿真,当你点击nam上的play按钮,将看到在仿真0.5秒后,n0开始向n1发送数据。
建议你作一些nam和Tcl脚本的试验,你可以在nam窗口中点击任何一个数据包,可以直接点击链路,得到一些带有统计信息的图形。也可以改变packetsize_和interval_,看看发生了什么。
编写这个Tcl脚本所需的大部分知识来自于tcl/ex/目录下的例子,关于CBR代理的参数,我是从ns手册中找到的。
15:52 | 添加评论 | 发送消息 | 固定链接 | 引用通告 (0) | 写入博客 | 学术研究
ns教程中译版第5、6章
日志标题:ns教程中译版——第五章
发表时间:2005-6-9 15:59:57
五、让仿真更有趣
在这一节我们将定义一个带有4个节点的拓扑结构,其中一个节点作为路由器,转发另两个节点向第4个节点发送的数据。我们将学习如何区分两条数据流以及如何监控一个队列,看看队列有多满,有多少包被丢弃。
1、拓扑结构
和以前一样,第一步是定义拓扑结构。你应该以前一节的代码为模板建立一个文件命名为example2.tcl。这些代码是很相似的。你每次都要创建一个仿真器对象,启动仿真器,打开跟踪文件,启动nam,定义结束过程关闭文件等。
现在加入创建4个节点的命令:
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]
set n3 [$ns node]
下面一段代码在节点之间创建3条双工链路:
$ns duplex-link $n0 $n2 1Mb 10ms DropTail
$ns duplex-link $n1 $n2 1Mb 10ms DropTail
$ns duplex-link $n3 $n2 1Mb 10ms DropTail
现在就可以保存文件并启动仿真,在nam中看到的拓扑结构可能有些难看,可以点击re-layout按钮调整一下,但是下面的语句可以对节点的放置进行更多控制:
$ns duplex-link-op $n0 $n2 orient right-down
$ns duplex-link-op $n1 $n2 orient right-up
$ns duplex-link-op $n2 $n3 orient right
现在对应nam中显示的图形,你应该可以知道上面3行代码的作用了。
注意,现在nam自动放置已经失效了,因为你选择了自己控制摆放。链路方向的选项有:right,left,up,down以及他们的组合。你以后可以试验一下这些设置,但现在就暂时这个样子吧。
2、事件
现在创建两个带有CBR业务流的UDP代理,并附加给n0和n1。然后创建Null代理,附加给n3
#Create a UDP agent and attach it to node n0
set udp0 [new Agent/UDP]
$ns attach-agent $n0 $udp0
# Create a CBR traffic source and attach it to udp0
set cbr0 [new Application/Traffic/CBR]
$cbr0 set packetSize_ 500
$cbr0 set interval_ 0.005
$cbr0 attach-agent $udp0
#Create a UDP agent and attach it to node n1
set udp1 [new Agent/UDP]
$ns attach-agent $n1 $udp1
# Create a CBR traffic source and attach it to udp1
set cbr1 [new Application/Traffic/CBR]
$cbr1 set packetSize_ 500
$cbr1 set interval_ 0.005
$cbr1 attach-agent $udp1
set null0 [new Agent/Null]
$ns attach-agent $n3 $null0
两个CBR代理必须和Null代理相连。
$ns connect $udp0 $null0
$ns connect $udp1 $null0
我们让第一个CBR代理在0.5秒开始发送在4.5秒停止,第二个CBR代理在1.0秒开始在4.0秒停止。
$ns at 0.5 "$cbr0 start"
$ns at 1.0 "$cbr1 start"
$ns at 4.0 "$cbr1 stop"
$ns at 4.5 "$cbr0 stop"
当你开始仿真后,你会发现从n0到n2和从n1到n2的流量加起来超过了n2到n3链路的容量。简单的计算一下就可以证实:以每秒200个包的速率发送,每个包500字节,相当于0.8Mbps,两条链路加在一起为1.6Mbps,而n2到n3的链路容量为1.0Mbps。显然有些包将被丢弃。但是哪些包被丢弃呢?两个数据流都是黑色的,只有通过在nam中点击每个包才能发现对这个包进行了哪些处理。下面一节我们将学习如何区分不同的数据流。
3、标记数据流
将下面两行代码加入到CBR代理的定义中。
$udp0 set class_ 1
$udp1 set class_ 2
现在加入下面的代码,由于这是仿真器建立的一部分,最好把它放在紧挨着仿真器创建代码之后。
$ns color 1 Blue
$ns color 2 Red
这段代码使不同的流使用不同的颜色标记。
现在开始仿真,可以看到nam中一条数据流使用红色标记而另一条使用蓝色。仿真进行一会儿之后,你可能会发现两个数据流在n2到n3上的流量并不是很公平。在下一节我们将学习如何查看链路队列。
4、监控队列
你只需要加入下面一行代码就可以对n2-n3链路队列进行监控
$ns duplex-link-op $n2 $n3 queuePos 0.5
再次启动仿真,将出现和下面相似的图形。
你现在就可以看到队列中的数据包了。过了一会儿之后,你甚至可以看到被丢弃的包,对于DropTail队列得不到太多的公平性。因此我们将设法增进队列的公平性,可以在n2-n3链路上使用SFQ(统计公平队列)来增加公平性。下面将改变n2-n3链路的定义:
$ns duplex-link $n3 $n2 1Mb 10ms SFQ
排队现在应该更加公平一些,丢弃同样数量蓝色和红色包。
日志标题:ns教程中译版——第六章
发表时间:2005-6-16 11:14:47
六、动态网络
在这一节我们将学习一个动态网络,其中路由器根据链路故障自动作出调整。在这个过程中我将告诉你如何把许多节点放到数组中而不用为每个节点起名字。
1、创建一个大的拓扑结构
建议将这个tcl脚本例子命名为example3.tcl。
向以往一样,必须先建立拓扑结构,尽管这次我们用不同的方式,对于大的拓扑结构这中方法会更方便。下面的代码创建7个节点并将它们保存在数组n()中。
for {set i 0} {$i < 7} {incr i} {
set n($i) [$ns node]
}
你肯定在别的什么编程语言中见过for循环,我相信你一看到它就知道它是干什么的。注意其中的数组,和Tcl其他变量一样,不需要事先声明。
现在我们要把这些节点连接起来构造一个环形拓扑结构。下面的代码可能有些复杂。
for {set i 0} {$i < 7} {incr i} {
$ns duplex-link $n($i) $n([expr ($i+1)%7]) 1Mb 10ms DropTail
}
这个for循环把数组中的节点一个接一个的连接起来,最后一个节点和第一个节点连接,我们使用取模运算达到这个目的。
现在当你运行这个脚本时,拓扑结构看上去可能有些奇怪,但是当你re-layout之后,拓扑结构应该和下面的图差不多。
2、链路失效
下面一步从n(0)向n(3)发送一些数据
#Create a UDP agent and attach it to node n(0)
set udp0 [new Agent/UDP]
$ns attach-agent $n(0) $udp0
# Create a CBR traffic source and attach it to udp0
set cbr0 [new Application/Traffic/CBR]
$cbr0 set packetSize_ 500
$cbr0 set interval_ 0.005
$cbr0 attach-agent $udp0
set null0 [new Agent/Null]
$ns attach-agent $n(3) $null0
$ns connect $udp0 $null0
$ns at 0.5 "$cbr0 start"
$ns at 4.5 "$cbr0 stop"
上面的代码对你应该比较熟悉了。和前面一部分唯一不同的地方在于现在我们必须使用数组中的节点对象。
如果你启动脚本,你会看到业务流选择了一条n0到n3最短的路径:通过n1和n2的路径,正如我们所期望。现在我们加入一些有趣的特性。我们让节点n1和n2之间的链路失效1秒钟。
$ns rtmodel-at 1.0 down $n(1) $n(2)
$ns rtmodel-at 2.0 up $n(1) $n(2)
上面两行代码理解起来可能不是特别困难。现在你可以再次运行脚本,你将看到在1.0秒和2.0秒之间,n1-n2链路将失效,从n0发来的数据也将丢失。
现在我们来学习如何使用动态路由解决这个问题。在Tcl脚本最前面,创建仿真器对象之后,加入下面一行代码:
$ns rtproto DV
再次启动仿真,现在你将发现最开始许多小的数据包在网络中传送,如果你把nam的速度放慢使你可以点击到其中的一个包,你将发现他们是rtProtoDV包,专门用来在节点之间交换路由信息。当链路在1.0秒失效时,路由将相应改变,业务流也被重新路由到n6,n5,n4链路上。
15:54 | 添加评论 | 发送消息 | 固定链接 | 引用通告 (0) | 写入博客 | 学术研究
ns教程中译版第7、8章
日志标题:ns教程中译版——第七章
发表时间:2005-6-16 11:16:16
七、为ns写一个新协议
本章我将给出一个可以在ns中实现的新协议的例子。在试着作这件事之前,你需要对ns相当熟悉,而且,需要有一些C++的知识。同时,应该读完ns手册中的3.1—3.3,以帮助理解Tcl和C++的接口方式。
本节的代码实现了一个Ping协议(灵感来自于ns手册中的9.6“ping requestor”)。具体说:一个节点可以向另一个节点发送一个包,那个节点立即将包送回,于是可以以此计算rtt。
我知道这里的代码可能不是最好的实现方式,而且我确信这段代码可以改进,但我希望它容易理解,这才是我真正的目的。如果有任何建议,发email到mailto:ns-users@isi.edu。
1、头文件
在新的头文件“ping.h”中,我们首先需要定义一个新的ping协议包的头部,用来保存相关数据。
struct hdr_ping {
char ret;
double send_time;
};
其中字符型的ret字段,在包被送往ping的目的节点时置为0,当包被送回时,被置为1。双精度型的send_time,记录当包被发送时的时间戳,然后被用于计算rtt。
下面一段代码声明了一个PingAgent类,它时Agent类的子类。
class PingAgent : public Agent {
public:
PingAgent();
int command(int argc, const char*const* argv);
void recv(Packet*, Handler*);
protected:
int off_ping_;
};
下一节,我将给出实现这里声明的构造函数PingAgent()、函数command()、函数recv()的C++代码。
整型变量off_ping_将被用于方位ping包头。注意,在对象本地有效的变量通常带一个_后缀。可以从http://www.isi.edu/nsnam/ns/tutorial/examples/ping.h 下载完整的头文件。(我建议你下载并浏览一下这个文件,因为在这里写出的代码并不完整)
2、C++代码
首先必须定义C++和TCL之间的连接关系。现在并不需要你完全理解下面这段代码,但是读读ns手册3.1-3.3会有很大帮助,如果你还没有阅读这一部分。
static class PingHeaderClass : public PacketHeaderClass {
public:
PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping",
sizeof(hdr_ping)) {}
} class_pinghdr;
static class PingClass : public TclClass {
public:
PingClass() : TclClass("Agent/Ping") {}
TclObject* create(int, const char*const*) {
return (new PingAgent());
}
} class_ping;
下面一段代码是PingAgent类的构造函数实现,它绑定了Tcl和C++中都要访问的变量:
PingAgent::PingAgent() : Agent(PT_PING)
{
bind("packetSize_", &size_);
bind("off_ping_", &off_ping_);
}
当在Tcl中执行PingAgent 类命令时,Command函数被调用。在我们的例子中是:'$pa send',假设pa是Agent/Ping的一个实例,因为我们要从一个ping agent发送包到另一个ping agent。一般,你需要解析传到command函数中的命令,如果没有匹配的命令,你需要把这个命令连同它的参数一同返回给基类处理函数,本例中是'Agent::command()',下面的代码看上去很长,主要原因是其中有很多注释:
int PingAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "send") == 0) {
// Create a new packet
Packet* pkt = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Set the 'ret' field to 0, so the receiving node knows
// that it has to generate an echo packet
hdr->ret = 0;
// Store the current time in the 'send_time' field
hdr->send_time = Scheduler::instance().clock();
// Send the packet
send(pkt, 0);
// return TCL_OK, so the calling function knows that the
// command has been processed
return (TCL_OK);
}
}
// If the command hasn't been processed by PingAgent()::command,
// call the command() function for the base class
return (Agent::command(argc, argv));
}
'recv()'函数定义了当收到一个包时进行的处理,如果ret字段是0,将回送一个具有相同send_time字段值,但ret字段是1的包,如果ret字段是1,将调用一个tcl函数处理这个事件。(这个函数需要用户在Tcl中定义,对于使用2.1b2的用户请注意,请把其中的'Address::instance().NodeShift_[1]' 改为'NODESHIFT')
void PingAgent::recv(Packet* pkt, Handler*)
{
// Access the IP header for the received packet:
hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_);
// Access the Ping header for the received packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Is the 'ret' field = 0 (i.e. the receiving node is being pinged)?
if (hdr->ret == 0) {
// Send an 'echo'. First save the old packet's send_time
double stime = hdr->send_time;
// Discard the packet
Packet::free(pkt);
// Create a new packet
Packet* pktret = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);
// Set the 'ret' field to 1, so the receiver won't send another echo
hdrret->ret = 1;
// Set the send_time field to the correct value
hdrret->send_time = stime;
// Send the packet
send(pktret, 0);
} else {
// A packet was received. Use tcl.eval to call the Tcl
// interpreter with the ping results.
// Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'
// has to be defined which allows the user to react to the ping
// result.
char out[100];
// Prepare the output to the Tcl interpreter. Calculate the round
// trip time
sprintf(out, "%s recv %d %3.1f", name(),
hdrip->src_.addr_ >> Address::instance().NodeShift_[1],
(Scheduler::instance().clock()-hdr->send_time) * 1000);
Tcl& tcl = Tcl::instance();
tcl.eval(out);
// Discard the packet
Packet::free(pkt);
}
}
你可以从http://www.isi.edu/nsnam/ns/tutorial/examples/ping.cc 下载完整的文件,最有趣的部分应该是'tcl.eval()'函数了,在这里,一个Tcl的recv函数被调用,调用参数是被ping的节点id和以毫秒计算的rtt值。在本章第四节将讨论这个函数的代码需要如何编写。但是在此之前,必须修改其他一些文件以使ns可以重新编译。
3、必要的修改
你必须修改一些ns源代码才能加入一个新的Agent,尤其是当你想使用新的包格式的时候。我建议你总是对自己修改过的代码进行注释,并使用#ifdef 等编译开关,这样你可以方便的去掉自己作的修改,或使之适应新版本的ns。
我们将需要一个用于ping agent的新的包格式,因此,第一步就是要修改packet.h。在那里,你可以发现许多协议包id的定义,如PT_TCP, PT_TELNET等,加入一个新定义:PT_PING,在我修改的版本,这个部分代码的最后几行是:
enum packet_t {
PT_TCP,
PT_UDP,
......
// insert new packet types here
PT_TFRC,
PT_TFRC_ACK,
PT_PING, // packet protocol ID for our ping-agent
PT_NTYPE // This MUST be the LAST one
};
(在新版本的ns中,PT_PING已经作为ns的一部分在这个文件中了,所以,这个例子没什么要作的,可以自己试着搞个别的什么名字的协议都可以。)
你还需要修改在同一文件中的p_info()结构,使它包含ping:
class p_info {
public:
p_info() {
name_[PT_TCP]= "tcp";
name_[PT_UDP]= "udp";
...........
name_[PT_TFRC]= "tcpFriend";
name_[PT_TFRC_ACK]= "tcpFriendCtl";
name_[PT_PING]="Ping";
name_[PT_NTYPE]= "undefined";
}
.....
}
注意,你需要先'make depend'一下,然后再'make',否则这两个文件可能不会被重新编译。
'tcl/lib/ns-default.tcl'这个文件也需要修改,这个文件中定义了Tcl所有默认取值,加入下面一行,为ping agent设置默认包长度。
Agent/Ping set packetSize_ 64
你还需要在文件'tcl/lib/ns-packet.tcl'中为新的ping包加入一个入口,看上去象:
{ SRMEXT off_srm_ext_}
{ Ping off_ping_ }} {
set cl PacketHeader/[lindex $pair 0]
最后是对'Makefile'的修改,你必须加入对'ping.o'的连接,例如:
sessionhelper.o delaymodel.o srm-ssm.o \
srm-topo.o \
ping.o \
$(LIB_DIR)int.Vec.o $(LIB_DIR)int.RVec.o \
$(LIB_DIR)dmalloc_support.o \
到此为止,你应该可以在ns目录下make了。
4、Tcl代码
现在我不打算把ping agent的完整的Tcl代码放在这,你可以从http://www.isi.edu/nsnam/ns/tutorial/examples/ping.tcl下载完整的代码,但是我会说明如何写当收到一个ping的echo包时,将被C++中recv函数调用的Tcl的recv函数。
Agent/Ping instproc recv {from rtt} {
$self instvar node_
puts "node [$node_ id] received ping answer from \
$from with round-trip-time $rtt ms."
}
这段代码应该不难理解,唯一的新东西是对基类'Agent'的成员变量'node_'的访问,以取得本agent相连的节点的id。
现在你可以试着作一些自己的试验。一个最简单的试验就是不给ret置1,你可能已经猜到将发生什么。你也可以加入一些代码,使用户可以指定ping发送的节点:'$pa send $node',而不用先把两个节点的agent连接起来,尽管乍一看有些令人迷惑,可以参考ns手册的9.6找到编写自己的agent的更多的信息。祝你好运。
日志标题:ns教程中译版——第八章
发表时间:2005-6-16 11:19:23
八、为Xgraph提供输出文件
xgraph是ns-allinone包的一个组件,用于仿真结果的图形显示。在这一部分,我们将学习如何在Tcl脚本中创建输出文件,然后把这个文件用于xgraph。在这个过程中,我们还将学习如何使用业务量生成器。
注意,我在这里使用的生成xgraph所需文件的方法只是许多方法中的一种,这种方法比较容易理解。
1、拓扑结构和业务源
首先,我们创建如下图的拓扑结构:
下面的代码对你应该很熟悉了:
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]
set n3 [$ns node]
set n4 [$ns node]
$ns duplex-link $n0 $n3 1Mb 100ms DropTail
$ns duplex-link $n1 $n3 1Mb 100ms DropTail
$ns duplex-link $n2 $n3 1Mb 100ms DropTail
$ns duplex-link $n3 $n4 1Mb 100ms DropTail
我们将把业务源附加到节点n0,n1和n2上,但首先我们编写一个函数(过程)使增加业务源更加方便:
proc attach-expoo-traffic { node sink size burst idle rate } {
#Get an instance of the simulator
set ns [Simulator instance]
#Create a UDP agent and attach it to the node
set source [new Agent/UDP]
$ns attach-agent $node $source
#Create an Expoo traffic agent and set its configuration parameters
set traffic [new Application/Traffic/Exponential]
$traffic set packet-size $size
$traffic set burst-time $burst
$traffic set idle-time $idle
$traffic set rate $rate
# Attach traffic source to the traffic generator
$traffic attach-agent $source
#Connect the source and the sink
$ns connect $source $sink
return $traffic
}
这个过程其实并不象它看上去那样复杂。它使用6个参数:node,事先创建的业务流终点sink,业务源的包长size,突发和空闲时间(用于指数分布),峰值速率。有关Expoo业务源的详细信息,请参考ns手册。
首先,过程创建一个业务源,并把它附加到节点node上,然后,创建一个Traffic/Expoo对象,设置它的参数并把它附加到业务源上,最后,连接业务源和业务终点。这个过程为我们提供了一个处理重复性工作如为许多节点附加业务源的例子。现在我们使用这个过程为n0,n1和n2附加带有不同峰值速率的业务源,然后将他们和n4上预先创建好的3个业务终点相连。
set sink0 [new Agent/LossMonitor]
set sink1 [new Agent/LossMonitor]
set sink2 [new Agent/LossMonitor]
$ns attach-agent $n4 $sink0
$ns attach-agent $n4 $sink1
$ns attach-agent $n4 $sink2
set source0 [attach-expoo-traffic $n0 $sink0 200 2s 1s 100k]
set source1 [attach-expoo-traffic $n1 $sink1 200 2s 1s 200k]
set source2 [attach-expoo-traffic $n2 $sink2 200 2s 1s 300k]
在此,我们使用Agent/LossMonitor对象作为业务终点,原因是它可以保存收到的字节数,这可以用于计算带宽。
2、在输出文件中记录数据
现在我们打开3个输出文件:
set f0 [open out0.tr w]
set f1 [open out1.tr w]
set f2 [open out2.tr w]
这些文件在最后要被关闭,我们使用修改的finish过程完成这个工作:
proc finish {} {
global f0 f1 f2
#Close the output files
close $f0
close $f1
close $f2
#Call xgraph to display the results
exec xgraph out0.tr out1.tr out2.tr -geometry 800x400 &
exit 0
}
它不仅关闭了文件,还调用xgraph显示结果。
现在我们可以写一个过程,来把数据写入到输出文件:
proc record {} {
global sink0 sink1 sink2 f0 f1 f2
#Get an instance of the simulator
set ns [Simulator instance]
#Set the time after which the procedure should be called again
set time 0.5
#How many bytes have been received by the traffic sinks?
set bw0 [$sink0 set bytes_]
set bw1 [$sink1 set bytes_]
set bw2 [$sink2 set bytes_]
#Get the current time
set now [$ns now]
#Calculate the bandwidth (in MBit/s) and write it to the files
puts $f0 "$now [expr $bw0/$time*8/1000000]"
puts $f1 "$now [expr $bw1/$time*8/1000000]"
puts $f2 "$now [expr $bw2/$time*8/1000000]"
#Reset the bytes_ values on the traffic sinks
$sink0 set bytes_ 0
$sink1 set bytes_ 0
$sink2 set bytes_ 0
#Re-schedule the procedure
$ns at [expr $now+$time] "record"
}
这个过程读取业务终点收到的字节数,然后计算带宽(MBit/s)并连同当前时间一起写入到3个输出文件中,之后重新设置接收字节为0,最后重调度它自己。
3、运行仿真
现在我们调度下面的事件:
$ns at 0.0 "record"
$ns at 10.0 "$source0 start"
$ns at 10.0 "$source1 start"
$ns at 10.0 "$source2 start"
$ns at 50.0 "$source0 stop"
$ns at 50.0 "$source1 stop"
$ns at 50.0 "$source2 stop"
$ns at 60.0 "finish"
$ns run
首先,调用record过程,以后,它自己会每隔0.5秒调度自己一次。然后3个业务源从10.0秒开始,在50秒结束。在60秒时,finish过程被调用。
当你运行仿真时,xgraph窗口将打开并显示象下面的图形:
正如你看到的,第一个数据流突发的峰值在0.1Mbit/s,第二个是0.2Mbit/s,第三个是0.3Mbit/s。现在你可以试着修改record过程中time的值。把它改为0.1看看发生了什么,再试试1.0。找到一个合适的time值对仿真流程十分重要。
注意,record过程输出的文件也可以用于gnuplot程序中。
16:09 | 添加评论 | 发送消息 | 固定链接 | 引用通告 (0) | 写入博客 | 学术研究
ns教程中译版第9章
日志标题:ns教程中译版——第九章
发表时间:2005-6-16 11:20:42
九、在ns中运行无线仿真
本节我们将学习如何使用ns中提供的无线模块进行仿真。本节包括两部分,首先我们创建并运行一个2节点的无线网络的仿真,然后我们把它扩展成一个相对复杂一些的场景。
注意,本节使用的api只在2.1b6以上版本中支持。
1、 创建简单的无线流程
我们将创建一个十分简单的无线流程。拓扑中包括两个移动节点,node_(0) node_(1)。两个节点在限定为500mX500m的范围内移动,节点开始时从两个边界出发,在仿真时间的前一半,两节点向对方移动,在后一半时间相互远离。两个节点之间建立tcp连接,当两节点进入通信范围后,开始互相交换数据包,当他们远离时,一部分数据包被丢弃。
和其他仿真一样,我们从建立一个tcl脚本作起,叫做simple-wireless.tcl,可以从http://www.isi.edu/nsnam/ns/tutorial/examples/simple-wireless.tcl下载完整的tcl脚本。
一个移动节点包括链路层模块(LL),接口队列(Ifq),Mac层,等节点用于传输的无线信道模块。对于详细的组件说明,请参考ns手册第15章第一节。在仿真开始之前,我们需要确定这些模块的类型,除此之外,我们还要定义其他的一些参数,如:天线类型,无线传播模型,adhoc路由协议等。参考下面代码中的注释,其中是每个定义的简单说明。用于定义这些参数的队列val(),不象以前的无线仿真中一样,它不是全局的。对于这些参数的细节以及有哪些参数可以定义,请参看ns手册第15章。我们从下面的一系列定义开始我们的仿真脚本。
# ====================================================================
# Define options
# ====================================================================
set val(chan) Channel/WirelessChannel ;# channel type
set val(prop) Propagation/TwoRayGround ;# radio-propagation model
set val(ant) Antenna/OmniAntenna ;# Antenna type
set val(ll) LL ;# Link layer type
set val(ifq) Queue/DropTail/PriQueue ;# Interface queue type
set val(ifqlen) 50 ;# max packet in ifq
set val(netif) Phy/WirelessPhy ;# network interface type
set val(mac) Mac/802_11 ;# MAC type
set val(rp) DSDV ;# ad-hoc routing protocol
set val(nn) 2 ;# number of mobilenodes
下面我们进入到仿真的主要部分,首先创建一个仿真器对象。
set ns_ [new Simulator]
建立trace支持,打开simple.tr文件,并调用trace-all过程。
set tracefd [open simple.tr w]
$ns_ trace-all $tracefd
然后建立一个拓扑对象用于控制节点在限定范围内移动。
set topo [new Topography]
我们刚才提到过,节点在500mX500m范围内移动,我们给拓扑对象提供一个xy范围在500mX500m的限制。
$topo load_flatgrid 500 500
拓扑现在被分为小的网格,网格的宽度默认为1个单位,可以在上面的设置最后增加第三个参数指定网格宽度。
下面我们创建一个God对象
create-god $val(nn)
援引CMU中关于God对象的文档,“God(General Operations Director)用于保存一个全局的环境、网络或节点状态信息,这些信息应该由一个观测者掌握,而不应由任何一个仿真的参与者掌握”。目前,God对象只保存网络中总共节点数和一个表格,其中保存从一个节点到另一个节点所需的最小跳数。下一跳信息一般是在仿真开始之前,通过运动模式文件加载到God对象中的,主要因为在仿真中计算这些值非常耗费时间。然而,为了使这个例子尽量简洁,我们不使用模式文件,因此也就不给God对象提供下一跳信息。如何使用模式文件给God提供下一跳信息将在本节的后半部分讨论。
create-god过程的定义在~ns/tcl/mobility/com.tcl,这个脚本创建全局唯一的God对象,除了完成一些评估功能外,God在内部被移动节点的Mac模块调用,因此,即使我们不使用God的评估功能,我们也要创建God对象。
下面,我们创建移动节点。创建节点的API被更新过,这里我们将使用新的API,注意新API在2.1b5以上版本才支持。
在创建节点之前,我们首先要对节点进行配置,节点配置API包括对寻址方式的定义(平坦/分层)、adhoc路由类型、链路层、mac层、IfQ等。配置API的定义如下:
(parameter examples)
# $ns_ node-config -addressingType flat or hierarchical or expanded
# -adhocRouting DSDV or DSR or TORA
# -llType LL
# -macType Mac/802_11
# -propType "Propagation/TwoRayGround"
# -ifqType "Queue/DropTail/PriQueue"
# -ifqLen 50
# -phyType "Phy/WirelessPhy"
# -antType "Antenna/OmniAntenna"
# -channelType "Channel/WirelessChannel"
# -topoInstance $topo
# -energyModel "EnergyModel"
# -initialEnergy (in Joules)
# -rxPower (in W)
# -txPower (in W)
# -agentTrace ON or OFF
# -routerTrace ON or OFF
# -macTrace ON or OFF
# -movementTrace ON or OFF
除了addressingType的默认值是flat外,其他参数的默认值都是NULL。
我们将使用默认的flat类型,同时打开AgentTrace 和RouterTrace开关,你也可以试验把所有的trace开关都打开。AgentTrace以AGT标志,RouterTrace以RTR标志,MacTrace以MAC标志,这些标志都在第5个字段。当打开MovementTrace开关时,将显示节点的运动,并在第2个字段以M标志。
创建移动节点的配置文件如下:
# Configure nodes
$ns_ node-config -adhocRouting $val(rp) \
-llType $val(ll) \
-macType $val(mac) \
-ifqType $val(ifq) \
-ifqLen $val(ifqlen) \
-antType $val(ant) \
-propType $val(prop) \
-phyType $val(netif) \
-topoInstance $topo \
-channelType $val(chan) \
-agentTrace ON \
-routerTrace ON \
-macTrace OFF \
-movementTrace OFF
下面我们创建两个移动节点:
for {set i 0} {$i < $val(nn) } {incr i} {
set node_($i) [$ns_ node ]
$node_($i) random-motion 0 ;# disable random motion
}
节点的随机移动在此被禁止,因为我们一会儿还要确定节点的位置和移动方向和速度。
既然我们创建了节点,我们就需要给他们确定起始位置。
#
# Provide initial (X,Y, for now Z=0) co-ordinates for node_(0) and node_(1)
#
$node_(0) set X_ 5.0
$node_(0) set Y_ 2.0
$node_(0) set Z_ 0.0
$node_(1) set X_ 390.0
$node_(1) set Y_ 385.0
$node_(1) set Z_ 0.0
node_(0)和node_(1)的起始位置分别为 (5,2)和(390,385)。
下面产生一些节点的运动。
#
# Node_(1) starts to move towards node_(0)
#
$ns_ at 50.0 "$node_(1) setdest 25.0 20.0 15.0"
$ns_ at 10.0 "$node_(0) setdest 20.0 18.0 1.0"
# Node_(1) then starts to move away from node_(0)
$ns_ at 100.0 "$node_(1) setdest 490.0 480.0 15.0"
$ns_ at 50.0 "$node_(1) setdest 25.0 20.0 15.0"这句话的意思是在50.0秒时候,节点开始向目的(25.0,20.0)运动,速度是15.0米/秒。这个API用来改变节点的方向和速度。
下面在两个节点之间建立数据流量:
# TCP connections between node_(0) and node_(1)
set tcp [new Agent/TCP]
$tcp set class_ 2
set sink [new Agent/TCPSink]
$ns_ attach-agent $node_(0) $tcp
$ns_ attach-agent $node_(1) $sink
$ns_ connect $tcp $sink
set ftp [new Application/FTP]
$ftp attach-agent $tcp
$ns_ at 10.0 "$ftp start"
这将创建一个以node_(0)为源的两个节点间的tcp连接。
然后,我们需要确定仿真结束的时间,并告诉节点reset,即节点reset它内部的网络模块。
#
# Tell nodes when the simulation ends
#
for {set i 0} {$i < $val(nn) } {incr i} {
$ns_ at 150.0 "$node_($i) reset";
}
$ns_ at 150.0001 "stop"
$ns_ at 150.0002 "puts \"NS EXITING...\" ; $ns_ halt"
proc stop {} {
global ns_ tracefd
close $tracefd
}
在150.0秒时候仿真结束,节点在这个时刻被reset,然后在稍微晚一些时候150.0002秒的时候,ns_ halt被调用。在此之前调用Stop过程,保存并关闭trace文件。
最后,是开始仿真的命令:
puts "Starting Simulation..."
$ns_ run
将这个文件保存为simple-wireless.tcl,也可以从http://www.isi.edu/nsnam/ns/tutorial/examples/simple-wireless.tcl下载一个,然后就和平常一样运行这个脚本。
在仿真运行的最后,trace的输出文件simple.tr被创建。因为我们打开了AgentTrace 和 RouterTrace开关,我们可以看到DSDV消息和TCP包分别从node _0_ 的Router和Agent 发送到node _1_。注意所有的无线仿真trace都以WL作为第一个字段开始。参考ns手册的第15章。我们可以看到TCP流从10秒时开始从node0发送,但一开始,两个节点距离较远,由于无法收到node1的回应,tcp包被丢弃。在81秒左右,两个节点开始交换路由信息,在100秒左右,第一个tcp包被node1的agent接收,同时回送一个ACK,这样,tcp连接就建立起来了。然而,随着两个节点逐渐远离,在116秒左右,连接又断开,包也又一次开始被丢弃。
2、 使用无线节点移动模式文件和流量模式文件以及其他特性
作为前半节的扩展,我们在此将仿真一个有3个节点的移动多跳网络。和以前一样,节点在一定范围内移动。然而,这次,节点移动是从一个节点移动文件中读取的,这个文件是scen-3-test,在此文件中定义了3个节点在670mX670m范围内的移动。这个文件作为ns发行的一部分,可以在~ns/tcl/mobility/scene文件夹找到,其中还有其他的移动文件。象scen-3-test这样的随机移动文件,可以通过CMU的节点移动生成器"setdest"产生,关于这个生成器的详细情况请看本教程的XI.2。
除了节点移动之外,两个节点之间的流量,同样由一个文件提供,此文件是cbr-3-test,cbr-3-test可以在~ns/tcl/mobility/scene文件夹找到。3个节点间建立起TCP随机CBR流,数据在能听到源节点的节点被接收或转发。关于更多流量建立的信息请参看cbr-3-test。这个流量模型文件也可以用CMU的TCP/CBR流量生成器脚本产生。这个话题在XI.1将继续讨论。
我们要对上一小节编写的simple-wireless.tcl脚本进行一些改造,改造之后的脚本我们叫做wireless1.tcl,此脚本可以从http://www.isi.edu/nsnam/ns/tutorial/examples/wireless1.tcl下载。除了在文件开始定义的一些变量之外,我们现在要定义更多的一些参数,如连接模式,节点模式文件,拓扑的xy范围,产生随机数的种子,仿真结束时间等,为了方便,列在下面:
set val(chan) Channel/WirelessChannel
set val(prop) Propagation/TwoRayGround
set val(netif) Phy/WirelessPhy
set val(mac) Mac/802_11
set val(ifq) Queue/DropTail/PriQueue
set val(ll) LL
set val(ant) Antenna/OmniAntenna
set val(x) 670 ;# X dimension of the topography
set val(y) 670 ;# Y dimension of the topography
set val(ifqlen) 50 ;# max packet in ifq
set val(seed) 0.0
set val(adhocRouting) DSR
set val(nn) 3 ;# how many nodes are simulated
set val(cp) "../mobility/scene/cbr-3-test"
set val(sc) "../mobility/scene/scen-3-test"
set val(stop) 2000.0 ;# simulation time
移动节点数被改为3,并使用DSR代替DSDV作为路由协议。
创建ns_之后,仿真器实例将打开一个文件wireless1-out.tr作为trace的输出,我们同时建立nam trace输出:
set tracefd [open wireless1-out.tr w] ;# for wireless traces
$ns_ trace-all $tracefd
set namtrace [open wireless1-out.nam w] ;# for nam tracing
$ns_ namtrace-all-wireless $namtrace $val(x) $val(y)
然后,在创建节点之后,将前面定义的节点移动文件和连接模式文件作为源:
#
# Define node movement model
#
puts "Loading connection pattern..."
source $val(cp)
#
# Define traffic model
#
puts "Loading scenario file..."
source $val(sc)
在scen-3-test文件中,我们能看到象下面那样的节点移动命令:
$ns_ at 50.000000000000 "$node_(2) setdest 369.463244915743 \
170.519203111152 3.371785899154"
这个和前一节说明过的一样,意思是在50秒时,节点2开始以3.37m/s的速度向(368.4,170.5)运动,我们还能看到:
$god_ set-dist 1 2 2
这是为God对象装入最短跳信息,表示节点1、2之间的最短跳数是2。提供了这个信息,就可以避免God在仿真运行时计算最短跳耗费大量时间。
Setdest程序使用random waypoint算法产生移动模型文件,此文件中已经包含了象上面一行那样的为God加载信息的语句。
一个叫做calcdest的程序,位于(~ns/indep-utilities/cmu-scen-gen/setdest/calcdest,可以用来为由其他程序产生的移动模型文件加入为God加载信息的语句。这个程序要求输入的移动模型文件符合一定格式,否则程序无法运行。如果calcdest不能处理你的模型文件,一个最简单的方法是先把模型文件输入ad-hockey程序,然后再保存一下,如果这个程序能够读入你的模型文件,它会把它保存成为calcdest可处理的格式。
Setdest和calcdest都基于无线传输范围来计算节点间的最短跳数,而忽略了其他任何实际仿真中使用的传播模型的影响,这个范围可以通过这些程序的输入参数指定或直接从模型文件中读取。
在CMU的Monarch项目中使用了God提供的链路长度信息来分析用于adhoc的最优链路长度,这些信息同样在每个包的CMUtrace中被打印出来。
CMU发现了这些信息的其他用途:
通过移动模型,分析拓扑变化速率的特性。
鉴别分裂部分的频率和大小
当通过God提供完美的邻居信息而不付出任何代价时,研究路由协议的表现。
下面加入一些语句,确定节点的初始位置。注意,目前在nam中只能看到节点的移动,而数据流则看不到。
# Define node initial position in nam
for {set i 0} {$i < $val(nn)} {incr i} {
# 20 defines the node size in nam, must adjust it according to your
# scenario size.
# The function must be called after mobility model is defined
$ns_ initial_node_pos $node_($i) 20
}
在"ns_ run"之前,给CMUTrace文件加入一些提示的文件头。
puts $tracefd "M 0.0 nn $val(nn) x $val(x) y $val(y) rp $val(adhocRouting)"
puts $tracefd "M 0.0 sc $val(sc) cp $val(cp) seed $val(seed)"
puts $tracefd "M 0.0 prop $val(prop) ant $val(ant)"
脚本其他部分保持不变。
将文件保存为wireless1.tcl,确认一下连接模型文件和移动模型文件在脚本声明的目录下,在命令行输入命令,运行脚本:
ns wireless1.tcl
运行结束时,将生成CMUTrace文件wireless1-out.tr和nam trace文件wireless1-out.nam,运行wireless1-out.nam我们可以看到3个节点在nam窗口运动,但是数据流仍然看不到。可以试验开启或关闭AgentTrace,RouteTrace,MacTrace,movementTrace看看相应结果。
节点1在节点2和0的通信范围内,于是所有向0和2发送的数据都通过1。
16:14 | 添加评论 | 发送消息 | 固定链接 | 引用通告 (0) | 写入博客 | 学术研究
ns教程中译版第10、11章
日志标题:ns教程中译版——第十章
发表时间:2005-7-11 20:16:55
十、在ns中创建有线-无线混合以及移动ip仿真
注意,本节使用的API在2.1b5中不支持,所以请使用2.1b6以上版本。
1、 创建简单的有线-无线混合场景
上一节建立的无线仿真可以支持多跳adhoc网络或wirelesslan。但是,我们可能需要对经过有线网络连接的多个无线网络进行仿真,或者说我们需要对有线-无线混合网络进行仿真。
本节,我们将对上节的简单无线仿真场景进行扩展,建立一个混合场景,数据在可移动和不可移动节点间传递。我们将修改 XI.2的脚本wireless1.tcl,重新命名为wireless2.tcl。
对于混合场景,我们将创建两个有线节点:W(0)和W(1),通过一个基站BS与一个含有3个无线节点的无线网络相连。BS作为一个连接有线网和无线网的网关,可以在两者之间转发数据。关于BS的详细信息,请参考ns手册第15章第2节,下图显示了这个拓扑:
首先检查一下对于wireless1.tcl中定义的变量有什么需要修改的地方。
Adhoc路由协议修改为DSDV。另外我们定义了有线节点和无线节点间的Tcp CBR流,因此也就不再需要原来定义的模型文件了,仿真结束时间也需要修改,注意,在这里我们用opt()队列代替val()队列,这仅仅表示现在它是一个局部变量,而不是全局变量。
set opt(adhocRouting) DSDV
set opt(cp) "" ;# cp file not used
set opt(stop) 300 ;# time to stop simulation
现在定义TCP流开始的时间:
set opt(ftp1-start) 160.0
set opt(ftp2-start) 170.0
还要加上下面的语句,定义有线节点和基站的数目:
set num_wired_nodes 2
set num_bs_nodes 1
现在,我们开始程序的主要部分。对于混合网络,我们需要使用分层路由以便能够在有线网和无线网之间传递数据包。正如ns手册15.2.1中所说,在ns中,有线网络中的路由信息是基于拓扑连接关系的,即,节点之间的链路是如何连接的。Ns使用这个连接信息在节点之间传播转发表。然而无线节点中没有链路的概念,数据包在无线拓扑中根据adhoc路由选路,这种路由根据邻居之间传递的路由请求信息建立转发表。因此,想在有线和无线节点之间传递数据,我们必须使用基站作为连接两个域的网关。我们通过分层拓扑结构定义域和子域(或叫做簇)。在"set ns [new Simulator]"这条语句之后加入下列语句:
$ns_ node-config -addressType hierarchical
AddrParams set domain_num_ 2 ;# number of domains
lappend cluster_num 2 1 ;# number of clusters in each
;#domain
AddrParams set cluster_num_ $cluster_num
lappend eilastlevel 1 1 4 ;# number of nodes in each cluster
AddrParams set nodes_num_ $eilastlevel ;# for each domain
在上面的代码中,我们首先将节点对象的地址类型配置为分层:hierarchical。随后定义了拓扑结构。在这个拓扑中,域的数量是2,域中的节点数定义为2 1,意思是,第一个域(有线域)有两个簇,第二个域(无线域)有一个簇。下面一行代码定义了在每个簇中的节点数:"1 1 4",即,在前面两个簇中,每个簇有一个节点,第三个簇中有4个节点。于是,拓扑被定义成一个三层结构。
下面我们建立仿真的trace文件。注意,对于混合网络仿真可能对两个域都产生trace文件。两个域的trace都写到同一个文件中:wireless2-out.tr。为了区分有线和无线两种trace,所有无线trace以"WL"开头。我们还建立了nam trace,前面提到过,无线仿真中nam只能显示节点移动。
set tracefd [open wireless2-out.tr w]
set namtrace [open wireless2-out.nam w]
$ns_ trace-all $tracefd
$ns_ namtrace-all-wireless $namtrace $opt(x) $opt(y)
下面我们需要创建无线、有线、基站节点。这里请注意,创建节点时,需要给节点传递分层地址,于是,在"create-god $opt(nn)"后面加入下列代码:
# create wired nodes
set temp {0.0.0 0.1.0} ;# hierarchical addresses to be used
for {set i 0} {$i < $num_wired_nodes} {incr i} {
set W($i) [$ns_ node [lindex $temp $i]]
}
为了创建基站节点,我们需要按照下面的方法配置节点。这是节点的一个新api,包括先配置节点然后创建节点。关于节点新的api请参考第IX章的node API。由于基站节点作为两个网络的网关,它需要开启有线网的路由协议,这可以通过-wiredRouting ON选项指定。在创建了基站节点之后,我们再重新配置无线节点,设置wiredRouting OFF。用于基站的其他配置都可以用于无线节点。还要把无线节点的基站节点指定为BS(0),于是,所有从无线节点发往有线节点的数据包都将被转发到BS(0)。
注意,需要把基站节点放到和无线节点同一个域中,这样,从有线节点发送到无线节点的数据包才能通过基站然后通过adhoc路由到达无线节点。于是,对于混合网络,需要:
打开分层路由
为有线节点和无线节点创建不同的域。可以有多个有线或无线域。
每个无线域中都要有一个基站,通过它,无线节点可以和其他节点通信。
我们将一步步演示如何创建分层路由。现在我们有两个域,0是有线域,1是无线域。两个有线节点被放在0和1两个簇中,于是他们的地址看起来象这样:0(domain 0).0(cluster 0).0(only node)和0 (same domain 0).1(cluster 1).0(again only node)。
对于无线节点,他们在域1,我们在其中定义了一个簇,于是,所有节点在同一个簇中,他们的地址为:
WL node#1 : 1.0.1(second node in cluster)
WL node#2 : 1.0.2(third node)
WL node#3 : 1.0.3(fourth node)
当然,我们可以把两个有线节点放在同一个簇中,也可以把多个无线节点放到不同的簇中。同样,根据我们的拓扑,也可以根本不要簇,而只用一个两层地址结构:域和节点。
# configure for base-station node
$ns_ node-config -adhocRouting $opt(adhocRouting) \
-llType $opt(ll) \
-macType $opt(mac) \
-ifqType $opt(ifq) \
-ifqLen $opt(ifqlen) \
-antType $opt(ant) \
-propType $opt(prop) \
-phyType $opt(netif) \
-channelType $opt(chan) \
-topoInstance $topo \
-wiredRouting ON \
-agentTrace ON \
-routerTrace OFF \
-macTrace OFF
#create base-station node
set temp {1.0.0 1.0.1 1.0.2 1.0.3} ;# hier address to be used for
;# wireless domain
set BS(0) [ $ns_ node [lindex $temp 0]]
$BS(0) random-motion 0 ;# disable random motion
#provide some co-ordinates (fixed) to base station node
$BS(0) set X_ 1.0
$BS(0) set Y_ 2.0
$BS(0) set Z_ 0.0
# create mobilenodes in the same domain as BS(0)
# note the position and movement of mobilenodes is as defined
# in $opt(sc)
# Note there has been a change of the earlier AddrParams
# function 'set-hieraddr' to 'addr2id'.
#configure for mobilenodes
$ns_ node-config -wiredRouting OFF
# now create mobilenodes
for {set j 0} {$j < $opt(nn)} {incr j} {
set node_($j) [ $ns_ node [lindex $temp \
[expr $j+1]] ]
$node_($j) base-station [AddrParams addr2id \
[$BS(0) node-addr]] ;# provide each mobilenode with
;# hier address of its base-station
}
下面连接有线节点和BS,并和无线节点、node_(0)和node W(0)之间,W(1)和 node_(2)之间建立tcp连接。
#create links between wired and BS nodes
$ns_ duplex-link $W(0) $W(1) 5Mb 2ms DropTail
$ns_ duplex-link $W(1) $BS(0) 5Mb 2ms DropTail
$ns_ duplex-link-op $W(0) $W(1) orient down
$ns_ duplex-link-op $W(1) $BS(0) orient left-down
# setup TCP connections
set tcp1 [new Agent/TCP]
$tcp1 set class_ 2
set sink1 [new Agent/TCPSink]
$ns_ attach-agent $node_(0) $tcp1
$ns_ attach-agent $W(0) $sink1
$ns_ connect $tcp1 $sink1
set ftp1 [new Application/FTP]
$ftp1 attach-agent $tcp1
$ns_ at $opt(ftp1-start) "$ftp1 start"
set tcp2 [new Agent/TCP]
$tcp2 set class_ 2
set sink2 [new Agent/TCPSink]
$ns_ attach-agent $W(1) $tcp2
$ns_ attach-agent $node_(2) $sink2
$ns_ connect $tcp2 $sink2
set ftp2 [new Application/FTP]
$ftp2 attach-agent $tcp2
$ns_ at $opt(ftp2-start) "$ftp2 start"
后面就是原来脚本剩下的部分。wireless2.tcl中可能有写代码没有在此讨论,完整的代码可以在这下载:http://www.isi.edu/nsnam/ns/tutorial/examples/wireless2.tcl
运行这个脚本,在执行的最后将生成ns和nam的trace文件。运行wireless2-out.nam将显示无线节点的运动。在wireless2-out.tr中,我们可以看到有线域和无线域的trace。在160秒的时候,在节点_3_, (即node_(0)) 和0, (即W(0))之间建立了一条Tcp连接,注意,节点id是仿真器内部指定并根据节点创建顺序分配的。在170秒时,另一个反向的Tcp连接建立。关于CMUTraces的详细信息请参考ns手册第15章。
1、 在一个简单混合拓扑上运行移动ip
到目前为止,我们已经创建了一个有线-无线混合拓扑并且可以通过基站在有线节点和无线节点之间交换数据包了。但是,一个无线节点可能会漫游到它的基站的覆盖范围之外,此时,它仍然应该可以接收发向它的数据包。换句话说,在混合拓扑中支持移动ip应该很有趣。
在这个例子中的有线节点W0和W1和前面例子中完全相同。有两个基站,一个叫做HomeAgent(HA),另外一个叫做ForeignAgent(FA)。有线节点W1与两个基站的连接方式如下图:
拓扑中有一个移动节点叫做MobileHost(MH),它在HA和FA之间移动。我们将在W0和MH建立TCP流。当MH移动出HA域并进入FA域时,我们可以看到以MH为目的地的数据包是如何被HA根据移动ip协议的定义重定向到FA的。
我们把wireless2.tcl修改一下,创建一个无线移动ip脚本,叫做wireless3.tcl。这里可能不会完整的讨论这个脚本,为方便起见,可以从http://www.isi.edu/nsnam/ns/tutorial/examples/wireless3.tcl下载一份该脚本的拷贝。
首先将移动节点数修改为1:
set opt(nn) 1 ;# just one MH
set opt(stop) 250
在这个例子中,我们将在脚本中定义节点的移动和TCP连接,因此不需要原来读取cp和sc文件:
set opt(cp) ""
set opt(sc) ""
定义TCP的开始时间:
set opt(ftp1-start) 100.0
修改有线节点,基站的数目,但是在这个脚本中的这个变量实际并没有被使用,基站节点HA和FA都是独立创建的。
set num_wired_nodes 2
在创建ns实例并设置了地址分层格式后,加入下面几行代码用来定义拓扑层次。和wireless2.tcl很象,除了现在我们有了第三个FA域,并相应修改了簇和节点的参数。
AddrParams set domain_num_ 3 ;# number of domains
lappend cluster_num 2 1 1 ;# number of clusters in each domain
AddrParams set cluster_num_ $cluster_num
lappend eilastlevel 1 1 2 1 ;# number of nodes in each cluster
AddrParams set nodes_num_ $eilastlevel ;# of each domain
于是,在这个拓扑中,我们有一个有线域,以0标记,两个无线域,分别以1和2标记。和上一节中一样,节点的地址还不变,分别是0.0.0和0.1.0。在第一个无线域(1)中,我们有一个基站HA和一个移动节点MH,它们在同一个簇中。地址分别是1.0.0和1.0.1。在第二个无线域(2)中,我们有一个基站FA,地址是2.0.0。然而,在仿真过程中,MH将移动到FA的域中,我们可以看到作为移动ip的结果,从有线域到无线域中节点的数据包是如何被转发的。
有线节点的创建和以前一样,但是这次将创建HA和FA而不是一个基站,注意,在这里为了打开移动ip标志开关,我们必须将节点结构配置为-mobileIP ON:
# Configure for ForeignAgent and HomeAgent nodes
$ns_ node-config -mobileIP ON \
-adhocRouting $opt(adhocRouting) \
-llType $opt(ll) \
-macType $opt(mac) \
-ifqType $opt(ifq) \
-ifqLen $opt(ifqlen) \
-antType $opt(ant) \
-propType $opt(prop) \
-phyType $opt(netif) \
-channelType $opt(chan) \
-topoInstance $topo \
-wiredRouting ON \
-agentTrace ON \
-routerTrace OFF \
-macTrace OFF
# Create HA and FA
set HA [$ns_ node 1.0.0]
set FA [$ns_ node 2.0.0]
$HA random-motion 0
$FA random-motion 0
#provide some co-ord (fixed) to these base-station nodes.
$HA set X_ 1.000000000000
$HA set Y_ 2.000000000000
$HA set Z_ 0.000000000000
$FA set X_ 650.000000000000
$FA set Y_ 600.000000000000
$FA set Z_ 0.000000000000
然后按下面的步骤创建移动节点。注意象以前一样,我们关闭选项-wiredRouting(这是用来创建基站节点的)。HA被设置为移动节点的家乡代理。MH有一个叫做关心地址的地址(COA:care-of-address)。通过移动节点和基站之间交换注册/信标消息,基站将自己的地址分配给移动节点作为其COA。于是,在这个仿真中,一开始将HA的地址作为COA分配给MH。当MH移动到FA域时,它的COA将变为FA的地址。关于ns中移动ip的细节问题,参考ns手册的15.2.2。另外文件mip.cc mip.h mip-reg.cc mip-reg.h tcl/lib/ns-mip.tcl, ns-wireless-mip.tcl也可以作为参考。
# configure for mobilehost
$ns_ node-config -wiredRouting OFF
# create mobilehost that would be moving between HA and FA.
# note address of MH indicates its in the same domain as HA.
set MH [$ns_ node 1.0.1]
set node_(0) $MH
set HAaddress [AddrParams set-hieraddr [$HA node-addr]]
[$MH set regagent_] set home_agent_ $HAaddress
# movement of the MH
$MH set Z_ 0.000000000000
$MH set Y_ 2.000000000000
$MH set X_ 2.000000000000
# MH starts to move towards FA (640, 610) at a speed of 20m/s
$ns_ at 100.000000000000 "$MH setdest 640.000000000000 610.000000000000
20.000000000000"
# and goes back to HA (2, 2) at a speed of 20 m/s
$ns_ at 200.000000000000 "$MH setdest 2.000000000000 2.000000000000
20.000000000000"
在有线节点和HA/FA间创建链路,并建立TCP连接:
# create links between wired and BaseStation nodes
$ns_ duplex-link $W(0) $W(1) 5Mb 2ms DropTail
$ns_ duplex-link $W(1) $HA 5Mb 2ms DropTail
$ns_ duplex-link $W(1) $FA 5Mb 2ms DropTail
$ns_ duplex-link-op $W(0) $W(1) orient down
$ns_ duplex-link-op $W(1) $HA orient left-down
$ns_ duplex-link-op $W(1) $FA orient right-down
# setup TCP connections between a wired node and the MobileHost
set tcp1 [new Agent/TCP]
$tcp1 set class_ 2
set sink1 [new Agent/TCPSink]
$ns_ attach-agent $W(0) $tcp1
$ns_ attach-agent $MH $sink1
$ns_ connect $tcp1 $sink1
set ftp1 [new Application/FTP]
$ftp1 attach-agent $tcp1
$ns_ at $opt(ftp1-start) "$ftp1 start"
脚本的剩余部分不用改变。保存并执行这个脚本。在http://www.isi.edu/nsnam/ns/tutorial/examples/wireless3.tcl可以下载一个wireless3.tcl脚本。
在脚本运行过程中,你可能会看到一些警告:
"warning: Route to base_stn not known: dropping pkt"
这表示MH从一个域移动到另一个域的过程中由于暂时没有注册到任何一个基站,因此不知道该把数据包向谁发送。运行结束后,生成的ns和nam的trace文件是:"wireless3-out.tr"和"wireless3-out.nam"。 nam的输出文件可以显示出节点的移动,以及有线域中的数据流。 Ns输出文件中含有有线节点和无线节点的所有数据。我们还可以看到周期性信标消息广播。一开始TCP数据包直接由HA转发给MH,当MH移动到HA域之外而进入FA域后,去往MH的数据包被封装并转发给FA,然后在解封装后转发给MH。
日志标题:ns教程中译版——第十一章
发表时间:2005-7-11 20:15:51
十一、为大规模无线仿真生成节点移动和流量连接文件
我们在前面的例子中已经使用过ns发行包中带有的流量模型和节点移动文件。在本节中,我们将学习如何使用CMU的流量和场景生成脚本来产生这些文件。本节分为两小节,第一小节讨论流量模型的生成脚本。第二小节是关于CMU的节点移动生成工具"setdest",它是用来为移动节点产生随机游走移动模型的。
1、 为无线场景产生随机流量模型
用一个流量生成器可以产生移动节点间的TCP和CBR的随机连接。这个流量生成脚本在~ns/indep-utils/cmu-scen-gen目录下,文件名叫做cbrgen.tcl。可以用它来生成移动节点间的CBR和TCP流量。为了产生流量连接文件,我们需要定义流量的类型(CBR还是TCP),节点数,节点间最大连接数和一个随机数种子,在CBR情况下,还要给定速率,这个速率的倒数被用来计算CBR包的间隔。因此命令行可能象下面一样:
ns cbrgen.tcl [-type cbr|tcp] [-nn nodes] [-seed seed] [-mc connections]
[-rate rate]
TCP/CBR连接的开始时间是最大为180.0秒的一个随机数。实现细节可以参看脚本源文件cbrgen.tcl。
举例如下:我们将为10个节点的CBR连接创建连接文件,最大连接数为8,随机数种子为1.0,速率为4.0。于是,我们在命令行输入:
ns cbrgen.tcl -type cbr -nn 10 -seed 1.0 -mc 8 -rate 4.0 > cbr-10-test
从生成的cbr-10-test文件中可以看到其中一个cbr连接可能如下:
#
# 2 connecting to 3 at time 82.557023746220864
#
set udp_(0) [new Agent/UDP]
$ns_ attach-agent $node_(2) $udp_(0)
set null_(0) [new Agent/Null]
$ns_ attach-agent $node_(3) $null_(0)
set cbr_(0) [new Application/Traffic/CBR]
$cbr_(0) set packetSize_ 512
$cbr_(0) set interval_ 0.25
$cbr_(0) set random_ 1
$cbr_(0) set maxpkts_ 10000
$cbr_(0) attach-agent $udp_(0)
$ns_ connect $udp_(0) $null_(0)
$ns_ at 82.557023746220864 "$cbr_(0) start"
于是在节点2和3之间建立起一个UDP连接。在这个文件的末尾,显示出总的UDP源和总的连接数分别是5和8。
类似的,TCP连接的生成可以通过把"type"改为tcp进行,如:
ns cbrgen.tcl -type tcp -nn 25 -seed 0.0 -mc 8 > tcp-25-test
tcp-25-test 中的一个典型连接可能象下面这样:
#
# 5 connecting to 7 at time 163.0399642433226
#
set tcp_(1) [$ns_ create-connection TCP $node_(5) TCPSink $node_(7) 0]
$tcp_(1) set window_ 32
$tcp_(1) set packetSize_ 512
set ftp_(1) [$tcp_(1) attach-source FTP]
$ns_ at 163.0399642433226 "$ftp_(1) start"
2、 为无线场景创建节点移动文件
~ns/indep-utils/cmu-scen-gen/setdest是一个节点移动生成器,同一目录下还有它的源代码和相应的Makefile。CMU版本的setdest使用了/dev/random下的系统相关的随机函数,并调用initstate()生成随机数。这后来被修改成使用ns中随机数生成器(RNG类)。为了编译setdest.cc,需要做以下工作(现在已经不需要这样了,译者注):
到ns目录下,运行configure,这将为setdest产生makefile。
转到目录indep-utils/cmu-scen-gen/setdest下,运行make,首先会为~ns/rng.cc创建一个独立的目标文件,然后将生成setdest的可执行文件。
运行setdest的参数如下:
./setdest [-n num_of_nodes] [-p pausetime] [-s maxspeed] [-t simtime] \
[-x maxx] [-y maxy] > [outdir/movement-file]
假设我们要创建一个20个节点以10.0m/s的平均速度移动,移动停顿时间为2秒的一个节点移动场景,并且希望仿真在200秒结束,拓扑范围为500 X 500,于是,我们的命令行应该是:
./setdest -n 20 -p 2.0 -s 10.0 -t 200 -x 500 -y 500 > scen-20-test
输出默认是标准输出,我们可以把它重定向到文件scen-20-test,文件一开始是节点的原始位置,然后定义它的移动:
$ns_ at 2.000000000000 "$node_(0) setdest 90.441179033457 44.896095544010
1.373556960010"
文件中的这一行首先定义了节点node_(0)在2.0s时,开始象目的(90.44, 44.89)移动,速度为1.37m/s。这行命令可以同时改变节点的速度和方向。
关于GOD的命令也在节点移动文件中生成。通用操作指示器(GOD)是用来保存全局的环境、网络或节点状态信息的,但这些信息对于仿真的参与者并不需要。
目前,god对象只用来保存一个节点到其他节点的最短跳数信息。God对象并不在仿真过程中计算这些信息,这样做将很耗时。下面这行代码将信息从移动模型文件装入到god对象中:
$ns_ at 899.642 "$god_ set-dist 23 46 2"
在节点23和46之间的最短路径在899.642s时变为2跳。
Setdest用随机游走模型生成移动文件,这些文件中已经包含了对god的设置。
另外一个程序叫做calcdest(同在~ns/indep-utils/cmu-scen-gen/setdest目录下),它可以用来在通过其他手段生成的移动模型文件中加入对god设置语句。Calcdest的执行假设文件符合一定格式,否则程序执行将失败。如果calcdest无法处理某些文件,有一个办法可以把这个文件转化为caldest可以识别的格式:用ad-hockey打开文件,再保存一下,如果ad-hockey可以打开你的输入文件,那么经过它保存的文件就是符合calcdest的格式。
Calcdest和setdest都时根据节点无线传输范围计算节点间跳数,它忽略了实际仿真中传输模型可能带来的任何影响。传输范围可以通过命令行参数输入,也可以从移动模型文件的头部提取。
在Monarch Project项目中,使用了路径长度信息分析adhoc网络中路由协议的最优性,于是也被CMUTrace作为每个包的信息打印了出来。
CMU发现这些信息的一些其他用途:
在移动模型中描述拓扑的变化率
网络分割的大小和频率
当god提供完美邻居代价(0代价)时,对路由协议的行为进行分析
在节点移动文件的最后有无法到达的节点数目,总的路由数和连接改变信息。
修改过的setdest版本在最新的ns版本中都有,如果没有,可以去看看http://www.isi.edu/nsnam/ns/ns-build.html。
(完)
16:15 | 添加评论 | 发送消息 | 固定链接 | 引用通告 (0) | 写入博客 | 学术研究
No comments:
Post a Comment