Reading a serialized struct at the receiver end boost asio

老子叫甜甜 提交于 2020-06-28 03:42:23

问题


I am new to boost and networking ;). I am making a client server application with boost::asio, I need to pass structs as messages so used boost::asio::serialization for it :

test.h

#pragma once

#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
struct Test
{
    public:
        int a;
        int b;
        template<typename archive> void serialize(archive& ar, const unsigned version) {
        ar & a;
        ar & b;
    }
};

client side sending:

void send_asynchronously(tcp::socket& socket) {
        Test info;

        info.a = 1;
        info.b = 2;

        {
            std::ostream os(&buf);
            boost::archive::binary_oarchive out_archive(os);
            out_archive << info;
        }

        async_write(socket, buf, on_send_completed);
    }

On the receiver side, I read the data into a boost::asio::buffer, I want to know a way to parse this buffer and extract the object on server side. Please help.


回答1:


You don't show enough code to know how you declared buf or managed the lifetime.

I'm assuming you used boost::asio::streambuf buf; and it has static storage duration (namespace scope) or is a class member (but you didn't show a class).

Either way, whatever you have you can do "the same" in reverse to receive.

Here's a shortened version (that leaves out the async so we don't have to make guesses about the lifetimes of things like I mentioned above);

Connect

Let's connect to an imaginary server (we can make one below) at port 3001 on localhost:

asio::io_context ioc;
asio::streambuf buf;
tcp::socket s(ioc, tcp::v4());
s.connect({{}, 3001});

Serialize

Basically what you had:

{
    std::ostream os(&buf);
    boost::archive::binary_oarchive oa(os);

    Test req {13,31};
    oa << req;
}

Note the {} scope around the stream/archive make sure the archive is completed before sending.

Send

/*auto bytes_sent =*/ asio::write(s, buf);

Receive

Let's assume our server sends back another Test object serialized in the same way¹.

Reading into the buffer, assuming no framing we'll just "read until the end of the stream":

boost::system::error_code ec;
/*auto bytes_received =*/ asio::read(s, buf, ec);
if (ec && ec != asio::error::eof) {
    std::cout << "Read error: " << ec.message() << "\n";
    return 1;
}

In real life you want timeouts and limits to the amount of data read. Often your protocol will add framing where you know what amount of data to read or what boundary marker to expect.

Deserialize

Test response; // uninitialized
{
    std::istream is(&buf);
    boost::archive::binary_iarchive ia(is);

    ia >> response;
}

Full Demo

Live On Coliru

#include <boost/asio.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <iostream>
namespace asio = boost::asio;
using tcp = boost::asio::ip::tcp;

struct Test {
    int a,b;
    template<typename Ar> void serialize(Ar& ar, unsigned) { ar & a & b; }
};

int main() {
    asio::io_context ioc;
    asio::streambuf buf;
    tcp::socket s(ioc, tcp::v4());
    s.connect({{}, 3001});

    ///////////////////
    // send a "request"
    ///////////////////
    {
        std::ostream os(&buf);
        boost::archive::binary_oarchive oa(os);

        Test req {13,31};
        oa << req;
    }
    /*auto bytes_sent =*/ asio::write(s, buf);

    /////////////////////
    // receive "response"
    /////////////////////

    boost::system::error_code ec;
    /*auto bytes_received =*/ asio::read(s, buf, ec);
    if (ec && ec != asio::error::eof) {
        std::cout << "Read error: " << ec.message() << "\n";
        return 1;
    }

    Test response; // uninitialized
    {
        std::istream is(&buf);
        boost::archive::binary_iarchive ia(is);

        ia >> response;
    }

    std::cout << "Response: {" << response.a << ", " << response.b << "}\n";
}

Using netcat to mock a server with a previously generated response Test{42,99} (base64 encoded here):

base64 -d <<<"FgAAAAAAAABzZXJpYWxpemF0aW9uOjphcmNoaXZlEgAECAQIAQAAAAAAAAAAKgAAAGMAAAA=" | nc -N -l -p 3001

It prints:

Response: {42, 99}  

¹ on the same architecture and compiled with the same version of boost, because Boost's binary archives are not portable. The live demo is good demonstration of this



来源:https://stackoverflow.com/questions/62193274/reading-a-serialized-struct-at-the-receiver-end-boost-asio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!