muduo中实现Protobuf编解码器codec与消息分发器dispatcher

前言Google Protocol Buffers(简称Protobuf)是一款非常优秀的库,它定义了一种紧凑(相对XML和JSON而言)的可扩展二进制消息格式,特别适合网络数据传输。在网络编程中使用Protobuf需要解决以下两个问题:长度。Protobuf打包的数据没有自带长度信息或终结符,需要由应用程序自已在发生和接收的时候做正确的切分。类型。Protobuf打包的数据没有自带类型...
摘要由CSDN通过智能技术生成

前言

Google Protocol Buffers(简称Protobuf)是一款非常优秀的库,它定义了一种紧凑(相对XML和JSON而言)的可扩展二进制消息格式,特别适合网络数据传输。

在网络编程中使用Protobuf需要解决以下两个问题:

  • 长度。Protobuf打包的数据没有自带长度信息或终结符,需要由应用程序自已在发生和接收的时候做正确的切分。
  • 类型。Protobuf打包的数据没有自带类型信息,需要由发送方把类型信息传给接收方,接收方创建具体的Protobuf Message对象,再做反序列化。

这里的第一个问题很好解决,通常的做法是在每个消息前面加个固定长度的length header。第二个问题也很好解决,因为Protobuf本身具有很强的反射功能,可以根据type name创建具体类型的Message对象。其具体做法是:

  • 用DescriptorPool::generated_pool()找到一个DescriptorPool对象,它包含了程序编译时所链接的全部Protobuf Message types。
  • 根据type name用DescriptorPool::FindMessageTypeByName()查找Descriptor。
  • 再用MessageFactory::generated_factory()找到MessageFactory对象,它能创建程序编译时所链接的全部Protobuf Message types。
  • 然后用MessageFactory::GetPrototype()找到具体Message type的default instance。
  • 最后用prototype->New()创建对象。

相应的代码为:

google::protobuf::Message* ProtobufCodec::createMessage(const std::string& typeName)
{
   
  google::protobuf::Message* message = NULL;
  const google::protobuf::Descriptor* descriptor =
    google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
  if (descriptor)
  {
   
    const google::protobuf::Message* prototype =
      google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
    if (prototype)
    {
   
      message = prototype->New();
    }
  }
  return message;
}

以上关于Protobuf的C++ API的详解可以参考我的一篇博文Protobuf C++ API 简介

那么为什么Protobuf的默认序列化格式没有包含消息的长度和类型呢?这是为了节省网络带宽和存储,因为绝大多数情况下,我们可以根据其它的信息推断出消息的长度和类型。只有在使用TCP连接,且在一个连接上传递不止一种消息的情况下,才需要在消息中添加长度和类型信息。这时我们需要一个分发器dispatcher,把不同类型的消息分给各个消息处理函数。

陈硕先生在muduo库中设计了一个简单的打包格式,包含Protobuf data和其对应的长度和类型信息,消息的末尾还有一个check sum。格式如下图所示,图中方块的宽度是32-bit。

在这里插入图片描述
将该格式用C代码描述:

 struct ProtobufTransportFormat __attribute__ ((__packed__))
 {
   
   int32_t  len;
   int32_t  nameLen;
   char     typeName[nameLen];
   char     protobufData[len-nameLen-8];
   int32_t  checkSum; // adler32 of nameLen, typeName and protobufData
 }

编解码器ProtobufCodec

编解码器(codec)是encoder和decoder的缩写,这里借指“网络数据和业务消息之间互相转换”的代码。

codec的基本功能之一是做TCP分包:确定每条消息的长度,为消息划分界限。在非阻塞网络编程中,codec几乎是必不可少的。如果只收到了半条消息,那么不会触发消息事件回调,数据会停留在Buffer里,等待收到一个完整的消息再通知处理函数。

codec提供了一层间接性,它位于TcpConnection和TcpSever之间,拦截收到的数据(Buffer),在收到完整的消息之后,解出消息对象(std::string),再调用TcpServer对应的处理函数。另外在发送消息的时候,TcpServer通过send()来发送string,codec会负责将它编码成Buffer。

Protobuf codec与此非常类似,只不过消息类型从std::string变成了protobuf::Message。

实现ProtobufCodec

根据上文定义的消息格式,我们的编码算法很直截了当,其代码为:

void ProtobufCodec::fillEmptyBuffer(Buffer* buf, const google::protobuf::Message& message)
{
   
  // buf->retrieveAll();
  assert(buf->readableBytes() == 0);

  const std::string& typeName = message.GetTypeName();
  int32_t nameLen = static_cast<int32_t>(typeName.size()+1);
  buf-
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值