views:

83

answers:

1

I am writing a client server application using boost::asio. I want to transfer a structure from a client to the server. The struct has a few std::wstrings in it. How do I encode the structure in boost::asio::buffer?

+1  A: 

Typically I use boost::asio::streambuf for serializing structures.

Message.h

#ifndef MESSAGE_H
#define MESSAGE_H

#include <boost/serialization/string.hpp>

#include <string>

struct Message
{
    std::string _a;
    std::string _b;

    template <class Archive>
    void serialize(
            Archive& ar,
            unsigned int version
            )
    {
        ar & _a;
        ar & _b;
    }
};

#endif

client.cpp

#include "Message.h"

#include <boost/archive/text_oarchive.hpp>

#include <boost/asio.hpp>

int
main()
{
    Message msg;
    msg._a = "hello";
    msg._b = "world";

    boost::asio::streambuf buf;
    std::ostream os( &buf );
    boost::archive::text_oarchive ar( os );
    ar & msg;

    boost::asio::io_service io_service;
    boost::asio::ip::tcp::socket socket( io_service );
    const short port = 1234;
    socket.connect(
            boost::asio::ip::tcp::endpoint(
                boost::asio::ip::address::from_string( "127.0.0.1" ),
                port
                )
            );

    const size_t header = buf.size();
    std::cout << "buffer size " << header << " bytes" << std::endl;

    // send header and buffer using scatter
    std::vector<boost::asio::const_buffer> buffers;
    buffers.push_back( boost::asio::buffer(&header, sizeof(header)) );
    buffers.push_back( buf.data() );
    const size_t rc = boost::asio::write(
            socket,
            buffers
            );
    std::cout << "wrote " << rc << " bytes" << std::endl;;
}

server.cpp

#include "Message.h"

#include <boost/archive/text_iarchive.hpp>

#include <boost/asio.hpp>

int
main()
{
    boost::asio::io_service io_service;
    const uint16_t port = 1234;
    boost::asio::ip::tcp::acceptor acceptor(
            io_service,
            boost::asio::ip::tcp::endpoint(
                boost::asio::ip::address::from_string( "127.0.0.1" ),
                port
                )
            );

    boost::asio::ip::tcp::socket socket( io_service );
    acceptor.accept( socket );
    std::cout << "connection from " << socket.remote_endpoint() << std::endl;

    // read header
    size_t header;
    boost::asio::read(
            socket,
            boost::asio::buffer( &header, sizeof(header) )
            );
    std::cout << "body is " << header << " bytes" << std::endl;

    // read body
    boost::asio::streambuf buf;
    const size_t rc = boost::asio::read(
            socket,
            buf.prepare( header )
            );
    buf.commit( header );
    std::cout << "read " << rc << " bytes" << std::endl;

    // deserialize
    std::istream is( &buf );
    boost::archive::text_iarchive ar( is );
    Message msg;
    ar & msg;

    std::cout << msg._a << std::endl;
    std::cout << msg._b << std::endl;
}
Sam Miller
I think this will work fine with sending the data. Does this also work with reading the data from socket? Here we dont know beforehand the size of the incoming strings.
Canopus
yes, you'll need to send a fixed length header indicating the size of the buffer.
Sam Miller
I've updated my answer showing how to send a header
Sam Miller
It would be helpful if you also update the code for reading the stream using the header info
Canopus
I've added a server example showing how to receive the header and body
Sam Miller