Serializzare e inviare una struttura di dati utilizzando Boost?
-
19-08-2019 - |
Domanda
Ho una struttura di dati che assomiglia a questa:
typedef struct { unsigned short m_short1; unsigned short m_short2; unsigned char m_character; } MyDataType;
Voglio usare boost :: serialization per serializzare questa struttura di dati, quindi usare boost :: asio per trasmetterli via TCP / IP, quindi fare in modo che un'altra applicazione riceva i dati e li serializzi usando le stesse librerie boost.
Sto cercando di seguire boost :: serializzazione tutorial , ( come alcune altre domande SO hanno suggerito ) ma l'esempio è specifico per scrivere / leggere su un file, non su un socket usando boost :: asio.
Sono abbastanza sicuro di avere gli strumenti giusti per il lavoro: ho solo bisogno di aiuto per farli lavorare insieme. Scrivere su un socket non può essere così diverso da scrivere su un file, giusto?
Eventuali suggerimenti sono molto apprezzati. Grazie!
Soluzione
Per una struttura così semplice, boost :: serialization è eccessivo e sovraccarico enorme.
Semplifica:
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]);
E risparmia un ENORME sovraccarico che aumenta :: la serializzazione ha
E se hai un protocollo privato in cui funzionano i computer con lo stesso ordine di byte (grande / piccolo) che invia semplicemente la struttura così com'è - POD.
Altri suggerimenti
Esiste una buona serializzazione esempio nella documentazione di asio: server.cpp , stock.hpp , connection.hpp .
Ecco uno 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);
Ho pensato di condividerlo con chiunque cercasse di serializzare un struct
in C ++ usando Boost. Per l'esempio sopra riportato, per rendere serializzabile struct
devi aggiungere una funzione 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;
EDIT: riprendo la mia risposta di seguito, ciò che ho proposto ha i vantaggi di spazio e tempo rispetto alla soluzione stringstream ma l'API asio :: stream manca di alcune funzionalità importanti che saranno necessarie a lungo termine (ad es. interruzione temporizzata ).
La mia risposta originale:
Usa i flussi da boost :: asio, ha vantaggi di tempo e spazio rispetto a scriverlo in std :: stringstreams e poi inviarlo in una volta sola. Ecco come:
Codice client:
boost::asio::ip::tcp::iostream stream("localhost", "3000");
if (!stream)
throw std::runtime_error("can't connect");
Codice server:
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());
E poi, dopo esserti connesso con il flusso client o server, fai semplicemente:
MyDataType obj;
// Send the object.
boost::archive::text_oarchive archive(stream);
archive << obj;
// Or receive it.
boost::archive::text_iarchive archive(stream);
archive >> obj;
Ovviamente devi aggiungere la funzione 'serializza' nel tuo MyDataType come Tymek ha scritto nella sua risposta.
Stai eseguendo la serializzazione per boost :: archive che ottiene il parametro del costruttore - flusso di destinazione, dove salverai i dati. È possibile utilizzare la libreria boost.iostreams per definire il proprio flusso che invierà i dati sulla rete, invece di archiviare o semplicemente utilizzare flussi socket asio ( http://www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/reference/ip__tcp/iostream.html ). È un buon modo, abbiamo fatto qualcosa di simile su questo, ma abbiamo pochi stream (zip / encrypt / send) e abbiamo usato la libreria boost iostreams per tutte le operazioni.
Modo semplice e fittizio: archivia i tuoi dati in un file temporaneo e invia questo file :)
Gli archivi di serializzazione boost possono essere costruiti con qualsiasi flusso. Quindi qualsiasi oarchive può usare qualsiasi ostream e qualsiasi iarchive può usare qualsiasi istream. Quindi puoi archiviare su ostringstream, trasmettere la stringa con asio e ricostruire i dati da quello.
Vedi il riferimento di binary_oarchive qui , per esempio.
Sospetto che prima vorrai archiviare in memoria, quindi scriverlo nel socket.