网络模拟和协议仿真课程设计报告Word下载.docx
《网络模拟和协议仿真课程设计报告Word下载.docx》由会员分享,可在线阅读,更多相关《网络模拟和协议仿真课程设计报告Word下载.docx(45页珍藏版)》请在冰豆网上搜索。
网络中的节点按照一定的规律进行休眠调度,节点维护的邻居相关信息除了最小跳步数、剩余能量、链路质量等常用选路困素外,还包括计算其醒来的时间差。
网络不需要全局或局部的时间同步,也不要求节点修改内部时钟。
而是通过记录的与邻居节点的时间差,决定发送数据包的时刻。
此外,可以通过发送的数据包和接收节点回馈的ACK进行时间差修补。
对下一跳节点进行选择时,加入对邻居节点醒来先后顺序的考虑。
为了简单起见,路由方面借鉴经典的基于地理位置路由的选路策略,如贪婪路由、对网络空洞的处理。
休眠调度也是利用简单的调度策略,实现定义节点的睡眠和活跃周期。
SDRAD路由协议是对DRAD协议的简写,简化了的DRAD协议。
SDRAD主要实现了建立网络结构,收集数据信息的功能[3]。
第二章协议设计
SDRAD协议分两个阶段:
网络拓扑形成阶段和数据收集阶段。
接下来将对
这两个阶段进行详细说明。
2.1网络拓扑形成阶段
当节点部署到网络并开启电源后,并不是立即进入数据收集。
而是首先由基站发送Hello包,节点接受到Hello包后,更新自己的邻居节点信息,从而建立树状的网络结构。
协议刚启时,所有节点的跳步数都为无穷大。
首先,由基站广播Hello包,为了简单,本例中将基站固定为序列号为0的节点。
节点接收到Hello包后,更新本节点跳步数和邻居列表,广播自身。
Hello包的分组头
包类型
(Packettype)
跳步数(Hopcount)
源节点序列号
(Sourcenodesequence)
节点的分布及形成的拓扑关系:
图1结点分布
如图1,网络中共有0~7个节点,其中节点0为基站,其跳步数为0。
节点1,2的跳步数为1,节点3,4,5的跳步数为2,节点6的跳步数为3。
在网络形成阶段由基站发送Hello包,节点1,2接收到Hello包,将节点0加入到它们的邻居表中,同时节点1,2重新向外发送Hello包,被节点3,4,5接收,依次建立邻居表,形成SDRAD的拓扑关系。
结点
结点0
结点1
结点2
结点3
结点4
结点5
结点6
1
2
3
4
5
2.2数据收集阶段
进入数据收集阶段后,当网络中某一节点接受到数据包后,该节点就会向邻居表中的某一节点发送数据收集包(collect包),最终将接收到的数据信息发送到基站,传给应用程序进行处理。
选择哪一个邻居节点呢?
协议将随机选择邻居表的一个节点进行转发。
图2数据转发路径图
如图2,节点6接收到数据信息,节点6将该数据封装成Collect包,依次传送给节点5、节点1、在到基站(节点0)。
路径中结点5与结点1都是随机选择的。
第三章协议实现
3.1SDRAD协议类
为了实现SDRAD协议类,我们编写了4个程序文件,分别是sdrad.h、sdrad.cc(SDRAD协议的定义和实现)、sdrad_packet.h(SDRAD新增包头的定义)、sdrad_nbtable.h(邻居表的定义和实现)。
在ns2中,所有路由协议都是从Agent类继承而来的,实现协议时主要关心的是节点对包的接收和发送,即recv函数和send函数。
Recv函数一般从Agent类继承而来,send函数根据需要自己编写。
下面给出SDRAD协议类的定义:
classSDRAD:
publicAgent{
friendclassSDRAD_Neighbor;
//SDRAD_Neighbor类是邻居表类
friendclassSDRAD_HelloTimer;
//Hello定时器类
public:
SDRAD(nsaddr_tid);
//含一个参数的构造函数
voidrecv(Packet*p,Handler*);
//接收上层协议送下来的数据并处理
protected:
//命令处理函数,实现Tcl和C++之间的信息交换
intcommand(int,constchar*const*);
inlineintinitialized(){return1&
&
target_;
}
//对上层发送的数据进行处理
voidresolve(Packet*p);
//邻居表的信息维护
voidnb_insert(u_int32_tseqno,u_int8_thop);
//插入
SDRAD_Neighbor*nb_lookup(u_int32_tseqno);
//查找
voidnb_delete(u_int32_tseqno);
//删除
voidnb_purge();
//清空
voidnb_print();
//打印,检验协议所建立的拓扑是否符合要求
voidforward(u_int32_tseqno,Packet*p,doubledelay);
//转发函数
voidsendHello(boolisbase);
//发送Hello包
//PacketRXRoutines
voidrecvHello(Packet*p);
//接收Hello包
//接收SDRAD包(包括Hello包和Collect包)
voidrecvSDRAD(Packet*p);
//PacketcollectRoutines,发送、接收Collect包
voidsendCollect(u_int32_tipdst);
voidrecvCollect(Packet*p);
//Timers
SDRAD_HelloTimerhtimer;
//定义定时器
private:
//本协议中为了简化处理,将IP地址和序列号设置为同一唯一值
nsaddr_tindex_;
//本节点的IP地址
u_int32_tseqno_;
//本节点的序列号
u_int8_thop_count_;
//节点跳步数
SDRAD_nbtablenbthead_;
//定义邻居表
//为调试和跟踪定义的变量
Trace*logtarget_;
NsObject*uptarget_;
NsObject*port_dmux_;
};
其构造函数的实现:
voidSDRAD:
:
SDRAD(nsaddr_tid):
Agent(PT_SDRAD),port_dmux_(0),htimer(this){
index_=id;
seqno_=id;
//
hop_count_=INFINITY;
//跳步数为无穷大
logtarget_=0;
LIST_INIT(&
nbthead_);
length=0;
//邻居表长度
}
3.2数据包头
现在,我们为SDRAD协议新建包头,对应实现协议中的分组头。
下述代码在sdrad_packet.h中:
//SDRAD包头:
hdr_sdrad
structhdr_sdrad{
u_int8_tdh_type;
//分组类型
staticintoffset_;
inlinestaticint&
offset(){returnoffset_;
inlinestatichdr_sdrad*access(constPacket*p){
return(hdr_sdrad*)p->
access(offset_);
Hello包头:
hdr_sdrad_hello
structhdr_sdrad_hello{
u_int8_thh_type;
u_int8_thh_hop_count;
//跳步数
u_int32_thh_src_seq;
//源节点序列号
//计算包头大小
inlineintsize(){
intsz=0;
sz=sizeof(u_int8_t)
+sizeof(u_int8_t)
+sizeof(u_int32_t);
returnsz;
Collect包头:
hdr_sdrad_collect
structhdr_sdrad_collect{
u_int8_thc_type;
u_int8_thc_hop_count;
u_int32_thc_src_seq;
u_int32_thc_dst_seq;
//目的节点序列号
inlineintsize(){//计算包头大小
+sizeof(u_int32_t)
//联合SDRAD的分组头,节约空间便于使用
unionhdr_all_sdrad{
hdr_sdraddh;
hdr_sdrad_hellohh;
hdr_sdrad_collecthc;
3.3邻居表
在ns2中内置了一个链表结构list(源码在~ns/lib/bsd-list.h中),在此使用这个list结构实现邻居类(sdrad_nbtable.h):
#include<
lib/bsd-list.h>
//包含头文件
classSDRAD_Neighbor{
friendclassSDRAD;
//设置SDRAD为友元
SDRAD_Neighbor(u_int32_ts,u_int8_thop){//构造函数
seqno_=s;
hop_count_=hop;
//链表结构
LIST_ENTRY(SDRAD_Neighbor)nb_link;
//邻居结点中的字段
u_int32_tseqno_;
//序列号
u_int8_thop_count_;
//定义链表结构
LIST_HEAD(SDRAD_nbtable,SDRAD_Neighbor);
解释类的定义和编译类的联接
解释类和编译类的连接方法比较固定,对于只关心协议内容的人,可参考ns2中同层协议的实现方法。
TclHooks
到目前为止,我们的SDRAD协议还不能被ns所认可,还需要其它定义。
下面部分是不可缺少的,而且通常只需要按照这个典型模式实现。
其中Agent\SDRAD指定了C++中的SDRAD类和Tcl部分中的Agent/SDRAD绑定在了一起。
//sdrad.cc文件中:
//TCLHooks
staticclassSDRADclass:
publicTclClass{
SDRADclass():
TclClass("
Agent/SDRAD"
){}
TclObject*create(intargc,constchar*const*argv){
assert(argc==5);
return(newSDRAD((u_int32_t)atoi(argv[4])));
}class_rtProtoSDRAD;
3.4SDRAD协议类的实现
3.4.1邻居表管理
本部分功能实现对邻居表的增删查改等,方便代理类的使用。
1>
插入
新建一个邻居节点,插入nbtable中。
voidSDRAD:
nb_insert(u_int32_tseqno,u_int8_thop)
2>
查找
查找序列号为seqno的节点
SDRAD_Neighbor*SDRAD:
nb_lookup(u_int32_tseqno)
3>
删除
删除序列号为seqno的节点
nb_delete(u_int32_tseqno)
4>
清空
清空邻居表
nb_purge()
5>
打印
输出邻居表的跳步数和序列号
nb_print()
3.4.2SDRAD路由协议中的关键函数
command函数
intSDRAD:
command(intargc,constchar*const*argv)
command函数的写法固定,主要实现Otcl和C++的交互,对Otcl命令
进行解析。
用来解析模拟器类create-sdrad-agent函数中的命令。
Simulatorinstproccreate-sdrad-agent{node}{
#CreateSDRADroutingagent
setragent[newAgent/SDRAD[$nodeid]]
puts"
=======[$nodeid]========"
;
#测试
$selfat0.0"
$ragentstart"
#在0.0s启动协议
$nodesetragent_$ragent
return$ragent
recv函数
recv(Packet*p,Handler*)
新协议类重载recv函数,判别分组包类型,并对其进行转发、更新、丢弃、重发等处理,来实现具体协议所要求的内容。
sendHello函数
sendHello(boolisbase){
发送hello数据包。
基站只处理发送一次Hello包,非基站节点根据自身信息新建Hello包,广播。
recvHello函数
recvHello(Packet*p)
基站接收到Hello包,丢弃;
节点接收到自身广播的Hello包,丢弃;
节点接收到有效Hello包,更新本节点的跳步数,将上一跳节点信息加入邻居表,并广播自身;
丢弃重复邻居Hello包和非邻居包。
HelloTimer定时器
运行一段时间的网络会不稳定(包括网络中有新结点加入,节点寿命结束,能量用尽等),用HelloTimer定时器定时发送Hello包,更新邻居信息,建立新的树状网络结构。
sdrad.h中HelloTimer的定义:
classSDRAD_HelloTimer:
publicHandler{
SDRAD_HelloTimer(SDRAD*a):
agent(a){}
voidhandle(Event*);
SDRAD*agent;
Eventintr;
重写handle函数,使基站发送Hello包,建立新的网络结构:
voidSDRAD_HelloTimer:
handle(Event*){
agent->
nb_purge();
//清楚邻居表
sendHello(true);
//基站发送Hello包
//该定时器每个50s调度一次
Scheduler:
instance().schedule(this,&
intr,50);
6>
Resolve函数
resolve(Packet*p)
节点通过第一节点建立好网络结构后,就进入数据收集阶段,此协议通过将有用数据处理成Collect包在网络之间传送。
在recv函数中节点在接收到有用数据后,通过resolve函数将其处理成collect包后转发给邻居节点。
7>
下一跳函数
u_int32_tSDRAD:
nexthop_random()
随机选择邻居表中节点编号
8>
转发函数forward
forward(u_int32_tseqno,Packet*p,doubledelay)
9>
发送collect包函数
sendCollect(u_int32_tipdst)
节点根据参数ipdst确定下一跳目的地,发送collect包。
10>
接收collect包函数
recvCollect(Packet*p)
节点随机选择下一跳,发送collect包。
基站处理数据,丢弃废包。
3.5将SDRAD整合到NS-2.35中
3.5.1建立协议文件
本协议程序共五个文件,分别是:
sdrad_packet.h、sdrad_nbtable.h、sdrad.h和sdrad.cc。
在<
ns-2.35>
目录下建立sdrad文件夹,将上述这5个文件放进去。
3.5.2修改NS-2.35源码
需要改五个文件:
ns-2.35/common/packet.h
ns-2.35/tcl/lib/ns-lib.tcl
ns-2.35/tcl/lib/ns-packet.tcl
ns-2.35/Makefile
ns-2.35/common/packet.h文件中找到如下位置,添加红色标记的一行。
staticconstpacket_tPT_PROTONAME=73;
//insertnewpackettypeshere
staticpacket_tPT_NTYPE=75;
//ThisMUSTbetheLASTone
找到如下位置,添加红色标记的一行。
name_[PT_DCCP_RESET]="
DCCP_Reset"
;
name_[PT_PROTONAME]="
protoname"
name_[PT_SDRAD]="
SDRAD"
name_[PT_NTYPE]="
undefined"
在ns-2.35/tcl/lib/ns-lib.tcl文件中找到如下位置,添加红色标记的一段代码。
AODV{
setragent[$selfcreate-aodv-agent$node]
SDRAD{
setragent[$selfcreate-sdrad-agent$node]
AOMDV{
setragent[$selfcreate-aomdv-agent$node]
在合适的位置加入如下代码:
在ns-2.35/tcl/lib/ns-packet.tcl文件中找到如下位置,添加红色标记部分。
MDART#routingprotocolforad-hocnetworks
#AOMDVpatch
AOMDV
Protoname
SDRAD#
在ns-2.35/Makefile文件中找到如下位置,添加红色标记部分
wpan/p802_15_4trace.owpan/p802_15_4transac.o\
apps/pbc.o\
protoname/protoname.oprotoname/protoname_rtable.o\
sdrad/sdrad.o\
$(OBJ_STL)
3.5.3编译NS-2.35源文件
在cygwin中运行如下命令完成源文件的编译。
huger@huger-PC~/ns-allinone-2.35
$cdn