将字符串转换为char数组,然后再转换回字符串

时间:2020-11-08 17:56:56

标签: c++ protocol-buffers

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

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

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

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 

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

1 个答案:

答案 0 :(得分: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);