【问题标题】:Converting string to char array and then back into string将字符串转换为 char 数组,然后再转换回字符串
【发布时间】:2020-11-09 19:04:27
【问题描述】:

我正在使用 Protobuf 在带有套接字的计算机之间更改数据。要传输数据,我使用以下方法:

myProtoMessage::Message m;
string s;
m.SerializeToString(&s);
const char *msg = s.c_str();
// Send msg through socket

但是,我注意到 Protobuf 无法读取任何非 int 类型的接收数据(它将其分配为 0)。 果然,如果我不使用套接字但尝试在相同的 sn-p 代码中取回我的消息,同样会发生:

myProtoMessage::Message m;
string s;
m.SerializeToString(&s);
const char *msg = s.c_str();
myProtoMessage::Message m2;
m.ParseFromString(msg); // my data is lost

我还注意到:

myProtoMessage::Message m;
string s;
m.SerializeToString(&s);
const char *msg = s.c_str();
string(msg) == s; // 0
// Let's try it again
string s2;
int size_msg = sizeof(msg) / sizeof(char);
s2.assign(msg, size_msg);
s2 == s; // still 0
myProtoMessage::Message m2;
m.ParseFromString(s2); // the data is lost

那么,为什么我不能正确地将数据转换回字符串?如果是引用的问题,为什么protobuf不能读取char数组数据或者char数组转换后的字符串数据呢? 任何帮助将不胜感激。

编辑

我的代码的一个更好的例子: protobuf 消息(message.proto)

syntax = 'proto3';

package message;

message Message1{
    uint64 time_us = 1;
    uint32 id = 2;
}
message Message2{
    uint64 id= 1;

    double l1= 2;
    double l2 = 3;
    float l3 = 4;

    enum MyEnum {
        IDLE = 0;
    }
    MyEnum myEnum = 8;
}

message CompleteMessage{
    Message1 message1 = 1;

    Message2 message2 = 2;
}

我的代码:

#include <iostream>
#include "message.pb.h"
#include "boost/asio.hpp"

using namespace boost::asio;

using namespace std;

// Send data through socker
void send_message(const char* msg, int msg_size, const char* IP, int port)
{
  io_service io_service;
  ip::udp::socket socket(io_service);
  ip::udp::endpoint remote_endpoint;

  socket.open(ip::udp::v4());

  remote_endpoint = ip::udp::endpoint(ip::address::from_string(IP), port);

  boost::system::error_code err;
  socket.send_to(buffer(msg, msg_size), remote_endpoint, 0, err);

  socket.close();
}

// Fill message data
void fill_m1_data(message::Message1* msg, uint64_t time_us, uint32_t id)
{
  msg->set_id(id);
  msg->set_time_us(time_us);
}

// Fill message data
void fill_m2_data(message::Message2* msg, uint64_t loaded_mission_id,
                     double latitude, double longitude, float altitude, 
                     message::Message2::MyEnum state)
{
  msg->set_id(loaded_mission_id);
  msg->set_l1(latitude);
  msg->set_l2(longitude);
  msg->set_l3(altitude);
  msg->set_myEnum (state);  
}

int main(int argc, char const *argv[])
{
  message::CompleteMessage m;
  fill_m1_data(m.mutable_message1(), 1, 1);
  fill_m2_data(m.mutable_message2(), 1, 22.0, 42.0, 10.f,                          
  message::Message2::MyEnum::MyEnum_IDLE);

  // Convert protobuf data to string
  string s;
  m.SerializeToString(&s);
  const char *msg = s.c_str();
  
  // Check the data, everything is fine
  cout << "message 2" << endl;
  cout << m.message2().id() << endl;
  cout << m.message2().l1() << endl;
  cout << m.message2().l2() << endl;
  cout << m.message2().l3() << endl;
  cout << m.message2().myEnum() << endl;
  cout << "message 1" << endl;
  cout << m.message1().id() << endl;
  cout << m.message1().time_us() << endl;

  // Get back the data
  message::CompleteMessage m2;
  m2.ParseFromString(msg); // get data back
  // converting msg back to string by the methods above yields the same results
  cout << "message 2" << endl;
  cout << m.message2().id() << endl;
  cout << m.message2().l1() << endl; // 0, should be 22.0
  cout << m.message2().l2() << endl; // 0, should be 42.0
  cout << m.message2().l3() << endl; // 0, should be 10.0f
  cout << m.message2().myEnum() << endl;
  cout << "message 1" << endl;
  cout << m.message1().id() << endl;
  cout << m.message1().time_us() << endl;

  // Send the message
  send_message(msg, s.length() - 1, "192.168.0.0", 9000);
  
  return 0;
}

Protobuf 文档:https://developers.google.com/protocol-buffers/docs/cpptutorial

编辑 2

以下代码在通过套接字发送和接收数据时起作用:

// send
string s;
msg.SerializeToString(&s);
void *buffer = malloc(s.size());
msg.SerializeToArray(buffer, s.size());
send(fd, buffer, s.size(), 0);
free(buffer);
// receive
numbytes = recv(connectfd, buf, MAXDATASIZE, 0);
buf[numbytes] = '\0';
// Receive  msg from clients
message::CompleteMessage m;
m.ParsePartialFromArray(buf, sizeof(buf) / sizeof(char)); // sizeof(buf) / sizeof(char) can be changed to numbytes 

不是最干净的,但可以完成工作。但是关于字符串和字符数组的问题仍然存在。

【问题讨论】:

  • 看不懂const char*的用法,sizeof(msg)/sizeof(char)肯定是错的。您能否提供minimal reproducible example,参考有关如何使用带字符串的 ProtoBuf 消息的文档?
  • 什么是mv? ..
  • @JohnFilleau,mv 是错字,应该是 m。
  • @AsteroidsWithWings 我会用一个例子来编辑这个问题。
  • 检查您的序列化和反序列化函数调用的返回值。必须在消息中设置所有必填字段才能使序列化工作。

标签: c++ protocol-buffers


【解决方案1】:

从 protobuf 消息序列化的字符串是二进制字符串。您不能使用单个二进制 char 指针构造 std::string。相反,您还应该指定二进制字符串的大小。

myProtoMessage::Message m;
string s;
m.SerializeToString(&s);
const char *msg = s.c_str();
myProtoMessage::Message m2;

// in this case, msg will be converted to std::string,
// and will be truncated when it meets the first '\0'.
// That's why your data is lost
// m.ParseFromString(msg); // my data is lost

// Instead, you should call `ParseFromArray` to specify the size of the string buffer.
m.ParseFromArray(msg, s.size());

// Or construct a std::string manually.
m.ParseFromString(std::string(msg, s.size()));
myProtoMessage::Message m;
string s;
m.SerializeToString(&s);
const char *msg = s.c_str();
// Send msg through socket

// When you send msg, also specify the size of the message:
// send(sock, msg, s.size(), flags);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-19
    • 2018-09-18
    • 2011-12-18
    • 2023-02-01
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 2020-02-20
    相关资源
    最近更新 更多