Protobuf与ActiveMQ-cpp中const char*转const unsigned char*的问题
Protobuf序列化内容通过ActiveMQ-cpp的ByteMessage传输解析异常问题
背景
我知道这个问题被多次提及,但现有答案无法解决我的场景。我通过Protobuf生成了类,想要结合ActiveMQ-cpp进行测试。
问题
我需要使用ByteMessage,但createBytesMessage()仅接受unsigned char*作为参数。我的疑问是:如何将std::string(或const char*)转换为const unsigned char*?同时当前接收端解析Protobuf内容异常,无法得到正确的消息结构。
发送端代码
for (int ix = 0; ix < m_numMessages; ++ix) { // 创建Protobuf生成的csgBox对象 csgBox csgNode; csgNode.set_n("box"); csgNode.set_h(1600.0f); csgNode.set_l(1600.0f); csgNode.set_w(1600.0f); // 问题出在这里:尝试将csgNode转为unsigned char数组 std::string csgNodeStr = csgNode.SerializeAsString(); auto msg = (const unsigned char *)csgNodeStr.c_str(); // 创建BytesMessage constexpr int msgSize = sizeof(msg); std::unique_ptr<cms::BytesMessage> message(m_session->createBytesMessage(msg, msgSize)); // 无编译错误,但接收端解析异常 LOG_INFO("Sent message: " << ix << " csgNode: " << csgNode.DebugString()); m_producer->send(message.get()); }
接收端代码
const cms::BytesMessage* msg = dynamic_cast<const cms::BytesMessage*> (message); const unsigned char* bytes = nullptr; if (msg != nullptr) { bytes = msg->getBodyBytes(); } CsgNode csgNode; csgNode.ParseFromArray(bytes, msg->getBodyLength()); LOG_INFO("Received message: " << count << " csgNode: " << csgNode.DebugString());
输出结果
[INFO] Sent message: 0 csgNode: n: "box" l: 1600 w: 1600 h: 1600 [INFO] Sent message: 1 csgNode: n: "box" l: 1600 w: 1600 h: 1600 [INFO] Received message: 1 csgNode: type: "box" 3: 0x00000000 [INFO] Received message: 2 csgNode: type: "box" 3: 0x00000000
Protobuf定义文件
message CsgNode { string type = 1; // 解码为以下类型之一: // message csgDifference, serial name 'csgDifference' // message csgIntersection, serial name 'csgIntersection' // message csgUnion, serial name 'csgUnion' // message csgCut, serial name 'csgCut' // message csgMesh, serial name 'csgMesh' // message csgBox, serial name 'csgBox' // message csgCone, serial name 'csgCone' // message csgCylinder, serial name 'csgCylinder' // message csgEmpty, serial name 'csgEmpty' // message csgSphere, serial name 'csgSphere' // message csgTorus, serial name 'csgTorus' bytes value = 2; } message csgBox { string n = 1; repeated float t = 2; float l = 3; float w = 4; float h = 5; Color4d cl = 6; }
补充尝试
根据建议,我尝试了两种方案:
方案一
// 尝试将csgNode转为unsigned char数组 std::string csgNodeStr = csgNode.SerializeAsString(); auto msg = (const unsigned char *)csgNodeStr.c_str(); std::unique_ptr<cms::BytesMessage> message(m_session->createBytesMessage(msg, csgNodeStr.size()));
方案二
std::string csgNodeStr = csgNode.SerializeAsString(); const char* signedArr = csgNodeStr.c_str(); const int msgSize = (int)csgNodeStr.size(); unsigned char* msg = new unsigned char[msgSize + 1]; for (int i = 0; i < msgSize; ++i) { msg[i] = static_cast<unsigned char>(signedArr[i]); } msg[msgSize + 1] = 0; std::unique_ptr<cms::BytesMessage> message(m_session->createBytesMessage(msg, csgNodeStr.size()));
尝试后的输出
[INFO] Sent message: 0 csgNode: n: "box" l: 1600 w: 1600 h: 1600 [INFO] Sent message: 1 csgNode: n: "box" l: 1600 w: 1600 h: 1600 [INFO] Received message: 1 csgNode: type: "box" 3: 0x44c80000 4: 0x44c80000 5: 0x44c80000 [INFO] Received message: 2 csgNode: type: "box" 3: 0x44c80000 4: 0x44c80000 5: 0x44c80000
结果更接近预期,但仍存在问题,我不清楚遗漏了什么。
解决方案
核心问题分析
你当前的问题本质不是std::string转const unsigned char*的转换,而是Protobuf消息的序列化层级不匹配:
- 你的Proto定义中,
csgBox是作为CsgNode的value字段来传输的,但发送端直接序列化了csgBox对象,接收端却尝试解析成CsgNode对象,结构完全不匹配。 - 最初代码中
constexpr int msgSize = sizeof(msg);是致命错误:msg是指针,sizeof(msg)得到的是指针的字节数(通常4或8),而非序列化后的字符串长度,导致发送内容不完整。
修正后的发送端代码
for (int ix = 0; ix < m_numMessages; ++ix) { // 创建具体的csgBox对象 csgBox boxNode; boxNode.set_n("box"); boxNode.set_h(1600.0f); boxNode.set_l(1600.0f); boxNode.set_w(1600.0f); // 创建上层的CsgNode对象,符合Proto定义的继承结构 CsgNode csgNode; csgNode.set_type("csgBox"); // 对应Proto注释中的serial name boxNode.SerializeToString(csgNode.mutable_value()); // 将box序列化到value字段 // 序列化整个CsgNode std::string serializedData = csgNode.SerializeAsString(); // 安全转换const char*到const unsigned char*,内存布局兼容 const unsigned char* msgBytes = reinterpret_cast<const unsigned char*>(serializedData.c_str()); // 创建BytesMessage,传入正确的序列化长度 std::unique_ptr<cms::BytesMessage> message(m_session->createBytesMessage(msgBytes, serializedData.size())); LOG_INFO("Sent message: " << ix << " csgNode: " << csgNode.DebugString()); m_producer->send(message.get()); }
接收端扩展(解析具体类型)
接收端原有代码可以正确解析CsgNode,若需要进一步解析出csgBox对象,可添加以下逻辑:
// 接收端解析完CsgNode后,进一步解析具体类型 if (csgNode.type() == "csgBox") { csgBox boxNode; boxNode.ParseFromString(csgNode.value()); LOG_INFO("Parsed csgBox: " << boxNode.DebugString()); }
关于std::string转const unsigned char*的说明
- 直接使用
reinterpret_cast<const unsigned char*>(str.c_str())完全安全:std::string底层是连续的char数组,unsigned char与char内存布局兼容,符号位差异对Protobuf二进制数据无影响。 - 无需手动拷贝数组(方案二的做法),不仅多余,还容易出现越界错误(比如方案二中
msg[msgSize + 1] = 0;会访问超出数组范围的内存)。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

