简体   繁体   English

C ++ Linux Google Protobuf + boost :: asio无法解析

[英]C++ Linux Google Protobuf + boost::asio Cannot Parse

I am trying to send a Google Protobuf message over a boost::asio socket via TCP. 我试图通过TCP在boost :: asio套接字上发送Google Protobuf消息。 I recognize that TCP is a streaming protocol and thus I am performing length-prefixing on the messages before they go through the socket. 我认识到TCP是一种流协议,因此我在消息通过套接字之前对消息执行长度前缀。 I have the code working, but it only appears to work some of the time, even though I'm repeating the same calls and not changing the environment. 我有代码工作,但它似乎只在某些时候工作,即使我重复相同的调用而不是改变环境。 On occasion I will receive the following error: 有时我会收到以下错误:

[libprotobuf ERROR google/protobuf/message_lite.cc:123] Can't parse message of type "xxx" because it is missing required fields: Name, ApplicationType, MessageType [libprotobuf错误google / protobuf / message_lite.cc:123]无法解析“xxx”类型的消息,因为它缺少必填字段:Name,ApplicationType,MessageType

The reason is easy to understand, but I cannot single out why this only occurs sometimes and parses just fine the majority of the time. 原因很容易理解,但我不明白为什么这种情况有时只会发生,并且在大多数情况下解析得很好。 It is very easy to duplicate the error by just having a single client talking to the server and simply restarting the processes. 通过让单个客户端与服务器通信并简单地重新启动进程来复制错误非常容易。

Below are the socket code snippets. 下面是套接字代码片段。

const int TCP_HEADER_SIZE = 8;

Sender: 发件人:

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));
}

Receiver: 接收器:

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: ReadFunc:

void HandleIncomingData(const char *data, const std::size_t size) {
    xxx::messaging::CMSMessage proto;
    proto.ParseFromArray(data, static_cast<int>(size));
}

I should mention that I need this to be as fast as possible, so any optimizations would be very much appreciated as well. 我应该提到我需要尽可能快,所以任何优化都会非常受欢迎。

The program invokes undefined behavior as it fails to meet a lifetime requirement for boost::asio::async_write() 's buffers parameter: 该程序调用未定义的行为,因为它无法满足boost::asio::async_write()buffers参数的生命周期要求:

[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called. [...]底层内存块的所有权由调用者保留,调用者必须保证它们在调用处理程序之前保持有效。

Within the Write() function, boost::asio::async_write() will return immediately, and potentially cause data to go out of scope before the asynchronous write operation has completed. Write()函数中, boost::asio::async_write()将立即返回,并可能导致data在异步写入操作完成之前超出范围。 To resolve this, consider expanding the life of the underlying buffer, such as by associating the buffer with the operation and performing cleanup in the handler, or making the buffer a data member on TCPSender . 要解决此问题,请考虑扩展底层缓冲区的生命周期,例如将缓冲区与操作相关联并在处理程序中执行清理,或使缓冲区成为TCPSender上的数据成员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM