OMNeT++/INET中无线链路丢包率及全网丢包总数计算方法问询
如何在OMNeT++/INET中计算无线网络链路丢包率和全网丢包总数
嘿,作为刚上手OMNeT++和INET的新手,你提的这个链路丢包率统计需求非常典型——我来一步步帮你落地实现!首先得确认你的核心公式完全正确:Packet Loss Ratio = TotalLostPackets / SentPackets,而且你明确排除了目的地址不匹配的数据包,这个方向抓得很准。
核心思路拆解
我们需要实现两个核心统计目标:
- 每条双向可达链路的丢包率:每个节点要跟踪「发送给特定邻居的数据包数」和「从该邻居成功接收的、目的为本节点的数据包数」,两者的差值就是这条单向链路的丢包数,再除以发送数得到丢包率;双向链路则需要统计两个方向的数值。
- 全网丢包总数:汇总所有节点的单向链路丢包数之和即可。
步骤1:为每个节点添加链路统计模块
INET的AdhocHost已经封装了完整的网络、MAC、物理层逻辑,我们不用修改底层代码,只需要给每个主机添加一个自定义的统计模块(比如LossStatsCollector),通过订阅INET的信号来捕获数据包的收发事件。
第一步:修改NED文件
先把自定义统计模块加入到你的网络拓扑中,修改后的NED代码如下:
import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; import inet.node.inet.AdhocHost; import inet.physicallayer.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; import inet.visualizer.contract.IIntegratedVisualizer; // 导入你的自定义统计模块(替换成你实际的包路径) import your.module.path.LossStatsCollector; network Net80211 { parameters: int numHosts; @display("bgb=650,450;bgi=background/terrain"); submodules: visualizer: <default("IntegratedCanvasVisualizer")> like IIntegratedVisualizer if hasVisualizer() { parameters: @display("p=640,15;is=s"); } configurator: Ipv4NetworkConfigurator { parameters: @display("p=640,112;is=s"); } radioMedium: Ieee80211ScalarRadioMedium { parameters: @display("p=638,56;is=s"); } // 全局统计模块,用于汇总全网丢包数 globalLossStats: GlobalLossStats { parameters: @display("p=640,200;is=s"); } host[numHosts]: AdhocHost { parameters: @display("r=,,#707070;p=300,200;is=s"); submodules: lossStats: LossStatsCollector { parameters: @display("p=100,100"); } } }
第二步:编写统计模块的C++代码
这个模块需要订阅INET的数据包收发信号,记录每个邻居的收发计数,最后计算丢包率并输出结果:
#include <inet/common/INETDefs.h> #include <inet/common/packet/Packet.h> #include <inet/networklayer/common/L3Address.h> #include <inet/networklayer/common/L3AddressTag_m.h> using namespace inet; class LossStatsCollector : public cSimpleModule { protected: // 存储<目标地址, 发送数据包数> std::map<L3Address, int> sentPacketCounts; // 存储<源地址, 成功接收的数据包数(目的为本节点)> std::map<L3Address, int> receivedPacketCounts; virtual void initialize(int stage) override; virtual void receiveSignal(cComponent* source, simsignal_t signalID, cObject* obj, cObject* details) override; virtual void finish() override; }; Define_Module(LossStatsCollector); void LossStatsCollector::initialize(int stage) { cSimpleModule::initialize(stage); if (stage == INITSTAGE_APPLICATION_LAYER) { // 订阅数据包发送和接收的信号 subscribe(packetSentToLowerSignal, getParentModule()); subscribe(packetReceivedFromLowerSignal, getParentModule()); } } void LossStatsCollector::receiveSignal(cComponent* source, simsignal_t signalID, cObject* obj, cObject* details) { auto packet = check_and_cast<Packet*>(obj); auto l3Tag = packet->findTag<L3AddressInd>(); if (!l3Tag) return; // 跳过无网络层地址的包 if (signalID == packetSentToLowerSignal) { // 记录发送给目标地址的数据包数 L3Address destAddr = l3Tag->getDestAddress(); sentPacketCounts[destAddr]++; } else if (signalID == packetReceivedFromLowerSignal) { // 只统计目的地址为本节点的数据包 L3Address selfAddr = getParentModule()->par("ipv4Address").stringValue(); if (l3Tag->getDestAddress() != selfAddr) return; // 记录从源地址接收的数据包数 L3Address srcAddr = l3Tag->getSrcAddress(); receivedPacketCounts[srcAddr]++; } } void LossStatsCollector::finish() { cSimpleModule::finish(); std::string hostName = getParentModule()->getFullName(); int totalSent = 0, totalReceived = 0; // 输出每条单向链路的丢包统计 for (const auto& entry : sentPacketCounts) { L3Address destAddr = entry.first; int sent = entry.second; int received = receivedPacketCounts.count(destAddr) ? receivedPacketCounts[destAddr] : 0; int lost = sent - received; double lossRatio = sent > 0 ? static_cast<double>(lost) / sent : 0.0; // 输出到仿真日志 EV_INFO << hostName << " -> " << destAddr.str() << " | 发送: " << sent << " 接收: " << received << " 丢包: " << lost << " 丢包率: " << lossRatio * 100 << "%" << endl; // 记录到仿真结果文件 recordScalar(("LossRatio_" + destAddr.str()).c_str(), lossRatio); recordScalar(("LostPackets_" + destAddr.str()).c_str(), lost); totalSent += sent; totalReceived += received; } // 计算本节点总丢包数 int nodeTotalLost = totalSent - totalReceived; EV_INFO << hostName << " 总丢包数: " << nodeTotalLost << endl; recordScalar("NodeTotalLostPackets", nodeTotalLost); // 发送总丢包数给全局统计模块 cMessage* lossMsg = new cMessage("NodeTotalLost"); lossMsg->addPar("lostCount") = nodeTotalLost; sendDirect(lossMsg, getParentModule()->getParentModule()->getSubmodule("globalLossStats")); }
步骤2:实现全网丢包总数统计
我们需要一个全局模块来汇总所有节点的丢包数,代码如下:
#include <inet/common/INETDefs.h> using namespace inet; class GlobalLossStats : public cSimpleModule { protected: int networkTotalLost = 0; virtual void handleMessage(cMessage* msg) override; virtual void finish() override; }; Define_Module(GlobalLossStats); void GlobalLossStats::handleMessage(cMessage* msg) { if (msg->isName("NodeTotalLost")) { int nodeLost = msg->par("lostCount").intValue(); networkTotalLost += nodeLost; delete msg; } } void GlobalLossStats::finish() { cSimpleModule::finish(); EV_INFO << "全网总丢包数: " << networkTotalLost << endl; recordScalar("NetworkTotalLostPackets", networkTotalLost); }
关键注意事项
- 过滤非目标包:一定要检查接收包的目的地址是否为本节点,避免把转发的数据包误统计为接收成功的包,这会导致丢包率计算错误。
- 处理除数为0:如果某个节点没有给特定邻居发送过包,丢包率直接设为0,避免出现除以0的运行时错误。
- 结果持久化:使用
recordScalar()函数可以把统计结果写入仿真输出文件(.sca),方便后续用OMNeT++的分析工具或者Excel处理。 - 信号名称确认:如果使用的是INET最新版本,信号名称可能有细微调整,可以查阅INET的API文档确认具体的信号常量。
内容的提问来源于stack exchange,提问作者Reza
相关产品推荐
相关产品推荐

