实现Gossip协议时JOINREP消息传递成员表出现垃圾值求助
问题分析与修复方案
嘿,我一眼就看出问题出在哪了——这完全是消息构造和解析的代码错误,和虚拟网络节点故障没有关系!那些奇怪的大数字心跳值其实是内存地址,说明你在序列化和反序列化成员列表的时候完全搞错了对象。
核心错误点拆解
1. 发送JOINREP时的序列化错误
你当前的代码犯了两个致命错误:
- 消息大小计算错误:
sizeof(memberNode->memberList)得到的是vector容器对象本身的大小(比如64位系统下通常是24字节,包含指针、容量、大小三个字段),而不是列表中所有成员节点的总字节数。这直接导致消息长度不够,或者后续拷贝越界。 - 拷贝的是容器对象而非元素数据:
memcpy((char *)(msg+1), &memberNode->memberList, sizeof(memberNode->memberList))拷贝的是vector的内部结构(比如指向堆内存的指针),而不是它存储的实际成员节点数据。接收方拿到这个指针后,指向的是发送方进程的内存(虚拟网络中进程内存是隔离的),自然读出来的是垃圾值。
2. 接收JOINREP时的反序列化错误
memcpy(&(memberNode->memberList), (char *)(msg+1), size - sizeof(msg+1))直接往vector对象内存里拷贝数据,完全破坏了vector的内部管理结构。vector依赖自己的内存分配逻辑,直接覆盖它的内部指针会导致读取到无效内存,也就是你看到的那些随机大数字。
修复后的代码示例
假设你的成员节点类型是Member(请根据实际定义调整),修复后的代码如下:
发送JOINREP的修正代码
if(msg->msgType == JOINREQ) { // ... 原有添加成员到列表的代码保持不变 ... // 计算正确的消息大小:消息头 + 所有成员节点的总字节数 using Member = decltype(memberNode->memberList)::value_type; size_t memberSize = sizeof(Member); size_t msgsize = sizeof(MessageHdr) + memberNode->memberList.size() * memberSize; msg = (MessageHdr *) malloc(msgsize); msg->msgType = JOINREP; // 拷贝成员列表的实际元素数据,而非vector对象本身 memcpy((char *)(msg+1), memberNode->memberList.data(), memberNode->memberList.size() * memberSize); emulNet->ENsend(&memberNode->addr, replyAddr, (char *)msg, msgsize); // ... 原有日志和内存释放代码保持不变 ... }
接收JOINREP的修正代码
} else if(msg->msgType == JOINREP) { using Member = decltype(memberNode->memberList)::value_type; size_t memberSize = sizeof(Member); // 计算收到的成员节点数量 int memberCount = (size - sizeof(MessageHdr)) / memberSize; // 清空现有列表并逐个添加新成员 memberNode->memberList.clear(); char* memberData = (char*)(msg+1); for(int i = 0; i < memberCount; ++i) { Member newMember; memcpy(&newMember, memberData + i * memberSize, memberSize); memberNode->memberList.push_back(newMember); } // ... 原有日志代码保持不变 ... }
为什么每次都是同一节点出问题?
因为你错误地拷贝了vector的内部指针,每次运行时发送方的vector内部指针地址是固定的(对于同一个节点来说),接收方读取这个无效指针指向的内存时,就会表现为同一个节点(ID0)的心跳值异常,而每次运行的地址不同,所以垃圾值也不一样。
内容的提问来源于stack exchange,提问作者Salman Hamid
相关产品推荐
相关产品推荐

