尝试使用boost :: serialization通过boos :: asio套接字发送派生类 [英] Trying to send an derived class through a boos::asio socket using boost::serialization

查看:74
本文介绍了尝试使用boost :: serialization通过boos :: asio套接字发送派生类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用UDP通过boost :: asio套接字发送一个对象,该对象是派​​生类的实例.

I'm trying to send an object that is an instance of a derived class through a boost::asio socket using UDP.

假设子类是PacketA,基类是Packet.

Let's say the child class is PacketA with the base class being the Packet.

我能够在客户端程序中序列化PacketA,但是每当我尝试在服务器中反序列化PacketA时,它将引发以下错误:

I'm able to serialize PacketA in the client program but whenever I try to deserialize it in the server it throws the following error:

抛出'boost :: archive :: archive_exception'实例后调用

terminatewhat():未注册的课程

terminate called after throwing an instance of 'boost::archive::archive_exception' what(): unregistered class

要尝试解决此问题,我在PacketA cpp文件中添加了宏 BOOST_CLASS_EXPORT_IMPLEMENT ,并在头文件中添加了 BOOST_CLASS_EXPORT_KEY ,而在Packet类中,我没有添加任何宏宏,但仍然无法正常工作.由于提升文档.我还尝试使用 register_type()函数注册子类,但是我也没有成功,解决方案似乎比宏更糟糕.

To try and fix this I added the macros BOOST_CLASS_EXPORT_IMPLEMENT in the PacketA cpp file and the BOOST_CLASS_EXPORT_KEY in the header file while in the Packet class I didn't add any macro, but it still doesn't work. I added these macros because of this section of the boost docs. I also tried to use the register_type() function to register the child classes but I wasn't successful either and the solutions seems to be worse than the macros.

我犯了任何明显的错误还是我错误地使用了API?

Is there any obvious mistake that I'm making or am I using the API wrongly?

代码:

反序列化:

        udp::endpoint senderEndPoint;
        char buffer[MAX_PACKET_SIZE] = {"\n"};
        int bytes = socket->receive_from(boost::asio::buffer(buffer, MAX_PACKET_SIZE), senderEndPoint, 0,error);
   
        std::stringstream stringStream(buffer);
        boost::archive::text_iarchive ia{stringStream};
        Packet *packet; //<-It throws the exception in this line but If I switch this pointer to 
                        //PacketA it works fine but the idea is to deserialize multiple child 
                        //packets that came from the sockets.
        ia & packet; 
        packet->bytes = 0;
        packet->senderEndPoint = senderEndPoint;

Packet.cpp:

Packet.cpp:

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include "Packet.hpp"
template<class Archive>
void Packet::serialize(Archive &ar, unsigned int version) {
  //I didnt add any code in here since I don't really need to serialize any information just the child packets
}

template void Packet::serialize(boost::archive::text_iarchive &arch, const unsigned int version);

template void Packet::serialize(boost::archive::text_oarchive &arch, const unsigned int version);

Packet.hpp:

Packet.hpp:

#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include <boost/asio/ip/udp.hpp>

using PacketType = std::string;

class Packet {
public:
    friend class boost::serialization::access;

    /*Some variables and functions from packet*/

    template<class Archive>
    void serialize(Archive &, unsigned int version);

};

PacketA.cpp:

PacketA.cpp:

#include "PacketA.hpp"
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>

/*Some other functions*/

template<class Archive>
void PacketA::serialize(Archive &ar, unsigned int version) {
    ar & boost::serialization::base_object<Packet>(*this);
    ar & boost::serialization::make_nvp("PacketType", packetType);
}

BOOST_CLASS_EXPORT_IMPLEMENT(PacketA)

PacketA.hpp:

PacketA.hpp:

#include <boost/serialization/export.hpp>
#include "../Packet.hpp"

class PacketA : public Packet {
public:
    PacketType packetType = "PacketA";

    friend class boost::serialization::access;

    /*Some functions*/

    template<class Archive>
    void serialize(Archive &ar, unsigned int version);
};

BOOST_CLASS_EXPORT_KEY(PacketA)

要序列化我正在使用此功能的所有数据包:

To serialize all the packets I'm using this function:

std::stringstream foo::serializePacket(Packet *packet) { //<-Here the *packet could be any 
                                                         //packet child
    std::stringstream ss;
    boost::archive::text_oarchive oa{ss};
    oa & packet;
    return ss;
}

推荐答案

您的注册实现无法看到输入档案定义,因为PacketA.cpp无法包括:

You registration implementation cannot see the input archive definition because PacketA.cpp fails to include:

#include <boost/archive/text_iarchive.hpp>

BOOST_CLASS_EXPORT在包含任何以下内容的同一源模块中存档类标题将实例化序列化所需的代码指示类型的多态指针指向所有那些归档类.如果不包括存档类标题,那么将不会有任何代码被实例化.

BOOST_CLASS_EXPORT in the same source module that includes any of the archive class headers will instantiate code required to serialize polymorphic pointers of the indicated type to the all those archive classes. If no archive class headers are included, then no code will be instantiated.

请注意,此功能的实现要求包含任何档案后,BOOST_CLASS_EXPORT宏就会出现要为其实例化代码的类头.

Note that the implemenation of this functionality requires that the BOOST_CLASS_EXPORT macro appear after the inclusion of any archive class headers for which code is to be instantiated.

附加说明

  • 类层次结构需要是虚拟的,以通过指针进行多态(反)序列化.最简单的方法是确保它添加了 virtual 析构函数.

    请注意,您无法NUL终止接收缓冲区,即表示您将调用 UB ,除非发送的数据包中包含它(并且它包含适合缓冲区大小).因此,以下内容将是一个开始更安全的反序列化看起来像:

    Note that you fail to NUL-terminate the receive buffer, which means you will invoke UB unless the sent packet includes it (and it fits in the buffer size). So the following would be a start of what safer deserialization looks like:

    std::array<char, MAX_PACKET_SIZE> buffer {'\0'}; // fill with NULs
    boost::system::error_code error;
    int bytes = socket->receive_from(boost::asio::buffer(buffer, MAX_PACKET_SIZE), senderEndPoint, 0,error);
    
    if (!error) {
        std::stringstream stringStream(std::string(buffer.data(), bytes));
        boost::archive::text_iarchive ia{stringStream};
        Packet* packet = nullptr;
        ia & packet;
        packet->bytes = 0;
        packet->senderEndPoint = senderEndPoint;
    }
    

    • 文件 Packet.hpp

     #include <boost/serialization/access.hpp>
     #include <boost/serialization/export.hpp>
     #include <boost/asio/ip/udp.hpp>
     #include <string>
    
     using PacketType = std::string;
    
     class Packet {
     public:
         virtual ~Packet() = default;
         friend class boost::serialization::access;
    
         /*Some variables and functions from packet*/
         int bytes = 0;
         boost::asio::ip::udp::endpoint senderEndPoint;
    
         template<class Archive>
         void serialize(Archive & /*ar*/, unsigned version);
     };
    

  • 文件 Packet.cpp

     #include <boost/archive/text_iarchive.hpp>
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/serialization/string.hpp>
     #include "Packet.hpp"
     template <class Archive> void Packet::serialize(Archive& /*ar*/, unsigned /*version*/)
     {
         // I didnt add any code in here since I don't really need to serialize any
         // information just the child packets
     }
    
     template void Packet::serialize(
         boost::archive::text_iarchive& arch, const unsigned int version);
    
     template void Packet::serialize(
         boost::archive::text_oarchive& arch, const unsigned int version);
    

  • 文件 PacketA.hpp

     #include <boost/serialization/export.hpp>
     #include "Packet.hpp"
    
     #define DECLARE_PACKET(Name)                                                   \
         struct Name : Packet {                                                     \
             PacketType packetType = #Name;                                         \
             /*Some functions*/                                                     \
                                                                                    \
           private:                                                                 \
             friend class boost::serialization::access;                             \
             template <class Archive>                                               \
             void serialize(Archive& ar, unsigned int version);                     \
         };                                                                         \
                                                                                    \
         BOOST_CLASS_EXPORT_KEY(Name)
    
     DECLARE_PACKET(PacketA)
     DECLARE_PACKET(PacketB)
     DECLARE_PACKET(PacketC)
     DECLARE_PACKET(PacketD)
     DECLARE_PACKET(PacketE)
    

  • 文件 PacketA.cpp

     #include "PacketA.hpp"
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/archive/text_iarchive.hpp>
     #include <boost/serialization/base_object.hpp>
    
     #define IMPLEMENT_PACKET(Name)                                                 \
         /*Some other functions*/                                                   \
                                                                                    \
         template <class Archive>                                                   \
         void Name::serialize(Archive& ar, unsigned /*version*/)                    \
         {                                                                          \
             ar& boost::serialization::base_object<Packet>(*this);                  \
             ar& boost::serialization::make_nvp("PacketType", packetType);          \
         }                                                                          \
                                                                                    \
         BOOST_CLASS_EXPORT_IMPLEMENT(Name)
    
    
     IMPLEMENT_PACKET(PacketA)
     IMPLEMENT_PACKET(PacketB)
     IMPLEMENT_PACKET(PacketC)
     IMPLEMENT_PACKET(PacketD)
     IMPLEMENT_PACKET(PacketE)
    

  • 文件 test.cpp

     #include <boost/asio.hpp>
     #include <iostream>
     #include <iomanip>
     using boost::asio::ip::udp;
    
     #include "PacketA.hpp"
     #include <boost/archive/text_iarchive.hpp>
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/core/demangle.hpp> // for test output
    
     static constexpr size_t MAX_PACKET_SIZE = 1024;
    
     std::unique_ptr<Packet> receive_packet(uint16_t port) {
         boost::asio::io_context io;
         udp::endpoint senderEndPoint;
         auto socket = std::make_unique<udp::socket>(io, udp::endpoint { {}, port });
    
         std::array<char, MAX_PACKET_SIZE> buffer {'\0'}; // fill with NULs
         boost::system::error_code error;
         int bytes = 0 = socket->receive_from(boost::asio::buffer(buffer, MAX_PACKET_SIZE), senderEndPoint, 0,error);
    
         Packet* packet = nullptr;
    
         if (!error) {
             {
                 std::stringstream stringStream(std::string(buffer.data(), bytes));
                 boost::archive::text_iarchive ia{stringStream};
                 ia & packet;
             }
    
             packet->bytes = 0;
             packet->senderEndPoint = senderEndPoint;
         }
    
         return std::unique_ptr<Packet>(packet); // take ownership
     }
    
     struct foo {
         static std::stringstream serializePacket(Packet* packet);
     };
    
     std::stringstream foo::serializePacket(Packet* packet)
     { //<-Here the *packet could be any packet child
         std::stringstream ss;
         boost::archive::text_oarchive oa { ss };
         oa& packet;
         return ss;
     }
    
     template <typename Type>
     void send() {
         auto request = std::make_unique<Type>();
         auto msg = foo::serializePacket(request.get()).str();
    
         boost::asio::system_executor ex;
         udp::socket s { ex };
         s.open(udp::v4());
         s.send_to(boost::asio::buffer(msg), { {}, 9977 });
     }
    
     template <typename Type>
     void test_roundtrip() {
         auto fut = std::async(std::launch::async, receive_packet, 9977);
    
         std::this_thread::yield(); // be reasonably sure the read started
         send<Type>();
    
         if (auto p = fut.get()) {
             std::cout << "Deserialized a "
                       << boost::core::demangle(typeid(*p).name()) << " packet"
                       << std::endl;
         }
     }
    
     int main() {
         test_roundtrip<PacketA>();
         test_roundtrip<PacketB>();
         test_roundtrip<PacketC>();
         test_roundtrip<PacketD>();
         test_roundtrip<PacketE>();
     }
    

  • 打印

    Deserialized a PacketA packet
    Deserialized a PacketB packet
    Deserialized a PacketC packet
    Deserialized a PacketD packet
    Deserialized a PacketE packet
    

    这篇关于尝试使用boost :: serialization通过boos :: asio套接字发送派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆