OMNeT++ GPSR协议数据包超时重传功能实现问题求助
解决OMNeT++中GPSR协议超时重传无法注入网络层的问题
看起来你在扩展GPSR协议的重传机制时踩了几个典型的框架接口与权限坑,我结合OMNeT++/INET的设计逻辑给你针对性的解决方案:
1. 放弃修改IPv4.cc的protected方法,用公开接口注入重传数据包
直接修改IPv4的protected方法会破坏框架封装,还引发后续的抽象类实例化问题,完全没必要。INET提供了公开的接口让你把数据包重新注入网络层,模拟上层发送的流程:
在你的GPSR模块的超时处理函数中,通过以下方式获取IPv4模块引用,调用公开的receivePacket方法注入重传包:
// 获取当前节点的IPv4模块(需要在NED文件中配置ipv4Module参数指向对应模块) IPv4 *ipv4Module = getModuleFromPar<IPv4>(par("ipv4Module"), this); // 获取无线接口(替换为你实际使用的接口名,比如wlan0) InterfaceEntry *wirelessIface = ipv4Module->getInterfaceByName("wlan0"); // 重置重传包的必要字段(比如TTL减1、标记重传次数) auto ipv4Header = retransmitPacket->removeAtFront<IPv4Header>(); ipv4Header->setTTL(ipv4Header->getTTL() - 1); retransmitPacket->insertAtFront(ipv4Header); // 将重传包注入网络层,触发完整的路由与发送流程 ipv4Module->receivePacket(retransmitPacket, wirelessIface, false);
这个方法会让数据包重新走一遍从上层到网络层的处理流程,自然会触发你注册的Netfilter Hook逻辑,完美绕开权限问题。
2. 用DefaultHook避免INetfilter抽象类实例化错误
INetfilter::IHook是纯抽象类,必须实现所有5个Hook方法,如果你只用到其中两个,直接继承INET提供的INetfilter::DefaultHook即可——它已经给所有Hook方法提供了默认的ACCEPT返回值,你只需要重写需要的方法:
class GPSRNetfilterHook : public INetfilter::DefaultHook { public: // 只重写你需要的两个Hook方法 FilterResult datagramPreRoutingHook(Packet *packet, const InterfaceEntry *inputIE, const IPv4Address& destAddr) override { // 你的预处理逻辑 return ACCEPT; } FilterResult datagramLocalOutHook(Packet *packet, const InterfaceEntry *outputIE, const IPv4Address& destAddr) override { // 你的发送队列存入、定时器调度逻辑 return ACCEPT; } // 其他3个Hook方法使用DefaultHook的默认实现,无需自己编写 };
然后在GPSR模块初始化时注册这个Hook:
void GPSR::initialize(int stage) { if (stage == INITSTAGE_NETWORK_LAYER) { IPv4 *ipv4Module = getModuleFromPar<IPv4>(par("ipv4Module"), this); auto gpsrHook = new GPSRNetfilterHook(); // 注册到对应的Hook点 ipv4Module->getNetfilter()->registerHook(gpsrHook, 0, INetfilter::HOOK_PRE_ROUTING); ipv4Module->getNetfilter()->registerHook(gpsrHook, 0, INetfilter::HOOK_LOCAL_OUT); } }
这样就不会再出现抽象类无法实例化的报错。
3. 修复postRoutingHook无法触发发送的问题
postRoutingHook的定位是路由决策后、接口发送前的处理,它本身不负责触发发送动作。你需要让重传包重新走完整的网络层流程(也就是上面提到的receivePacket方式),这样数据包会重新经过路由选择,最终触发接口发送,完全不需要依赖postRoutingHook来启动发送。
4. 发送队列与定时器的配套优化
为了避免无效重传和内存泄漏,建议给每个待重传包绑定定时器消息,用数据包ID做跟踪:
// 发送数据包时,创建定时器并关联数据包 void GPSR::sendPacketWithRetransmit(Packet *packet) { cMessage *retransmitTimer = new cMessage("retransmitTimer"); retransmitTimer->setContextPointer(packet); scheduleAt(simTime() + par("retransmitTimeout"), retransmitTimer); // 将数据包存入发送队列,用ID做键 sentPacketQueue[packet->getId()] = packet; } // 定时器触发时的处理 void GPSR::handleMessage(cMessage *msg) { if (msg->isName("retransmitTimer")) { Packet *targetPacket = check_and_cast<Packet*>(msg->getContextPointer()); // 检查队列中是否还存在该包(已转发则移除) if (sentPacketQueue.count(targetPacket->getId())) { // 执行重传逻辑(调用前面的receivePacket方法) // ... // 如果需要多次重传,重新调度定时器 scheduleAt(simTime() + par("retransmitTimeout"), new cMessage("retransmitTimer", targetPacket->getId())); } delete msg; } // 其他消息处理逻辑 } // 收到转发确认时,移除队列并取消定时器 void GPSR::onPacketForwarded(int packetId) { if (sentPacketQueue.count(packetId)) { cancelAndDelete(findTimerByPacketId(packetId)); // 实现一个根据ID找定时器的方法 sentPacketQueue.erase(packetId); } }
内容的提问来源于stack exchange,提问作者Naveen Kumar Gupta
相关产品推荐
相关产品推荐

