C ++ Linux Google Protobuf + boost :: asio Unable to parse

I am trying to send google Protobuf post over boost :: asio socket over TCP. I understand that TCP is a streaming protocol and therefore I prefix the length on messages before they go through the socket. The code works for me, but it only works for a while, although I repeat the same calls and don't change the environment. Sometimes I get the following error:

[libprotobuf ERROR google / protobuf / message_lite.cc: 123] Unable to parse message like "xxx" because it lacks required fields: Name, ApplicationType, MessageType

The reason is simple to understand, but I cannot isolate why this only happens sometimes, and analyzes just fine in most cases. It is very easy to duplicate the error by just having one client talking to the server and just restarting the processes.

Following are the code snippets of the socket.

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

      

Recipient:

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

      

I must mention that I need this to be as fast as possible, so any optimizations would be greatly appreciated.

+3


source to share


1 answer


The program calls undefined behavior because it doesn't meet the timing requirements :boost::asio::async_write()

buffers

[...] ownership of the underlying blocks of memory is retained by the caller, which should ensure that they remain valid until the handler is called.



Inside the function Write()

boost::asio::async_write()

will return immediately and potentially cause it to data

disappear from scope until the asynchronous write operation completes. To solve this problem, consider extending the lifespan of the underlying buffer, for example by associating the buffer with an operation and performing the cleanup in a handler, or making the buffer a data member by TCPSender

.

+3


source







All Articles