24

I have a data structure that looks like this:

typedef struct
{
  unsigned short m_short1;
  unsigned short m_short2;
  unsigned char m_character;
} MyDataType;

I want to use boost::serialization to serialize this data structure, then use boost::asio to transmit it via TCP/IP, then have another application receive the data and de-serialize it using the same boost libraries.

I'm trying to following boost::serialization tutorial, (as some other SO questions have suggested) but the example is specifically for writing/reading to a file, not to a socket using boost::asio.

I'm pretty sure I've got the right tools for the job -- I just need help making them work together. Writing to a socket can't be that different from writing to a file, right?

Any suggestions are very much appreciated. Thanks!

Runcible
  • 7,006
  • 12
  • 42
  • 62

7 Answers7

32

There is a good serialization example in the asio documentation: server.cpp, stock.hpp, connection.hpp.

Here's a snippet:

std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << your_struct;
outbound_data_ = archive_stream.str();
boost::asio::async_write(socket_, 
    boost::asio::buffer(outbound_data_), handler);
hvintus
  • 2,547
  • 21
  • 18
28

I thought I'd share this with anyone who was trying to serialize a C++ struct using Boost. For the example given above, to make the struct serializable you would add a serialize function:

typedef struct
{
  unsigned short m_short1;
  unsigned short m_short2;
  unsigned char m_character;

  template <typename Archive>
  void serialize(Archive& ar, const unsigned int version)
  {
    ar & m_short1;
    ar & m_short2;
    ar & m_character;
  }
} MyDataType;
Tymek
  • 664
  • 7
  • 8
  • 1
    Correct me if I am wrong, but when the bytes are transmitted over the network, does boost add some extra identifier junk at the start of the data-gram. I was wondering if I could use that serialization technique to serialize structures using boost serialization but I think it might not be possible without the extra things boost adds to the stream (to support marshalling and demarshalling) – johnco3 Jan 09 '16 at 05:51
  • 1
    I used this method to serialize a vector of structs and it worked perfectly! – raw-bin hood May 28 '20 at 03:36
5

For such simple structure, boost::serialization is overkill and huge overhead.

Do simpler:

vector<uint16_t> net(3,0);

net[0]=htons(data.m_short1);
net[1]=htons(data.m_short2);
net[2]=htons(data.character);

asio::async_write(socket,buffer((char*)&net.front(),6),callback);

vector<uint16_t> net(3,0);
asio::async_read(socket,buffer((char*)&net.front(),6),callback);

callback:
data.m_short1=ntohs(net[0]);
data.m_short2=ntohs(net[1]);
data.character=ntohs(net[2]);

And Save yourself HUGE overhead that boost::serialization has

And if you private protocol where computers with same order of bytes work (big/little) that just send structure as is -- POD.

Nick Veys
  • 23,458
  • 4
  • 47
  • 64
Artyom
  • 31,019
  • 21
  • 127
  • 215
  • I definitely agree with you about the overhead of boost::serialization. It turned my tiny data structure into something that was almost 50 bytes larger! – Runcible Mar 19 '09 at 21:19
  • 41
    This does not answer the question of how it is generally possible to send serialized data over network (connecting Boost Serialization and Asio). This just answers the question of how to send the specific struct via Boost::Asio – MOnsDaR Oct 12 '10 at 09:05
4

EDIT: I take back my answer below, what I proposed does have the time and space advantages over the stringstream solution but the asio::stream API is lacking some important functionality that will be needed in a long run (e.g. timed interruption).


My original answer:

Use streams from boost::asio, it has time and space advantages over writing it into std::stringstreams and then sending it in one go. Here is how:

Client code:

boost::asio::ip::tcp::iostream stream("localhost", "3000");

if (!stream)
  throw std::runtime_error("can't connect");

Server code:

boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint
  = boost::asio::ip::tcp::endpoint(ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
boost::asio::ip::tcp::iostream stream;

// Your program stops here until client connects.
acceptor.accept(*stream.rdbuf()); 

And then, after you are connected with either client or server stream just do:

MyDataType obj;

// Send the object.
boost::archive::text_oarchive archive(stream);
archive << obj;

// Or receive it.
boost::archive::text_iarchive archive(stream);
archive >> obj;

You of course need to add the 'serialize' function into your MyDataType as Tymek wrote in his answer.

Peter Jankuliak
  • 3,464
  • 1
  • 29
  • 40
  • immaculate answer, except that it didn't work. When I tried attaching an archive to the stream I just got an error. True mine was a binary archive but still it should work. – CashCow Jul 17 '17 at 13:35
  • Ok, I see my issue, I needed to put in the no_header flag. I had to work my way through debugging to work that out, there was nowhere in the boost header itself nor documentation that I could simply find out what flags you actually might pass into the stream constructor as the second parameter. Boost libraries are fantastic in their implementation, terrible in their documentation. – CashCow Jul 17 '17 at 14:05
1

You doing serialization to boost::archive which get constructor parameter - destination stream, where you will save data. You could use boost.iostreams library for define your own stream which will send data over network, instead file or just use asio socket streams (http://www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/reference/ip__tcp/iostream.html). It is a good way, we did something similar on this, but we have few streams (zip/encrypt/send) and used boost iostreams library for all operation.

Easy and dummy way - store your data in temporary file and send this file :)

bayda
  • 13,365
  • 8
  • 39
  • 48
1

The boost serialization archives can be constructed with any stream. Thus any oarchive can use any ostream, and any iarchive can use any istream. Thus you can archive to an ostringstream, transmit the string with asio, and reconstruct the data from that.

See the reference of binary_oarchive here, for example.

rlbond
  • 65,341
  • 56
  • 178
  • 228
-1

I suspect you'll want to archive to memory first, and then write that to the socket.

Matt Cruikshank
  • 2,932
  • 21
  • 24