上一节,大体了解了一下网络协议中最上层--应用层的仿真。这一节将进行网络层(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 {nexthop下一跳结点地址,很明显是网络层地址。rssi信号强度,根据信号强度来决定路由表的更新。
LAddress::L3Type nextHop;
double rssi;
} tRouteTableEntry;
下面是路由表的定义:
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;
}
}
这个函数的功能相当于一个定时器,在指定时间向全网泛洪数据包用于建立路由表。当函数判定接收到的数据是routeFloodTimer就开始广播,并重新开始计时器。那么什么时候开始第一次的广播呢?当然是在初始化的时候指定的:
void WiseRoute::initialize(int stage)从上面代码中可以看出,仿真开始后0.5s~1.5s之间的某个时刻开始广播建立路由。
{
...........
// only schedule a flood of the node is a sink!!
if (routeFloodsInterval > 0 && myNetwAddr==sinkAddress)
scheduleAt(simTime() + uniform(0.5, 1.5), routeFloodTimer);
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找到去destAddr结点的下一跳结点。
{
// 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;
}