【发布时间】:2015-10-14 09:04:59
【问题描述】:
我正在尝试通过 TCP 通过 boost::asio 套接字发送 Google Protobuf 消息。我认识到 TCP 是一种流协议,因此我在消息通过套接字之前对其执行长度前缀。我有代码工作,但它似乎只在某些时候工作,即使我重复相同的调用而不改变环境。有时我会收到以下错误:
[libprotobuf ERROR google/protobuf/message_lite.cc:123] 无法解析“xxx”类型的消息,因为它缺少必填字段:名称、应用程序类型、消息类型
原因很容易理解,但我不能单独指出为什么这种情况有时会发生并且在大多数情况下解析得很好。只需让一个客户端与服务器通信并简单地重新启动进程,就很容易复制错误。
下面是socket代码sn-ps。
const int TCP_HEADER_SIZE = 8;
发件人:
bool Write(const google::protobuf::MessageLite& proto) {
char header[TCP_HEADER_SIZE];
int size = proto.ByteSize();
char data[TCP_HEADER_SIZE + size];
sprintf(data, "%i", size);
proto.SerializeToArray(data+TCP_HEADER_SIZE, size);
boost::asio::async_write(Socket,
boost::asio::buffer(data, TCP_HEADER_SIZE + size),
boost::bind(&TCPSender::WriteHandler,
this, _1, _2));
}
接收者:
std::array<char, TCP_HEADER_SIZE> Header;
std::array<char, 8192> Bytes;
void ReadHandler(const boost::system::error_code &ec,
std::size_t bytes_transferred) {
if(!ec) {
int msgsize = atoi(Header.data());
if(msgsize > 0) {
boost::asio::read(Socket, boost::asio::buffer(Bytes,static_cast<std::size_t>(msgsize)));
ReadFunc(Bytes.data(), msgsize);
}
boost::asio::async_read(Socket, boost::asio::buffer(Header, TCP_HEADER_SIZE),
boost::bind(&TCPReceiver::ReadHandler, this, _1, _2));
}
else {
std::cerr << "Server::ReadHandler::" << ec.message() << '\n';
}
}
ReadFunc:
void HandleIncomingData(const char *data, const std::size_t size) {
xxx::messaging::CMSMessage proto;
proto.ParseFromArray(data, static_cast<int>(size));
}
我应该提一下,我需要它尽可能快,所以任何优化都将非常感谢。
【问题讨论】:
-
略读,我没有看到明显的问题,但关于优化,请查看
MessageLite::SerializeWithCachedSizesToArray()。您当前的代码实际上计算了两次ByteSize()。如果您还没有,也可以尝试在调试模式下编译(没有-DNDEBUG);然后将在序列化时(而不仅仅是解析时)检查必填字段,这样您就可以排除这方面的错误。 -
谢谢你,我没有意识到protobuf已经内置了这样的优化。我将不得不考虑更多地使用 CodedOutputStream,但我认为这可能是正确的轨道。
-
如果您查看 message_lite.cc 中的定义,您可以了解各种序列化/解析方法在底层做了什么,而无需太多工作。通常,如果您了解包装器方法的作用,则不需要直接使用“编码流”。
标签: c++ linux tcp boost-asio protocol-buffers