序列化
序列化:将结构化信息转化为大端序的二进制序列
Sponge和Minnow在这里处理有差异,Sponge是解析后直接返回string,Minnow是通过序列化一个Serializer类处理
class Serializer
{
std::vector<Ref<std::string>> output_ {};
std::string buffer_ {};
void flush();
public:
// 关键函数,将不同大小的无符号整数转化位大端序字节流
template<std::unsigned_integral T>
void integer( const T val )
{
constexpr uint64_t len = sizeof( T );
for ( uint64_t i = 0; i < len; ++i ) {
const uint8_t byte_val = val >> ( ( len - i - 1 ) * 8 );
buffer_.push_back( byte_val );
}
}
void buffer( std::string buf );
void buffer( Ref<std::string> buf );
void buffer( const std::vector<Ref<std::string>>& bufs );
std::vector<Ref<std::string>> finish();
};
output_是最终完整的系列化字节,buffer_是临时缓冲区
void Serializer::flush()
{
if ( not buffer_.empty() ) {
output_.emplace_back( move( buffer_ ) );
buffer_.clear();
}
}
void Serializer::buffer( string buf )
{
if ( not buf.empty() ) {
flush();
output_.emplace_back( move( buf ) );
}
}
void Serializer::buffer( Ref<string> buf )
{
if ( not buf.get().empty() ) {
flush();
output_.emplace_back( move( buf ) );
}
}
void Serializer::buffer( const vector<Ref<string>>& bufs )
{
for ( const auto& b : bufs ) {
buffer( b.borrow() );
}
}
vector<Ref<string>> Serializer::finish()
{
flush();
return move( output_ );
}
观察这些实现发现,序列化也就是将这些数据统统写入缓冲区output_
那么它和其他协议配合就能实现序列化
struct IPv4Datagram {
IPv4Header header {};
std::vector<Ref<std::string>> payload {};
.......
void serialize( Serializer& serializer ) const
{
header.serialize( serializer );
serializer.buffer( payload );
}
};
在IPv4Datagram中有这个函数,分别序列化IPv4Header并写入payload
void IPv4Header::serialize( Serializer& serializer ) const
{
// consistency checks
if ( ver != 4 ) {
throw runtime_error( "wrong IP version" );
}
const uint8_t first_byte = ( static_cast<uint32_t>( ver ) << 4 ) | ( hlen & 0xfU );
serializer.integer( first_byte ); // version and header length
serializer.integer( tos );
serializer.integer( len );
serializer.integer( id );
const uint16_t fo_val = ( df ? 0x4000U : 0 ) | ( mf ? 0x2000U : 0 ) | ( offset & 0x1fffU );
serializer.integer( fo_val );
serializer.integer( ttl );
serializer.integer( proto );
serializer.integer( cksum );
serializer.integer( src );
serializer.integer( dst );
}
在header中的序列化函数实现是这样的,利用integer()模版函数进行端序转换
最后写入output_,用finish()函数输出序列化结果
优点
零拷贝,高性能
避免内存频繁分配,消耗性能
高兼容性,采用模版函数,能够序列化各种类型无符号整数,而且跨平台
天然支持分层解析,高灵活性
- 为什么不使用struct来存储header?
字节端序冲突
结构体存在内存对齐,会插入空白字节,打乱header
如果出现接收到的数据包长度不足,使用指针访问容易出现越界或溢出