Hello,

I have got a problem serializing data via TCP/IP.
My application is highly inspired of the asio example on serialization.

I have a server (my class \"Recepteur\"), and a client (my class \"Logger\").

I try to serialize two kine of data :
 - Loggable
 - HumanReadable.

Loggable is a public base of HumanReadable, later I will have other Loggable class to serialize (OneDPlotable etc...).
Look at my two serializable classes :

[code]
// file Loggable.hpp
#ifndef LOGGABLE_HPP
#define LOGGABLE_HPP
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/export.hpp>
#include <string>
class Loggable
{
protected :
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive& _archive, const unsigned int _version)
    {
        std::cout << \"serialize loggable part\" << std::endl;
        _archive & m_channelName;
        std::cout << \"channel name : \" << m_channelName << std::endl;
    }
public :
    std::string m_channelName;
    Loggable()
    {
        m_channelName = \"NonInit\";
    }
    Loggable(std::string _channelName)
    {
        m_channelName = _channelName;
    }
    virtual ~Loggable(){}
};
BOOST_CLASS_VERSION(Loggable, 0)
BOOST_CLASS_EXPORT_GUID(Loggable, \"Loggable\")
#endif
// end of file

// file HumanReadable.hpp
#ifndef HUMAN_READABLE_HPP
#define HUMAN_READABLE_HPP
#include \"Loggable.hpp\"
#include <boost/serialization/string.hpp>
#include <boost/serialization/base_object.hpp>
#include <string>

class HumanReadable : public Loggable
{
protected :
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive& _archive, const unsigned int _version)
    {
        _archive.template register_type<HumanReadable>();
        std::cout << \"serialize HR part\" << std::endl;
        _archive & boost::serialization::base_object<Loggable>(*this);
        _archive & m_message;
        std::cout << \"Message : \" << m_message << std::endl;
    }
public :
    std::string m_message;
    HumanReadable() {}
    HumanReadable(std::string _channelName, std::string _message):
        Loggable(_channelName)
    {
        m_message = _message;
    }
};
BOOST_CLASS_VERSION(HumanReadable, 0)
BOOST_CLASS_EXPORT_GUID(HumanReadable, \"HumanReadable\");
//end of file
[/code]

My TCP IP Server called Recepteur (receiver in english) is the following class :
[code]
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include \"connection.hpp\" // Must come before boost/serialization headers.
#include \"HumanReadable.hpp\"
namespace LibLogger
{
class Recepteur
{
public:
  Recepteur(boost::asio::io_service& io_service, unsigned short port): acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
  {
      connection_ptr new_conn(new connection(acceptor_.io_service()));
      acceptor_.async_accept(new_conn->socket(), boost::bind(&Recepteur::handle_accept, this, boost::asio::placeholders::error, new_conn));
  }

  void handle_accept(const boost::system::error_code& e, connection_ptr conn)
  {
        if (!e)
        {
            conn->async_read(m_hr, boost::bind(&Recepteur::handle_read, this, boost::asio::placeholders::error));

            connection_ptr new_conn(new connection(acceptor_.io_service()));
            acceptor_.async_accept(new_conn->socket(), boost::bind(&Recepteur::handle_accept, this, boost::asio::placeholders::error, new_conn));
        }
        else
        {
            std::cerr << \"Erreur dans accept : \" <<  e.message() << std::endl;
        }
  }
  void handle_read(const boost::system::error_code& e)
  {
        if (!e)
        {
            // Print out the data that was received.
            std::cout << \"Log recue du channel : \" << m_hr.m_channelName << \" : \" << m_hr.m_message <<  std::endl;
        }
        else
        {
            // An error occurred.
            std::cerr <<\"Erreur dans le read : \" << e.message() << std::endl;
        }
  }

private:
  /// The acceptor object used to accept incoming socket connections.
  boost::asio::ip::tcp::acceptor acceptor_;
  HumanReadable m_hr;
};
} // namespace
[/code]

My TCP IP client called logger is the following class :
[code]
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include \"connection.hpp\" // Must come before boost/serialization headers.
#include \"HumanReadable.hpp\"

namespace LibLogger
{
class Logger
{
public:
    Logger(boost::asio::io_service& io_service, const std::string& host, const std::string& service)
    {
        connection_ = connection_ptr(new connection(io_service));
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query(host, service);
        boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;

        connection_->socket().async_connect(endpoint, boost::bind(&Logger::handle_connect, this,
                boost::asio::placeholders::error, ++endpoint_iterator));
    }
    void handle_connect(const boost::system::error_code& e, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
    {
        if(e)
        {
            if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
            {
                connection_->socket().close();
                boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
                connection_->socket().async_connect(endpoint, boost::bind(&Logger::handle_connect, this,
                                                    boost::asio::placeholders::error, ++endpoint_iterator));

            }
            else
            {
                std::cerr << e.message() << std::endl;
            }
        }
        else
        {
            Debug();
        }
    }
    void Debug()
    {
        HumanReadable hr(\"TotoChannel\", \"Coucou le monde\");
        connection_->async_write(hr, boost::bind(&Logger::handle_write, this, boost::asio::placeholders::error, connection_));
    }
    void handle_write(const boost::system::error_code& e, connection_ptr conn){}
private:
    connection_ptr connection_;
};

} // namespace s11n_example
[/code]

And of course I have a custom \"connection\" class which is approximatively the same than the connection class of the asio example on serialization, have a look :
[code]
namespace LibLogger
{
class connection : public boost::enable_shared_from_this<connection>
{
public:
    /// Constructor.
    ~connection()
    {
        std::cout << \"Destruction de la connection\" << std::endl;
    }
    connection(boost::asio::io_service& io_service)
    : socket_(io_service)
    {
    }
    boost::asio::ip::tcp::socket& socket()
    {
        return socket_;
    }

    /// Asynchronously write a data structure to the socket.
    template <typename T, typename Handler>
    void async_write(const T& t, Handler handler)
    {
        // Serialize the data first so we know how large it is.
        std::ostringstream archive_stream;
        boost::archive::text_oarchive archive(archive_stream);
        archive << t;
        outbound_data_ = archive_stream.str();

        // Format the header.
        std::ostringstream header_stream;
        header_stream << std::setw(header_length) << std::hex << outbound_data_.size();
        if (!header_stream || header_stream.str().size() != header_length)
        {
            // Something went wrong, inform the caller.
            boost::system::error_code error(boost::asio::error::invalid_argument);
            socket_.io_service().post(boost::bind(handler, error));
            return;
        }
        outbound_header_ = header_stream.str();
        std::vector<boost::asio::const_buffer> buffers;
        buffers.push_back(boost::asio::buffer(outbound_header_));
        buffers.push_back(boost::asio::buffer(outbound_data_));
        boost::asio::async_write(socket_, buffers, handler);
    }

    /// Asynchronously read a data structure from the socket.
    template <typename T, typename Handler>
    void async_read(T& t, Handler handler)
    {
        // Issue a read operation to read exactly the number of bytes in a header.
        void (connection::*f)( const boost::system::error_code&, T&, boost::tuple<Handler>) = &connection::handle_read_header<T, Handler>;
        boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_, header_length),
                                         boost::bind(f, shared_from_this(), boost::asio::placeholders::error, boost::ref(t), boost::make_tuple(handler)));
    }

    template <typename T, typename Handler>
    void handle_read_header(const boost::system::error_code& e, T& t, boost::tuple<Handler> handler)
    {
        std::cout <<\"reading header\" << std::endl;
        if (e)
        {
            std::cout << \"Error : \" << e.message() << std::endl;
            boost::get<0>(handler)(e);
        }
        else
        {
            // Determine the length of the serialized data.
            std::istringstream is(std::string(inbound_header_, header_length));
            std::size_t inbound_data_size = 0;
            if (!(is >> std::hex >> inbound_data_size))
            {
                // Header doesn\'t seem to be valid. Inform the caller.
                boost::system::error_code error(boost::asio::error::invalid_argument);
                boost::get<0>(handler)(error);
                return;
            }

            // Start an asynchronous call to receive the data.
            inbound_data_.resize(inbound_data_size);
            void (connection::*f)( const boost::system::error_code&, T&, boost::tuple<Handler>) = &connection::handle_read_data<T, Handler>;
            boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_), boost::bind(f, shared_from_this(), boost::asio::placeholders::error, boost::ref(t), handler));
        }
    }

    /// Handle a completed read of message data.
    template <typename T, typename Handler>
    void handle_read_data(const boost::system::error_code& e, T& t, boost::tuple<Handler> handler)
    {
        std::cout << \"Reading data\" << std::endl;
        if (e)
        {
            std::cout << \"Error\" << std::endl;
            boost::get<0>(handler)(e);
        }
        else
        {
            try
            {
                std::string archive_data(&inbound_data_[0], inbound_data_.size());
                std::cout << \"read : \" << archive_data << std::endl;;
                std::istringstream archive_stream(archive_data);
                boost::archive::text_iarchive archive(archive_stream);
                archive >> t;
            }
            catch (std::exception& e)
            {
                // Unable to decode data.
                std::cout << \"Erreur durant lecture des donnĂ©es : \" << e.what() << std::endl;
                boost::system::error_code error(boost::asio::error::invalid_argument);
                boost::get<0>(handler)(error);
                return;
            }

            // Inform caller that data has been received ok.
            boost::get<0>(handler)(e);
        }
    }

public:
    boost::asio::ip::tcp::socket socket_;
    enum { header_length = 8 };
    std::string outbound_header_;
    std::string outbound_data_;
    char inbound_header_[header_length];
    std::vector<char> inbound_data_;
};
typedef boost::shared_ptr<connection> connection_ptr;
} // namespace s11n_example
[/code]

Then, I instanciate a Receiver (TCP IP server) and a logger (TCP IP client) in two different processes :
[code]
// File mainReceiver.cpp
int main(int argc, char** argv)
{

     io_service io_service;
     LibLogger::Recepteur recepteur(io_service, 1000);
     io_service.run();

    return 0;
}
// end of file
// File mainLogger.cpp
int main(int argc, char**argv)
{
    boost::asio::io_service io_service;
    LibLogger::Logger Logger(io_service, \"127.0.0.1\", \"1000\");
    io_service.run();
    usleep(1000000);
    //Logger.Debug();
    return 0;
}
//end of file
[/code]

Looks like great isn\'t it ?

I execute my two applications, and the sender console output is :
[code]
serialize HR part
serialize loggable part
channel name : TotoChannel
Message : Coucou le monde
Destruction de la connection
[/code]
All the serialization process seems to be ok !

My receiver console output is :
[code]
reading header
 La taille : 73
Reading data
read : 22 serialization::archive 7 1 0
0 1 0
1 11 TotoChannel 15 Coucou le monde
serialize HR part
serialize loggable part
channel name :
Message : 1
Log recue du channel :  : 1
Destruction de la connection
[/code]

Thanks to the lines 4 to 7 we can see that all the informations are in the string \"archive_data\" in connection::handle_read_data.
But thanks to the lines 10 and 11 I can see that the \"deserialization\" process doesn\'t work at all !

Does someone can explain me why ?

I tried to serialize only a Loggable object and all works well.

Cheers,

A. BARRAL