読者です 読者をやめる 読者になる 読者になる

CStringを含むデータ型をBoost.SerializationでシリアライズしてBoost.Asioで送信

C++

UTF-16と仮定したCStringWをUTF-8のCStringAに変換してからシリアライズしています。



まずシリアライズ部分。
cstring_serialization.hpp

#ifndef MFC_CSTRING_SERIALiZATION_INCLUDE
#define MFC_CSTRING_SERIALiZATION_INCLUDE

/*=============================================================================
    Copyright (C) Akira Takahashi 2011
    Copyright (c) John Paul Pirau 2008

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

    UTF16toUTF8() and UTF8toUTF16() function published here:
    http://www.codeproject.com/KB/string/utfConvert.aspx
==============================================================================*/

#include <afx.h>
#include <string>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>

namespace mfc_code_conv {

CStringA UTF16toUTF8(const CStringW& utf16)
{
   CStringA utf8;
   int len = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, 0, 0);
   if (len>1)
   { 
      char *ptr = utf8.GetBuffer(len-1);
      if (ptr) WideCharToMultiByte(CP_UTF8, 0, utf16, -1, ptr, len, 0, 0);
      utf8.ReleaseBuffer();
   }
   return utf8;
}

CStringW UTF8toUTF16(const CStringA& utf8)
{
   CStringW utf16;
   int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
   if (len>1)
   { 
      wchar_t *ptr = utf16.GetBuffer(len-1);
      if (ptr) MultiByteToWideChar(CP_UTF8, 0, utf8, -1, ptr, len);
      utf16.ReleaseBuffer();
   }
   return utf16;
}

} // namespace mfc_code_conv

namespace boost { namespace serialization {

template <class Archive>
inline void save(Archive& ar, const CStringW& s, const unsigned int version)
{
    static_cast<void>(version);

    const CStringA utf8 = ::mfc_code_conv::UTF16toUTF8(s);
    const std::string ss(utf8);
    ar & boost::serialization::make_nvp("String", ss);
}

template<class Archive>
inline void load(Archive& ar, CStringW& s, const unsigned int version) 
{
    static_cast<void>(version);

    std::string ss;
    ar & boost::serialization::make_nvp("String", ss);
    const CStringA utf8 = ss.c_str();

    s = ::mfc_code_conv::UTF8toUTF16(utf8);
}

template <class Archive>
inline void serialize(Archive& ar, CStringW& s, const unsigned int version)
{
    boost::serialization::split_free(ar, s, version);
}

}} // namespace boost::serialization


#endif // MFC_CSTRING_SERIALiZATION_INCLUDE


クライアント(送る側)

#include "cstring_serialization.hpp"
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

namespace asio = boost::asio;
namespace ip = asio::ip;

struct Data {
    int value;
    CStringW str;

private:
    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive& ar, unsigned int version)
    {
        static_cast<void>(version);
        ar & boost::serialization::make_nvp("value", value);
        ar & boost::serialization::make_nvp("str", str);
    }
};

class Client {
    asio::io_service& io_service_;
    ip::tcp::socket socket_;
    std::string send_data_;

public:
    Client(asio::io_service& io_service)
        : io_service_(io_service),
          socket_(io_service) {}

    void connect()
    {
        socket_.connect(ip::tcp::endpoint(ip::address::from_string("127.0.0.1"), 31400));
    }

    void send(const Data& data)
    {
        std::ostringstream ss;
        boost::archive::xml_oarchive ar(ss);

        ar << boost::serialization::make_nvp("data", data);

        send_data_ = ss.str();
        asio::async_write(socket_, asio::buffer(send_data_),
            boost::bind(&Client::on_send_end, this, asio::placeholders::error));
    }

    void on_send_end(const boost::system::error_code& error)
    {
        if (error) {
            std::cout << error.message() << std::endl;
        }
        else {
            std::cout << "送信成功" << std::endl;
        }
    }
};

int main()
{
    asio::io_service io_service;

    Client client(io_service);

    client.connect();

    Data data;
    data.value = 3;
    data.str = L"abcあいうえおxyz";

    client.send(data);

    io_service.run();
}


サーバー側(受け取る側)

#include <afxwin.h>
#include "cstring_serialization.hpp"
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

namespace asio = boost::asio;
namespace ip = asio::ip;

struct Data {
    int value;
    CString str;

private:
    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive& ar, unsigned int version)
    {
        static_cast<void>(version);
        ar & boost::serialization::make_nvp("value", value);
        ar & boost::serialization::make_nvp("str", str);
    }
};

class Server {
    asio::io_service& io_service_;
    ip::tcp::socket socket_;
    asio::streambuf receive_buff_;
    std::string receive_data_;

    boost::function<void(const boost::system::error_code&, const Data&)> receive_handler;

public:
    Server(asio::io_service& io_service)
        : io_service_(io_service),
          socket_(io_service) {}

    void accept()
    {
        ip::tcp::acceptor acc(io_service_, ip::tcp::endpoint(ip::tcp::v4(), 31400));
        acc.accept(socket_);
    }

    void receive(boost::function<void(const boost::system::error_code&, const Data&)> handler)
    {
        receive_handler = handler;

        // Start reading remaining data until EOF.
        boost::asio::async_read(socket_, receive_buff_, boost::asio::transfer_at_least(1),
                boost::bind(&Server::handle_read_content, this,
                            boost::asio::placeholders::error));
    }

    void handle_read_content(const boost::system::error_code& error)
    {
        if (!error)
        {
            receive_data_ += asio::buffer_cast<const char*>(receive_buff_.data());

            // Continue reading remaining data until EOF.
            boost::asio::async_read(socket_, receive_buff_, boost::asio::transfer_at_least(1),
                boost::bind(&Server::handle_read_content, this,
                            boost::asio::placeholders::error));
        }
        else if (error != boost::asio::error::eof)
        {
            receive_handler(error, Data());
        }
        else {
            std::stringstream ss;
            ss << receive_data_;

            boost::archive::xml_iarchive ar(ss);

            Data data;
            ar >> boost::serialization::make_nvp("data", data);

            receive_handler(boost::system::error_code(), data);
        }
    }
};

void received_data(const boost::system::error_code& error, const Data& data)
{
    if (error) {
        std::cout << "Error: " << error.message() << std::endl;
    }
    else {
        std::cout << data.value << std::endl;
        AfxMessageBox(data.str);
    }
}

int main()
{
    asio::io_service io_service;

    Server server(io_service);

    server.accept();
    server.receive(received_data);

    io_service.run();
}