One网络模拟器探索之七:路由模块的工作原理
作者:SunSmile 出处:http://www.cnblogs.com/sunsmilecs 欢迎转载,也请保留这段声明。
DTN路由是移动网络环境中研究最热门的方向,基于概率的路由、基于效用的路由、基于预测的路由、基于社会网络的路由甚至是社交网络中人与人之间联系的紧密程度都拿来作为路由决策的依据。而ONE是专门的DTN路由仿真器,当前网络上对于ONE仿真器的相关资料较少,特总结其路由模块的工作原理。
首先需要了解core包中类之间的关系,以及分别对应实际DTN网络中的哪些实体。
a)DTNHost模拟的是DTN网络中的移动节点,其具体的移动规律或者移动路径是由MovementModel决定的。
b)NetworkInterface是网络接口设备,也就是通常说的网卡,由于节点有可能安装多个网卡,因此,DTNHost与NetworkInterface之间是一对多的关系
c)Connection就是两个及以上的DTNHost在移动过程中进入彼此无线通信半径之后,自动建立起无线连接,并可以进行通信
d)Application是运行在DTNHost之上的应用程序,其主要功能是负责生成消息和接收消息;
e)MessageRouter相当于传统的网络路由器,但DTN网络中,每个节点有需要具有消息转发功能,因此,每个DTNHost都需要包含一个MessageRouter,并将其大部分与消息相关的处理都交给MessageRouter负责。
一个完整的消息传输过程是这样的:DTNHost的某个Application在运行过程中可能需要与其他节点的Application进行通信,从而需要生成消息和接收消息,DTNHost的消息操作通过MessageRouter代为执行,MessageRouter在消息的传输过程中决定消息转发给哪一个邻居节点(DTNHost的邻居节点动态性较高,变化比较频繁)。而消息的传输必须通过连接两个不同DTNHost节点上NetworkInterface的Connection进行传输,因此,当消息离开上一跳节点但还未到达下一跳节点时,消息是由Connection进行管理的。
节点在移动的过程中,还需要接收其他节点发送的消息,并转发给最可能到达目的节点的下一跳节点,也就是担当中间节点的角色。因此,MessageRouter在转发消息的时候首先判断当前是否存在邻居节点,如果存在那么从消息缓存(messages列表)中取出一条消息(具体去哪条消息是路由策略相关,可以是FIFO,也可以是基于优先级的),将其发送给下一跳节点。此外,MessageRouter负责管理DTNHost的缓存,在ONE中的实现直接将缓存作为MessageRouter的属性,因此消息的删除同样是通过MessageRouter完成的。
有一点需要详细解释的是,这里认为只有消息的发送和接收,是DTNHost中的Application参与,消息中间过程在Application是看不到的。因此当调用DTNHost.sendMessage(msgid,to)时,认为消息直接就从源节点到目的节点了,同样接收消息DTNHost.receiveMassage(message,from),也是认为直接就从源节点接收到目的节点了。所有消息在中间节点的转发和丢弃都是在MessageRouter中完成的,而MessageRouter进行转发和丢弃的动作都是在节点运动过程中是否两个节点都进入彼此的无线通信半径之后且离开通信半径之前完成的。换言之,MessageRouter的驱动力来自两个方面:一是DTNHost上运行的Application;二是DTNHost的不断运动。
前者自然不必多说,这里从ONE的代码实现角度来理清楚DTNHost的运动是如何导致MessageRouter的相关动作的。首先ONE仿真器的主循环(也就是说主循环的一次循环要完成仿真中所有时间和事件的更新)是在ui.DTNSim.runSim()函数(此函数的具体实现是在其子类ui.DTNSimTextUI.runSim()和gui.DTNSimGUI.runSim()),这个函数反复调用core.World.update()函数(world类就是仿真环境中的世界,其update()表示更新所有时间和事件)。core.World.update()函数中updateHosts()和moveHosts()两个函数,而它们正好对应DTNHost的应用更新和位置更新。更进一步,MessageRouter及其子类的主要函数都是通过updateHosts()和moveHosts()间接调用的。
public static void main(String[] args) { //省略初始化代码 if (batchMode) {//批处理模式下,也就是Shell模式下 long startTime = System.currentTimeMillis(); for (int i = nrofRuns[0]; i < nrofRuns[1]; i++) { // print("Run " + (i + 1) + "/" + nrofRuns[1]); Settings.setRunIndex(i); resetForNextRun(); new DTNSimTextUI().start();//仿真器启动函数 } double duration = (System.currentTimeMillis() - startTime) / 1000.0; print("---\nAll done in " + String.format("%.2f", duration) + "s"); } else {//GUI模式下 Settings.setRunIndex(guiIndex); new DTNSimGUI().start();//仿真器启动函数 } }
public abstract class DTNSimUI { //省略部分代码 /** * Runs simulation after the model has been initialized. */ protected abstract void runSim(); //省略部分代码 }
public class DTNSimTextUI extends DTNSimUI { //省略部分代码 protected void runSim() { double simTime = SimClock.getTime(); double endTime = scen.getEndTime(); print("Running simulation '" + scen.getName() + "'"); startTime = System.currentTimeMillis(); lastUpdateRt = startTime;
//这里就是ONE仿真器的主循环 while (simTime < endTime && !simCancelled) { try { world.update(); //看这里^_^,所有事件的更新 } catch (AssertionError e) { e.printStackTrace(); done(); return; } simTime = SimClock.getTime(); this.update(false);//时间更新 } //省略部分代码 } //省略部分代码 }
/** * World contains all the nodes and is responsible for updating their location * and connections. */ public class World { //省略部分代码 public void update() {/* process all events that are due until next interval update */ while (this.nextQueueEventTime <= runUntil) { simClock.setTime(this.nextQueueEventTime); ExternalEvent ee = this.nextEventQueue.nextEvent(); ee.processEvent(this); updateHosts(); //更新所有DTNHost setNextEventQueue(); } moveHosts(this.updateInterval);////更新所有DTNHost位置 simClock.setTime(runUntil); updateHosts();//更新所有DTNHost } //省略部分代码 }
最终的结论就是,只要NetworkInterface中存在的Connection都是当前正在连接中的,因此可以直接用来进行消息传输。其中,一个DTNHost中存在多个NetworkInterface,而多个NetworkInterface存在多个Connections,因此,MessageRouter某时刻可用的Connection数量可能值为0,1,2,……
到这里,就解决了MessageRouter可以用来进行传输Message的Connection来源问题以及什么时候调用MessageRouter的问题了。至此,MessageRouter的工作原理也应该算讲清楚了。