Десериализация неполиморфной структуры из сетевого пакета

#c #network-programming #protocols

#c #сетевое программирование #протоколы

Вопрос:

Я разрабатываю сетевой протокол. У меня много неполиморфных структур. Мне нужно передать структуру ввода в пакет с его идентификатором (уникальным номером структуры) и сериализовать его в массив байтов для отправки. И десериализуйте ее так же: получите массив байтов, получите идентификатор структуры и десериализуйте байты как эту структуру.

Как?

Пример с комментариями:

 // Internal structure, cannot be edited
struct FirstProcedureParams {
    std::string some_string;
    int some_integer;
};

// Also cannot be edited
struct SecondProcedureParams {
    std::vector<int> some_vector;
    std::map<int, int> some_map;
};

// My class to serialize and deserialize packet data to send to client or server.
class CallProcedurePacket : public BasePacket {
    public:
    void Serialize(uint8_t** bytes, size_t* length) override;
    
    void Deserialize(const uint8_t* bytes, size_t length) override;
    
    public:
    int procedure_id; // for example, 1 - FirstProcedureParams, 2 - SecondProcedureParams.
    
    // there is must be parameters for concrete procedure id.
};

void CallProcedurePacket::Serialize(uint8_t** bytes, size_t* length) {
    // Some stuff..
    
    writer.Write(procedure_id);
    
    // How can I write dynamic parameters structure
    // If it is not polymorphic and it hasn't method like Serialize?
    // In this example only 2 structures: FirstProcedureParams and SecondProcedureParams.
    // But in real code I have more than 15 structures.
}

void CallProcedurePacket::Deserialize(const uint8_t* bytes, size_t length) {
    // Some stuff..
    
    reader.Read(procedure_id);
    
    // I need to get FirstProcedureParams if procedure_id equal 1 
    // or SecondProcedureParams if procedure_id equal 2.
}

// -----------------------------

// Example of using CallProcedurePacket:
void SomeSendFunction() {
    FirstProcedureParams params;
    params.some_string = "some string";
    params.some_integer = 789;
    
    CallProcedurePacket call_procedure_packet;
    call_procedure_packet.procedure_id = 1;
    call_procedure_packet.params = params; // what type should be `params` field? how can I write FirstProcedureParams to it?
    
    SendPacket(call_procedure_packet);
}

void SomeSendFunction2() {
    SecondProcedureParams params;
    params.some_vector = { 1, 2, 3 };
    params.some_map = {
        { 1, 5 },
        { 7, 9 }
    };
    
    CallProcedurePacket call_procedure_packet;
    call_procedure_packet.procedure_id = 2;
    call_procedure_packet.params = params; // params field should accept SecondProcedureParams too.
    
    SendPacket(call_procedure_packet);
}

// And reading..
void SomeReceiveFunction() {
    CallProcedurePacket call_procedure_packet = ReceivePacket();
    
    if (call_procedure_packet.procedure_id == 1) {
        FirstProcedureParams params;
        // somehow get the params.
        
        procedure(params.some_string, params.some_integer);
    }
    else if (call_procedure_packet.procedure_id == 2) {
        SecondProcedureParams params;
        
        // some usage of params.
    }
}
  

Комментарии:

1. Вы более или менее заново изобретаете буферы протокола и, в частности, их oneof функции . Рассмотрите возможность использования protobufs вместо создания собственных.

2. Вот простой способ выяснить, как это сделать, и он никогда не перестает работать. Просто достаньте чистый лист бумаги. Запишите, используя короткие простые предложения на простом английском языке, пошаговый процесс выполнения этого. Когда закончите, позвоните своей резиновой утке, чтобы договориться о встрече . Мы не пишем код для других людей в Stackoverflow. Мы всегда отсылаем такие вопросы к вашей резиновой утке. После того, как ваш резиновый утенок одобрит предложенный вами план действий, просто возьмите то, что вы записали, и переведите это непосредственно на C . Миссия выполнена!

3. Я думаю, вам следует прежде всего уточнить для себя, разрабатываете ли вы пользовательскую библиотеку сериализации или протокол связи, они разные , но имеют некоторые общие черты, например, требуют большого количества шаблонного кода. Вручную писать такой код — плохая идея, поэтому рекомендуется использовать различные генераторы кода. Если это сериализация, используйте protobuf или аналогичный. В случае, если это фактический протокол связи, используйте что-то вроде экосистемы CommsChampion