Сериализировать и отправить структуру данных с помощью Boost?

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

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.

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

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


person Runcible    schedule 16.03.2009    source источник


Ответы (7)


Для такой простой структуры 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]);

И сэкономьте себе ОГРОМНЫЕ накладные расходы, которые имеет boost::serialization

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

person Artyom    schedule 18.03.2009
comment
Я определенно согласен с вами по поводу накладных расходов boost::serialization. Это превратило мою крошечную структуру данных во что-то почти на 50 байт больше! - person Runcible; 20.03.2009
comment
Это не отвечает на вопрос, как вообще можно отправлять сериализованные данные по сети (подключив Boost Serialization и Asio). Это просто отвечает на вопрос, как отправить конкретную структуру через Boost::Asio - person MOnsDaR; 12.10.2010


Я решил поделиться этим со всеми, кто пытался сериализовать 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;
person Tymek    schedule 26.07.2011
comment
Поправьте меня, если я ошибаюсь, но когда байты передаются по сети, boost добавляет какой-то дополнительный мусор идентификатора в начале датаграммы. Мне было интересно, могу ли я использовать эту технику сериализации для сериализации структур с помощью ускоренной сериализации, но я думаю, что это может быть невозможно без дополнительных вещей, которые Boost добавляет в поток (для поддержки сортировки и демаршаллинга) - person johnco3; 09.01.2016
comment
Я использовал этот метод для сериализации вектора структур, и он работал отлично! - person I'm Root James; 28.05.2020

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


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

Используйте потоки из 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;

Вам, конечно, нужно добавить функцию «сериализации» в ваш MyDataType, как написал Tymek в своем ответе.

person Peter Jankuliak    schedule 21.11.2011
comment
безупречный ответ, за исключением того, что это не сработало. Когда я попытался прикрепить архив к потоку, я просто получил ошибку. Правда у меня был бинарный архив но все равно должно работать. - person CashCow; 17.07.2017
comment
Хорошо, я вижу свою проблему, мне нужно было поставить флаг no_header. Мне пришлось проделать свой путь через отладку, чтобы понять это, в самом заголовке boost или документации не было нигде, чтобы я мог просто узнать, какие флаги вы действительно можете передать в конструктор потока в качестве второго параметра. Библиотеки Boost фантастичны в своей реализации, но ужасны в своей документации. - person CashCow; 17.07.2017

Вы выполняете сериализацию для 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.

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

person bayda    schedule 16.03.2009

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

Например, см. ссылку на binary_oarchive здесь.

person rlbond    schedule 16.03.2009
comment
я думаю, вы имели в виду «любой iarchive может использовать любой istream» - person David Claridge; 25.11.2009

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

person Matt Cruikshank    schedule 16.03.2009