c++ - 尝试使用 boost::serialization 通过 boos::asio 套接字发送派生类
问题描述
我正在尝试使用 UDP 通过 boost::asio 套接字发送一个作为派生类实例的对象。
假设子类是 PacketA,基类是 Packet。
我能够在客户端程序中对 PacketA 进行序列化,但是每当我尝试在服务器中对其进行反序列化时,它都会引发以下错误:
在抛出“boost::archive::archive_exception”的实例后调用终止什么():未注册的类
为了尝试解决这个问题,我BOOST_CLASS_EXPORT_IMPLEMENT
在 PacketA cpp 文件和BOOST_CLASS_EXPORT_KEY
头文件中添加了宏,而在 Packet 类中我没有添加任何宏,但它仍然不起作用。由于boost docs的这一部分,我添加了这些宏。我也尝试使用该register_type()
函数来注册子类,但我也没有成功,而且解决方案似乎比宏更糟糕。
我是否犯了任何明显的错误,或者我错误地使用了 API?
代码:
反序列化:
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;
数据包.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);
数据包.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);
};
数据包A.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)
数据包A.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)
要序列化我正在使用此功能的所有数据包:
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 未能包括:
#include <boost/archive/text_iarchive.hpp>
文档中解释了此要求:
包含任何归档类头文件的同一源模块中的 BOOST_CLASS_EXPORT 将实例化将指定类型的多态指针序列化到所有这些归档类所需的代码。如果没有包含归档类头文件,则不会实例化任何代码。
请注意,此功能的实现要求 BOOST_CLASS_EXPORT 宏出现在包含要为其实例化代码的任何存档类标头之后。
补充说明
类层次结构需要是虚拟的,才能通过指针进行多态(反)序列化。最简单的方法是确保它正在添加
virtual
析构函数。请注意,您无法 NUL 终止接收缓冲区,这意味着您将调用UB,除非发送的数据包包含它(并且它适合缓冲区大小)。因此,以下将是更安全的反序列化的开始:
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
推荐阅读
- mysql - MySQL UNION 和喜欢多表。#1064 mysql
- babeljs - 无法使用 babel 转换代码 使用配置文件进行转换
- lua - Roblox 问题:预期为 ')'(在第 18 列关闭 '('),得到 '='
- java - 如何通过插件获取 Eclipse 代码模板存储?
- c# - 异步发送邮件 C#
- python - 在 Robot Framework 中使用模板(数据驱动)时如何计算测试用例?
- laravel - laravel response()->download 为非图像格式(如 pdf、zip 等)返回空正文
- xml - 如何引用基本 XML 架构中的属性
- android - Android 设备管理器无法启动设备
- firebase - 如何使用自定义角色更新 Firestore 规则