Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导
- Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导
- 必要的message类实现
- 从下面开始是在veins/src/veins/modules/application/ieee80211p文件夹内进行操作
- Beacon
- 路由探索
- 路由传输
Veins(车载通信仿真框架)入门教程(三)——多跳路由实现指导
之前为大家介绍了一下Veins的入门知识,现在教大家怎么在Veins的基础上实现多跳路由,其实基本掌握了Veins后实现思路并不难理解,实现一个简单的算法也不难,难的是提出一个更优的路由协议,调试出更好的结果,本文给大家介绍个简单的例子:通过广播RREQ(一些名词我就不解释了)寻找目的节点的位置,目的节点收到RREQ后单播返回RREP给源节点,源节点根据RREP内记录的目的节点位置信息选择距离目的节点最近的邻居节点作为下一跳节点并将数据包传给此节点,之后的每一跳节点也都采用这种转发逻辑选择下一跳节点,直到数据包传至目的节点。
主要是指导,辅助贴几张源码截图,更多的细节都是搞研究的自己摸索吧。( ˘▽˘)っ加油宝宝们,我也是这么过来的。最后如果对大家研究有帮助、有启发,大家发论文有心的引一下我的博客,嫌麻烦的也没关系。又啰里啰嗦了一大堆~
必要的message类实现
路由就是用来传输message的,各种各样的message,既有我们要传的数据帧,也有维持正常传输所需的各种辅助帧,比如RREQ、RREP、RERR、Beacon等等。怎么实现它们呢,其实并不难,我们到veins/src/veins/modules/messages下面就可以发现一些veins内置的message类,把它们copy一下(包括.h,.cc,.msg),然后改一下类名(直接用查找替换,不用管它是怎么实现的),然后在对应的.h后缀名文件内的如下图位置添上自己需要这个message携带的各种数据格式(截图内的源码为草稿,为了赶时间狂写狂改的,各种代码冗余、命名不规范等问题,各位编程大神不要见笑),
补充一下:在对应的.msg文件里面也要加上对应的变量
之后进入如下图所示的文件夹内打开终端,
依次输入如下命令进行编译,
OMNeT++会自动为你在新加的message类文件内添加好对应数据的设置和读取函数,比如
至此message类就实现了,后面写路由协议的时候把相应的类导进来,然后就可以直接调用了,比如RREQ传到目的节点,目的节点生成RREP后将自己的位置写入其中,使用RREQ的getAlongNodeID函数(仔细看上面的图)得到沿路节点的ID(这就需要每一跳节点接收到RREQ后将自己的ID写入RREQ携带的alongNodeID[100]数组的对应位置),写入RREP,然后单播转发RREP,每一跳节点都从alongNodeID[100]查找上一跳节点ID并传给它(要查找上一跳还需要hopNumber这一计数变量辅助),最终单播返回源节点,是不是很简单~
从下面开始是在veins/src/veins/modules/application/ieee80211p文件夹内进行操作
还是按上一节的操作,将BaseWaveApplLayer对应的.h .cc .ned文件copy一下改成自己的类名,以此为基础实现自己的路由逻辑,以下设为MultihopBaseWaveApplLayer。
Beacon
OMNeT++是分布式实现网络仿真的,因此我们无法从系统的层面(也就是上帝视角)对仿真过程进行操纵,这也就是为什么要用广播RREQ的方式找到目的节点,而无法直接将目的节点的位置坐标告知源节点指导其数据转发,当然这也是有好处的,这使得我们的仿真更加贴合实际,将来工程化的时候这些函数拿出来直接就能用了。
既然无法从上帝视角进行操作,那么每个节点是怎么确定周围有多少邻居节点以及它们的信息的呢?
通过Beacon,也就是每隔一定时间(为了减少数据包碰撞,可以采用变时长,这是一个研究方向)广播Beacon帧,里面携带当前节点的位置、速度、所处道路ID等信息(Beacon携带的信息按上一节介绍的也可以自己设计)。然后周围节点接收到Beacon帧后将相关信息加入自己的邻居节点信息表内(在MultihopBaseWaveApplLayer内用struct结构自己设计一个,然后用作MultihopBaseWaveApplLayer的成员变量)。
Beacon是定时的,怎么实现这种定时操作呢?
如图所示,在initialize(初始化)的时候就通过scheduleAt给自己定时发送了sendBeaconEvt消息。另外两个nextRoutEvt和routeSearchCheckEvt是我用来定时发送下一个数据包和检查之前的RREQ广播出去有没有收到RREP的,如果没有的话再重新路由探索。(反正各种情况都要考虑到,细讲的话可以开门课了,你们自己实现吧)。
MultihopBaseWaveApplLayer中用下面这个函数handleSelfMsg接收各种自己发给自己的消息,实现定时自操作,接收到消息后再使用scheduleAt定时给自己发消息,就能实现循环定时操作了。
MultihopBaseWaveApplLayer自己接收到给自己发的sendBeaconEvt后生成Beacon并使用sendDown函数发往下一层MAC层(细读源码,我也不细讲Veins的结构了),之后MAC层一顿操作,什么信道切换、什么EDCA机制、什么随机退避,之后通过物理层将Beacon帧广播出去。
邻居节点的MultihopBaseWaveApplLayer怎么接收Beacon等其他各种数据帧的呢?
通过如下图所示函数handleLowerMsg,可以接收各类从MAC层传上来的数据帧,至于物理层、MAC层怎么操作的不用管,我们只要得到这个数据帧就行,然后通过onBSM函数收集邻居节点的信息存到本地邻居节点信息集内。那么onBSM函数里面写了些什么呢?这就是秘密了~
路由探索
路由探索就是之间讲的广播RREQ寻找目的节点,目的节点收到RREQ单播返回RREP给源节点的过程。
思路很简单,初始化的时候定个时某个时刻随机生成个目的节点ID(不太好,最后自己先用默认的例子跑一遍自己生成的交通流,看看有哪些节点,自己选个合适的目的节点),在handleSelfMsg对应位置生成RRRQ然后sendDown,采用广播的形式,每个节点收到RREQ后(handleLowerMsg函数)看看自己是不是目的节点,如果不是则再次广播出去(这种广播方式造成广播风暴,引发大量的数据帧碰撞,如何优化广播机制也是个研究方向),直到目的节点收到RREQ。每个节点接收到RREQ后都将自己的ID写入其中,目的节点收到RREQ就可以根据这个单播返回RREP。
RREP的单播返回是怎么实现的呢?
数据都是广播出去的,只要处于信号覆盖范围就能收到,但是如果只有数据帧内设好的下一跳节点接收到此数据帧才进行后续操作,其他节点收到数据后不进行处理则可实现单播,这样做的好处是每个接收到数据帧的节点不必都广播,减轻了数据碰撞的程度。
RREP根据RREQ记录的沿路节点ID(目的节点从RREQ中读取并在生成RREP时写入RREP)设置下一跳节点ID这一类变量,即可实现前述单播操作。
路由传输
确定好目的节点位置后,即进行正式的数据传输,采用前述单播方式传输数据。通过对比邻居节点集各邻居节点的位置与目的节点的位置,选取距离目的节点最近的邻居节点作为下一跳节点。(如果没有邻居节点比当前节点距离目的节点更近呢?此时采取右手转发,有基础的可能发现了,我讲的就是GPSR路由协议( ̄︶ ̄)↗)同样的也是在每个节点的MultihopBaseWaveApplLayer的handleLowerMsg函数接收到相应类型数据帧时进行操作。大家是不是开始理会到了OMNeT++分布式操作的含义了~
剩下的路由错误、路由恢复就不说了,都是细节。
最后实现出来的效果就是下面这样了,粉色是RREQ,绿色代表正式的数据帧,黄色代表目的节点接收到数据帧后返回的数据确认帧(ACK),小圆代表中转节点,大圆代表目的节点。