The ONE仿真器进行一系列初始化之后,开始仿真,详情可参考之前博文《The ONE使用笔记:main函数剖析》。仿真的核心部分是world.update()
函数,world.update
负责每隔一段时间,处理所有事件,并且更新所有节点。本文讲解The ONE仿真器每隔一段时间都做些什么。
目录
1. The ONE运行机制
The ONE是基于代理离散事件(agent-based discrete event)仿真器。把节点间连接建立UP或DOWN,消息产生、发送、接收都视为事件,The ONE进行一些初始化工作后,开始仿真。每隔一段间(通过在设置文件Scenario.updateInterval
来设置),系统处理每一个到期的事件,并同时更新所有节点。
1.1 main函数
以批处理(batch mode)为例,main函数整个调用过程如下,详情参考博文《The ONE使用笔记:main函数剖析》:
main(String[] args) //DTNSim.java initSettings(confFiles, firstConfIndex); //初始化设置,读入default_settings.txt new DTNSimTextUI().start(); initModel(); //创建仿真模型 runSim(); //开始仿真 world.update(); //在每个updateInterval,处理所有到期事件
1.2 world.update
world.udate
,每隔updateInterval
处理所有到期事件,并更新所有节点。值得注意的是:每处理一个到期事件,都会更新所有节点。完了之后,处理节点的移动,再次更新所有节点。相关源代码如下:
//World.java private List<EventQueue> eventQueues; //事件队列链表,用链表将事件队列组织起来 public void update () { double runUntil = SimClock.getTime() + this.updateInterval; // /* process all events that are due until next interval update */ setNextEventQueue(); //找到一个事件队列,该事件队列含有本updateInterval可以处理的事件 while (this.nextQueueEventTime <= runUntil) { simClock.setTime(this.nextQueueEventTime); ExternalEvent ee = this.nextEventQueue.nextEvent(); //取得事件 ee.processEvent(this); //处理事件,见2 updateHosts(); //update all hosts after every event,见3 setNextEventQueue(); } moveHosts(this.updateInterval); //Moves all hosts in the world for a given amount of time simClock.setTime(runUntil); updateHosts(); }
2. 处理事件
消息的创建、转发、接收,连接的建立、撤消都可以视为事件。值得注意的是:使用MessageEventGenerator
自动创建消息,只有创建消息事件MessageCreateEvent
在world.update
的processEvent
被处理,消息转发、接收则是在相关路由器的update
被处理。我用MessageEventGenerator
产生消息,用静态移动模型(即导入外部数据集,如infocom06
)来仿真,以下是world.update
处理的部分事件:
CONN up @464.0 43<->8 CONN up @464.0 43<->17 CONN down @464.0 43<->17 CONN up @464.0 43<->23 CONN down @464.0 43<->23 CONN down @464.0 43<->44 CONN up @464.0 43<->53 CONN up @464.0 43<->57 CONN down @464.0 43<->61 MSG @464.0 M16 [8->1] size:1016 CREATE CONN up @465.0 0<->5 CONN down @465.0 0<->5 CONN down @465.0 0<->6
可见,在我的例子中,world.update
只需处理消息创建和连接建立、撤消事件。关于事件处理,可参考之前博文《The ONE使用笔记:深入源码理解消息创建过程》和《The ONE使用笔记:连接事件ConnectionEvent
读取与处理》。
3. updateHosts
3.1 节点顺序
The ONE仿真器将所有节点组织成一个ArrayList
,依次取出节点做更新。这里取出节点的顺序有两种:其一,按网络地址network address排序的;其二,随机顺序。默认是随机顺序,可在设置文件设置,相关源代码如下:
# 可在设置文件设置 Optimization.randomizeUpdateOrder = true public static final boolean DEF_RANDOMIZE_UPDATES = true; //默认是随机顺序,World.java
随机的顺序取决于当前的仿真时钟,相关源代码如下:
//World.java private List<DTNHost> hosts; //indexed by their network address private ArrayList<DTNHost> updateOrder; //randomized order //对所有节点进行随机排序,World.java的updateHosts() Random rng = new Random(SimClock.getIntTime()); Collections.shuffle(this.updateOrder, rng);
3.2 updateHosts
updateHosts
根据节点是否随机顺序,依次更新每一个节点,简化后的源代码如下:
//World.java private void updateHosts() { if (this.updateOrder == null) { //indexed by their network address for (int i=0, n = hosts.size(); i < n; i++) { hosts.get(i).update(simulateConnections); } } else { //random order for (int i=0, n = hosts.size(); i < n; i++) { this.updateOrder.get(i).update(simulateConnections); } } if (simulateConOnce && simulateConnections) { simulateConnections = false; } }
simulateConnections
用于标识网络层network layer是否需要被更新,simulateConOnce
标识只更新一次,相关源代码如下:
//World.java private boolean simulateConnections; //Should network layer be updated too private boolean simulateConOnce; //Should the connectivity simulation be stopped after one round public static final String SIMULATE_CON_ONCE_S = "simulateConnectionsOnce"; simulateConOnce = s.getBoolean(SIMULATE_CON_ONCE_S, false); //默认是false,可以在设置文件更改:Optimization.simulateConnectionsOnce
4. DTNHost.update
节点的更新,先判断节点是否处于活动状态,若不是,断开与该节点建立的所有连接;接着,判断是否需要更新网络层;最后,调用路由协议的update
。其源代码如下:
public void update(boolean simulateConnections) { if (!isRadioActive()) { // Make sure inactive nodes don't have connections tearDownAllConnections(); return; } if (simulateConnections) { for (NetworkInterface i : net) { i.update(); } } this.router.update(); }
关于router.update
,可以参考博文《The ONE使用笔记:深入源码理解消息转发过程》和《The ONE使用笔记:深入源码理解消息接收过程》。
微信赞赏
支付宝赞赏
updateHosts();在world.update里有两处出现,一处在循环内,一处在循环外,我看了您和另外位大大的讨论这两个 updateHosts();的问题,有一个疑惑:如果去掉循环外的 updateHosts();会出问题吗?循环内只要有一消息来了都要做 updateHosts();循环外还有必要吗?谢谢博主指点:)
去掉外部的updateHosts,对你仿真结果几乎没什么影响,因为updateInterval默认是0.1。实际上,The ONE这样频繁调用updateHosts,目的在于仿真现实网络,只要有消息,有邻居,信道空闲就充分使用。想像下极限的概念,只要updateHosts被调用得越频繁(可以理解为非常频繁地检查节点的状态),那么也就越能仿真现实网络了。