Я думаю, что это неясное и низкоуровневое решение для меня. Насколько я узнал, низкоуровневые решения, как правило, сложнее поддерживать, читать и отлаживать.
Сериализация / десериализация была реализована много раз многими разработчиками / сообществами. Я думаю, что лучше использовать одну из распространенных, хорошо известных и без ошибок библиотек сериализации.
Например, https://github.com/USCiLab/cereal - достойный вариант, просто позвольте ему сериализоваться и десериализоваться для вас или просто используйте QDataStream.
Чтобы предотвратить конфликты между хостом и клиентскими компьютерами Вы можете использовать portable_archive
или QDataStream
.
Также, если вы используете UDP и ваши данные важны, вы должны добавить часть контрольной суммы из-за вероятности потери по UDP.
Если вы используете TCP, необходимо добавить длину сообщения в сообщение из-за его ориентированной на поток структуры.
Пример Я демонстрирую основную c, некрасивую, но реализацию более высокого уровня
#include <QDataStream>
#include <QCoreApplication>
#include <QDebug>
template< int MsgId , typename T>
struct msg
{
int id() { return msg_id; }
T data;
private:
template< int Id , typename Type >
friend QDataStream& operator<<( QDataStream& out , const msg<Id , Type>& );
template< int Id , typename Type >
friend QDataStream& operator>>( QDataStream& out , msg<Id , Type>& );
int msg_id { MsgId };
};
template< int MsgId , typename T>
QDataStream& operator <<( QDataStream& out , const msg< MsgId , T >& m )
{
return out << m.msg_id << m.data;
}
template< int MsgId, typename T>
QDataStream& operator >>( QDataStream& in , msg< MsgId , T >& m )
{
return in >> m.msg_id >> m.data;
}
struct server_status
{
int number_of_active_users {};
int station_load {};
};
QDataStream& operator <<( QDataStream& out , const server_status& s )
{
return out << s.station_load << s.number_of_active_users;
}
QDataStream& operator >>( QDataStream& in , server_status& s )
{
return in >> s.station_load >> s.number_of_active_users;
}
struct error_occurred
{
int code;
QString msg;
};
QDataStream& operator <<( QDataStream& out , const error_occurred& e )
{
return out << e.code << e.msg;
}
QDataStream& operator >>( QDataStream& in , error_occurred& e )
{
return in >> e.code >> e.msg;
}
int main( int argc , char** argv)
{
QCoreApplication a { argc , argv };
// Serialize to byte array
QByteArray msg_buffer;
QDataStream out { &msg_buffer , QIODevice::WriteOnly };
msg<10 , server_status> m;
m.data.station_load = 54;
m.data.number_of_active_users = 112;
out << m;
msg<11 , error_occurred> e;
e.data.msg = "Device temperature is so high.";
e.data.code = 401;
out << e;
// Send over udp
// some_udp_socket->write( msg_buffer );
auto incomed_datagram = msg_buffer; // some_udp_socket->receiveDatagram().data();
QDataStream in { &incomed_datagram , QIODevice::ReadOnly };
while( !in.atEnd() )
{
int msg_id {};
in >> msg_id;
if ( msg_id == 10 )
{
server_status s;
in >> s;
qDebug() << "station_load : %" << s.station_load << "active_users :" << s.number_of_active_users;
}
else if ( msg_id == 11 )
{
error_occurred e;
in >> e;
qDebug() << "code :" << e.code << "msg :" << e.msg;
}
}
a.exec();
}
message_id проверяется, и соответствующий класс инициализируется вручную, но можно написать шаблонный класс, который отправляет и инициализирует сообщения в соответствии с message_id автоматически.