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?

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
Canopus
  • 7,351
  • 11
  • 46
  • 57

1 Answers1

7

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
  • 23,808
  • 4
  • 67
  • 87
  • 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 Aug 30 '10 at 13:03
  • yes, you'll need to send a fixed length header indicating the size of the buffer. – Sam Miller Aug 30 '10 at 13:16
  • It would be helpful if you also update the code for reading the stream using the header info – Canopus Aug 31 '10 at 09:36
  • I've added a server example showing how to receive the header and body – Sam Miller Aug 31 '10 at 12:06
  • At the end of the server example, are there two variables defined with same name `msg`? Does this code compile? – Deqing Jul 03 '16 at 14:12
  • @Deqing there is a single variable named msg, the code should compile though it has been several years since I wrote it. – Sam Miller Jul 04 '16 at 21:22
  • I see. `ar & msg` is using operator &, not defining a reference. – Deqing Jul 05 '16 at 13:17