You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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消息的序列化层级不匹配:

  1. 你的Proto定义中,csgBox是作为CsgNode的value字段来传输的,但发送端直接序列化了csgBox对象,接收端却尝试解析成CsgNode对象,结构完全不匹配。
  2. 最初代码中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:27:06