使用boost的archive做可變長度的網絡消息資料打包
目的
在結構體上面可以定義std::string這樣的資料,并友善打包與解包
核心
1. boost庫提供了非常友善的對像序列化庫boost::archive、boost::serialization,通過這兩個庫我們可以很友善的打包std裡面像std::string、std::list這些類型的資料。
2. 打包資料我們當然要使用二進制的方式是以使用boost::archive::binary_iarchive、boost::archive::binary_oarchive。
3. 在真正編寫代碼的過程中發現這兩個類為了序列化出來的資料有版本的區分還在輸出的資料最前面加上一些版本資訊,為了去除它們,最終我的解決方案是重寫這兩個類,将輸出版本資訊這塊代碼給關閉掉。
4. boost的序列化方法是在結構體上面做一個函數(全局的不提了),如下:
struct data
{
std::string v;
int v2;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & v;
ar & v2;
}
};
程式員總有一個通病“懶”,看到這樣的代碼就在想能不能将serialize函數給去掉或者讓計算機自動生成,分析了很多,最後的解決方法是使用宏來處理在代碼裡可以看到MSG1、MSG2…MSG9這樣的宏,它們就是為了完成這個想法而做的東西,很醜!!!(哪位有好的解決方法請一定要告知我,非常感謝)
5. 為了做到使用更加友善,簡單做了MsgPack與MsgUnpack類來做打包與解包工作。
代碼
//msg_binary_iarchive.h
#pragma once
// 這檔案内容是直接複制的boost的binary_iarchive.hpp的内容做了點兒修改
#include <istream>
#pragma warning(push)
#pragma warning(disable : 4267)
#pragma warning(disable : 4996)
#include <boost/archive/binary_iarchive_impl.hpp>
#include <boost/archive/impl/basic_binary_iprimitive.ipp>
#include <boost/archive/impl/basic_binary_iarchive.ipp>
#pragma warning(pop)
namespace boost {
namespace archive {
class naked_binary_iarchive :
public binary_iarchive_impl<
boost::archive::naked_binary_iarchive,
std::istream::char_type,
std::istream::traits_type
>
{
public:
naked_binary_iarchive(std::istream & is, unsigned int flags = 0) :
binary_iarchive_impl<
naked_binary_iarchive, std::istream::char_type, std::istream::traits_type
>(is, flags)
{}
naked_binary_iarchive(std::streambuf & bsb, unsigned int flags = 0) :
binary_iarchive_impl<
naked_binary_iarchive, std::istream::char_type, std::istream::traits_type
>(bsb, flags)
{}
};
} // namespace archive
} // namespace boost
#include <boost/archive/shared_ptr_helper.hpp>
namespace boost {
namespace archive {
class msg_binary_iarchive :
public binary_iarchive_impl<
boost::archive::msg_binary_iarchive,
std::istream::char_type,
std::istream::traits_type
>,
public detail::shared_ptr_helper
{
public:
typedef binary_iarchive_impl<
boost::archive::msg_binary_iarchive,
std::istream::char_type,
std::istream::traits_type
> base;
msg_binary_iarchive(std::istream & is, unsigned int flags = 0) :
binary_iarchive_impl<
msg_binary_iarchive, std::istream::char_type, std::istream::traits_type
>(is, flags)
{}
msg_binary_iarchive(std::streambuf & bsb, unsigned int flags = 0) :
binary_iarchive_impl<
msg_binary_iarchive, std::istream::char_type, std::istream::traits_type
>(bsb, flags)
{}
template<class T>
void load_override(T & t, BOOST_PFTO int)
{
BOOST_MPL_ASSERT_NOT(( boost::is_pointer<T> ));
base::load_override(t, 0);
}
// 這些資訊都不要了
void load_override(boost::archive::class_id_optional_type &, int){}
void load_override(boost::archive::tracking_type & t, int){t.t = false;}
void load_override(boost::archive::version_type & t, int){t.t = 0;}
};
} // namespace archive
} // namespace boost
// required by export
BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::msg_binary_iarchive)
BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::archive::msg_binary_iarchive)
//msg_binary_oarchive.h
#pragma once
// 這檔案内容是直接複制的boost的binary_oarchive.hpp的内容做了點兒修改
#include <ostream>
#pragma warning(push)
#pragma warning(disable : 4267)
#pragma warning(disable : 4996)
#include <boost/archive/binary_oarchive_impl.hpp>
#include <boost/archive/impl/basic_binary_oprimitive.ipp>
#include <boost/archive/impl/basic_binary_oarchive.ipp>
#pragma warning(pop)
namespace boost {
namespace archive {
class msg_binary_oarchive :
public binary_oarchive_impl<
msg_binary_oarchive, std::ostream::char_type, std::ostream::traits_type
>
{
public:
typedef binary_oarchive_impl<
msg_binary_oarchive, std::ostream::char_type, std::ostream::traits_type
> base;
msg_binary_oarchive(std::ostream & os, unsigned int flags = 0) :
binary_oarchive_impl<
msg_binary_oarchive, std::ostream::char_type, std::ostream::traits_type
>(os, flags)
{}
msg_binary_oarchive(std::streambuf & bsb, unsigned int flags = 0) :
binary_oarchive_impl<
msg_binary_oarchive, std::ostream::char_type, std::ostream::traits_type
>(bsb, flags)
{}
template<class T>
void save_override(T & t, BOOST_PFTO int)
{
BOOST_MPL_ASSERT_NOT(( boost::is_pointer<T> ));
base::save_override(t, 0);
}
// 這些資訊都不要了
void save_override(const boost::archive::class_id_optional_type &, int){}
void save_override(const boost::archive::tracking_type &, int){}
void save_override(const boost::archive::version_type &, int){}
};
typedef msg_binary_oarchive naked_binary_oarchive;
} // namespace archive
} // namespace boost
// required by export
BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::msg_binary_oarchive)
BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::archive::msg_binary_oarchive)
//MsgBinaryArchive.h
#pragma once
#include <strstream>
#include "boost/serialization/string.hpp"
#include "boost/serialization/list.hpp"
#include "msg_binary_iarchive.h"
#include "msg_binary_oarchive.h"
#define MSG1(mn,t1,n1)/
struct mn/
{/
t1 vn1;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & vn1;/
}/
};
#define MSG2(mn,t1,n1,t2,n2)/
struct mn/
{/
t1 n1;/
t2 n2;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
}/
};
#define MSG3(mn,t1,n1,t2,n2,t3,n3)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
}/
};
#define MSG4(mn,t1,n1,t2,n2,t3,n3,t4,n4)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
t4 n4;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
ar & n4;/
}/
};
#define MSG5(mn,t1,n1,t2,n2,t3,n3,t4,n4,t5,n5)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
t4 n4;/
t5 n5;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
ar & n4;/
ar & n5;/
}/
};
#define MSG6(mn,t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
t4 n4;/
t5 n5;/
t6 n6;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
ar & n4;/
ar & n5;/
ar & n6;/
}/
};
#define MSG7(mn,t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
t4 n4;/
t5 n5;/
t6 n6;/
t7 n7;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
ar & n4;/
ar & n5;/
ar & n6;/
ar & n7;/
}/
};
#define MSG8(mn,t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7,t8,n8)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
t4 n4;/
t5 n5;/
t6 n6;/
t7 n7;/
t8 n8;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
ar & n4;/
ar & n5;/
ar & n6;/
ar & n7;/
ar & n8;/
}/
};
#define MSG9(mn,t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7,t8,n8,t9,n9)/
struct mn/
{/
t1 n1;/
t2 n2;/
t3 n3;/
t4 n4;/
t5 n5;/
t6 n6;/
t7 n7;/
t8 n8;/
t9 n9;/
template<class Archive> void serialize(Archive & ar, const unsigned int version)/
{/
ar & n1;/
ar & n2;/
ar & n3;/
ar & n4;/
ar & n5;/
ar & n6;/
ar & n7;/
ar & n8;/
ar & n9;/
}/
};
class MsgPack
{
public:
MsgPack():
_oa(_os, boost::archive::no_header)
{}
template <class T>
MsgPack& operator & (const T & v)
{
reset();
_oa & v;
return *this;
}
template <class T>
MsgPack& operator << (const T & v)
{
_oa & v;
return *this;
}
void reset()
{
_os.freeze(false);
_os.seekp(0);
_os.seekg(0);
}
const char* buffer()
{
return _os.str();
}
size_t size()
{
return _os.pcount();
}
private:
std::strstream _os;
boost::archive::msg_binary_oarchive _oa;
};
class MsgUnpack
{
public:
MsgUnpack():
_ia(_is, boost::archive::no_header)
{}
void reset(const char* buf, size_t size)
{
if (_is.pcount())
{
_is.seekp(0);
_is.seekg(0);
}
_is.write(buf, (std::streamsize)size);
}
template <class T>
MsgUnpack& operator >> (T & v)
{
_ia & v;
return *this;
}
private:
std::strstream _is;
boost::archive::msg_binary_iarchive _ia;
};
/* 例子
===============================================================================
// 定義一個有兩個成員變量的消息結構
MSG2(stTestMsg,
float, x,
std::string, str);
// 定義一個有四個成員變量的消息結構
MSG4(A,
std::list<int>, _list,
int, _int,
std::string, _str,
char, _char);
void test()
{
std::string recvMsgBuf;
// 發送
{
MsgPack msgPack;
stTestMsg testmsg = {3.2f,"fdsfd"};
A a;
a._char = 'a';
a._int = 343;
a._list.push_back(3);
a._list.push_back(432);
a._str = "test str";
// 打包消息
msgPack & a; // 重置消息緩沖,并打包資料
msgPack << testmsg; // 在目前包後面添加資料
// 可以用這兩個玩意兒去發送消息了
const char* msgBuf = msgPack.buffer();
size_t msgSize = msgPack.size();
recvMsgBuf.resize(msgSize);
memcpy((char*)recvMsgBuf.c_str(), msgBuf, msgSize);
}
// 接收
{
MsgUnpack msgUnpack;
stTestMsg testmsg;
A a;
// 設定接收到的包資料
msgUnpack.reset(recvMsgBuf.c_str(), recvMsgBuf.size());
// 解包資料到消息結構體内
msgUnpack >> a >> testmsg;
}
}
*/