Сериализация и отправка структуры данных с помощью Boost? - PullRequest
20 голосов
/ 17 марта 2009

У меня есть структура данных, которая выглядит следующим образом:

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

Я хочу использовать boost :: serialization для сериализации этой структуры данных, затем использовать boost :: asio для передачи их через TCP / IP, а затем попросить другое приложение получить данные и десериализовать их, используя те же библиотеки boost.

Я пытаюсь следовать учебному пособию boost :: serialization , (, как предлагали некоторые другие вопросы SO ), но этот пример специально для записи / чтения в файл, а не в сокет, используя boost :: asio.

Я почти уверен, что у меня есть нужные инструменты для работы - мне просто нужна помощь, чтобы они работали вместе. Запись в сокет не может отличаться от записи в файл, верно?

Любые предложения очень приветствуются. Спасибо!

Ответы [ 7 ]

29 голосов
/ 17 марта 2009

В документации asio есть хорошая сериализация пример : server.cpp , stock.hpp , connection.hpp .

Вот фрагмент:

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);
21 голосов
/ 27 июля 2011

Я решил поделиться этим со всеми, кто пытался сериализовать C ++ struct с помощью Boost. Для приведенного выше примера, чтобы сделать сериализуемый struct, вы добавили бы функцию serialize:

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;
6 голосов
/ 18 марта 2009

Для такой простой структуры, boost :: serialization - это излишние и огромные накладные расходы.

Делай проще:

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]);

И сэкономьте ОГРОМНЫЕ накладные расходы, которые повышают: сериализация имеет

А если у вас приватный протокол, где работают компьютеры с одинаковым порядком байтов (большой / маленький) это просто отправить структуру как есть - POD.

4 голосов
/ 22 ноября 2011

РЕДАКТИРОВАТЬ: я забираю свой ответ ниже, то, что я предложил, имеет преимущество во времени и пространстве по сравнению с решением для работы с потоком строк, но API asio :: stream не хватает некоторых важных функций, которые потребуются в долгосрочной перспективе (например, прерывание по времени). ).


Мой оригинальный ответ:

Используйте потоки из boost :: asio, у него есть преимущества во времени и пространстве по сравнению с записью его в std :: stringstreams и последующей его отправкой за один раз. Вот как:

Код клиента:

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

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

Код сервера:

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()); 

А затем, после того как вы подключитесь к клиентскому или серверному потоку, просто выполните:

MyDataType obj;

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

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

Вам, конечно, нужно добавить функцию 'serialize' в ваш MyDataType, как написал Тимек в своем ответе.

1 голос
/ 17 марта 2009

Архивы расширенной сериализации могут быть созданы с любым потоком. Таким образом, любой oarchive может использовать любой ostream, а любой iarchive может использовать любой istream. Таким образом, вы можете архивировать в поток ostring, передавать строку с asio и восстанавливать данные из этого.

См. Ссылку на двоичный_архив здесь , например.

1 голос
/ 17 марта 2009

Вы делаете сериализацию для boost :: archive, которая получает параметр конструктора - целевой поток, в который вы будете сохранять данные. Вы можете использовать библиотеку boost.iostreams для определения своего собственного потока, который будет отправлять данные по сети, вместо файла или просто использовать потоки сокетов asio (http://www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/reference/ip__tcp/iostream.html). Это хороший способ, мы сделали что-то похожее, но несколько потоков (zip / encrypt / send) и использованная библиотека boost iostreams для всех операций.

Простой и дурацкий способ - сохраните свои данные во временном файле и отправьте этот файл:)

0 голосов
/ 17 марта 2009

Я подозреваю, что вы сначала захотите архивировать в память, а затем записать это в сокет.

...