上一节,大体了解了一下网络协议中最上层--应用层的仿真。这一节将进行网络层(networklayer)。
无线传感器网络中的路由协议和算法都是在网络层中实现的,所以对这一层的理解的好坏对协议和算法的设计与仿真这关重要。
在omnet++IDE中,双击我用红色矩形标记的图标(网络层模块),打开网络层NED文件,默认进入design模式。
我们发现,这里仅仅给了一个网络层的接口。具体使用那个模块需要我们自己指定。host802154_2400MHz的祖先结点wirelessnode指定了默认的networkType = default("BaseNetwLayer"),也就是说使用basenetwlayer模块。networkType 这个参数指定了host802154_2400MHz结点网络层使用的模块。
好吧,下面查看BaseNetwLayer模块的源码。
simple BaseNetwLayer extends BaseLayer like IBaseNetwLayer { parameters: @class(BaseNetwLayer); bool coreDebug = default(false); // debug switch for core framework bool stats = default(false); // stats switch int headerLength @unit(bit); // length of the network packet header (in bits) }
basenetwlayer的NED文件很简单。这个模块是基模块,是用来继承的。当我们实现自己的网络层协议和算法时,自己的网络层模块去继承basenetwlayer,这样,网络层的大部分功能就已经实现了,剩下的是添加自己的算法代码。下面是basenetwlayer.h中定义的成员变量和成员方法,通过查看这些成员方法就可以看出这个模块的大致功能了。basenetwlayer这个模块大致功能包括:初始化,对上下层数据进行封包或解包....。代码中我一一注明功能。
class MIXIM_API BaseNetwLayer : public BaseLayer { private: BaseNetwLayer(const BaseNetwLayer&);//拷贝构造函数,这里写出原型,意味着默认拷贝构造函数不可用 BaseNetwLayer& operator=(const BaseNetwLayer&);//<span style="font-family: Arial, Helvetica, sans-serif;">拷贝构造函数,这里写出原型,意味着默认拷贝构造函数不可用</span> public: typedef NetwPkt* netwpkt_ptr_t;//网络层数据包 enum BaseNetwMessageKinds { /** @brief Stores the id on which classes extending BaseNetw should * continue their own message kinds.*/ LAST_BASE_NETW_MESSAGE_KIND = 24000, }; /** @brief Control message kinds used by this layer.*/ enum BaseNetwControlKinds { /** @brief Stores the id on which classes extending BaseNetw should * continue their own control kinds.*/ LAST_BASE_NETW_CONTROL_KIND = 24500, }; protected: <span style="white-space:pre"> </span>/*下面变量从<span style="font-family: Arial, Helvetica, sans-serif;">omnetpp.ini中读取*/</span> int headerLength;//数据包大小 /** @brief Pointer to the arp module*/ ArpInterface* arp; LAddress::L3Type myNetwAddr;//网络层地址,<span style="font-family: Arial, Helvetica, sans-serif;">LAddress::L3Type代表网络层,</span><span style="font-family: Arial, Helvetica, sans-serif;">LAddress::L2Type代表mac层地址</span> bool coreDebug; public: BaseNetwLayer() : BaseLayer() , headerLength(0) , arp(NULL) , myNetwAddr() , coreDebug(false) {} BaseNetwLayer(unsigned stacksize) : BaseLayer(stacksize) , headerLength(0) , arp(NULL) , myNetwAddr() , coreDebug(false) {} /** @brief Initialization of the module and some variables*/ virtual void initialize(int); protected: virtual void handleUpperMsg(cMessage* msg);// 处理上层来的数据 virtual void handleLowerMsg(cMessage* msg);// 处理下层来的数据 virtual void handleSelfMsg(cMessage* /*msg*/){ error("BaseNetwLayer does not handle self messages"); } virtual void handleLowerControl(cMessage* msg); virtual void handleUpperControl(cMessage* /*msg*/){ error("BaseNetwLayer does not handle control messages"); } virtual cMessage* decapsMsg(netwpkt_ptr_t); //对数据解包以备发送到上层 virtual netwpkt_ptr_t encapsMsg(cPacket*); // 对数据封包以备发送到下层 virtual cObject* setDownControlInfo(cMessage *const pMsg, const LAddress::L2Type& pDestAddr); virtual cObject* setUpControlInfo(cMessage *const pMsg, const LAddress::L3Type& pSrcAddr); };
在仿真中不用这个模块,我们使用它的子模块wiseroute。下面是WiseRoute的NED定义:
simple WiseRoute extends BaseNetwLayer { parameters: // debug switch bool debug = default(false); bool trace = default(false); bool useSimTracer = default(false); // sink node address (integer) int sinkAddress = default(0); // the sink directs the tree building procedure with periodic floods. // iterationDelay is the period between two floods. // RSSI threshold for route selection double rssiThreshold @unit(dBm) = default(-50 dBm); // If set to zero, this node does not initiates route tree building. // If set to a value larger than zero, this nodes periodically initiates route tree building. double routeFloodsInterval @unit(s) = default(0 s);// 默认是0即不更新路由表,但是在omnetpp.ini中设置其1200s,表示20分钟广播更新一次路由。 @display("i=block/fork"); @class(WiseRoute); }
wiseroute在网络层中的功能大致是:建立路由表,并且周期性的维护路由表。根据路由表选择下一跳结点,将数据包发送到下一跳结点上。
从WiseRoute模块的c++头文件开始看起。找到路由表中的一个表项(entry)的定义:
typedef struct tRouteTableEntry { LAddress::L3Type nextHop; double rssi; } tRouteTableEntry;nexthop下一跳结点地址,很明显是网络层地址。rssi信号强度,根据信号强度来决定路由表的更新。
下面是路由表的定义:
typedef std::map<LAddress::L3Type, tRouteTableEntry> tRouteTable;
<span style="white-space:pre"> </span>tRouteTable routeTable;路由表数据类型troutetable定义成一个map的数据结构。定义路由表 routeTable。
下面看看WiseRoute模块的c++源文件中的关键函数:
void WiseRoute::handleSelfMsg(cMessage* msg) { if (msg->getKind() == SEND_ROUTE_FLOOD_TIMER) { // Send route flood packet and restart the timer WiseRoutePkt* pkt = new WiseRoutePkt("route-flood", ROUTE_FLOOD); pkt->setByteLength(headerLength); pkt->setInitialSrcAddr(myNetwAddr); pkt->setFinalDestAddr(LAddress::L3BROADCAST); pkt->setSrcAddr(myNetwAddr); pkt->setDestAddr(LAddress::L3BROADCAST); pkt->setNbHops(0); floodTable.insert(make_pair(myNetwAddr, floodSeqNumber)); pkt->setSeqNum(floodSeqNumber); floodSeqNumber++; pkt->setIsFlood(1); setDownControlInfo(pkt, LAddress::L2BROADCAST); sendDown(pkt); nbFloodsSent++; nbRouteFloodsSent++; scheduleAt(simTime() + routeFloodsInterval + uniform(0, 1), routeFloodTimer); } else { EV << "WiseRoute - handleSelfMessage: got unexpected message of kind " << msg->getKind() << endl; delete msg; } }
void WiseRoute::initialize(int stage) { ........... // only schedule a flood of the node is a sink!! if (routeFloodsInterval > 0 && myNetwAddr==sinkAddress) scheduleAt(simTime() + uniform(0.5, 1.5), routeFloodTimer);从上面代码中可以看出,仿真开始后0.5s~1.5s之间的某个时刻开始广播建立路由。
void WiseRoute::updateRouteTable(const LAddress::L3Type& origin, const LAddress::L3Type& lastHop, double rssi, double ber) { tRouteTable::iterator pos; pos = routeTable.find(origin); if(trace) { receivedRSSI.record(rssi); receivedBER.record(ber); } if (pos == routeTable.end()) { // A route towards origin does not exist yet. Insert the currently discovered one // only if the received RSSI is above the threshold. if (rssi > rssiThreshold) { tRouteTableEntry newEntry; // last hop from origin means next hop towards origin. newEntry.nextHop = lastHop; newEntry.rssi = rssi; if(trace) { routeRSSI.record(rssi); routeBER.record(ber); } routeTable.insert(make_pair(origin, newEntry)); if(useSimTracer) { tracer->logLink(myNetwAddr, lastHop); } nbRoutesRecorded++; if (origin == LAddress::L3NULL && trace) { nextHopSelectionForSink.record(static_cast<double>(lastHop)); } } } else { } }
这个函数功能是更新路由。更新的条件是1.在当前路由中没有发现到origin结点的路由。2.信号强度要比设置的阈值大。
WiseRoute::tFloodTable::key_type WiseRoute::getRoute(const tFloodTable::key_type& destAddr, bool /*iAmOrigin*/) const { // Find a route to dest address. As in the embedded code, if no route exists, indicate // final destination as next hop. If we'are lucky, final destination is one hop away... // If I am the origin of the packet and no route exists, use flood, hence return broadcast // address for next hop. tRouteTable::const_iterator pos = routeTable.find(destAddr); if (pos != routeTable.end()) return pos->second.nextHop; else return LAddress::L3BROADCAST; }找到去destAddr结点的下一跳结点。