protocol buffers的编码原理
protocol buffers使用二进制传输格式传递消息,因此相比于xml,json来说要轻便很多。
示例:假设定义了一个Message
message Test1 {
required int32 a = ;
}
实际使用的时候将a设置为150,然后将其序列化到输出流,查看编码后的message,可以看到如下3个byte
解析:
上述三个字节实际分为两部分: 08 96 01。第一部分(08)包含了message成员变量的field number(a=1)和变量类型(Varint),第二部分(96 01)为a的实际值150。
这里面涉及几个概念:
Varint:这个可以理解为可变长的int类型,数值越小使用的byte越少;
field number和type:protocol buffer消息为一系列的key-value对。二进制版本的消息使用field number作为key。
当接收到一个message时,解析器可以忽略无法识别的字段,通过这样的方式,也可以在不影响老功能的前提下添加新的字段。 通信格式下的key实际包含2个值:.proto文件中的field number,和通信类型。通信类型如下
| Type | Meaning | Used For |
|---|---|---|
| 0 | Varint | int32, int64, uint32, uint64, sint32, sint64, bool, enum |
| 1 | 64-bit | fixed64, sfixed64, double |
| 2 | Length-delimited | string, bytes, embedded messages, packed repeated fields |
| 3 | Start group | groups (deprecated) |
| 4 | End group | groups (deprecated) |
| 5 | 32-bit | fixed32, sfixed32, float |
message流中的key类型为varint,计算方式为:(field_number << 3) | wire_type ,即后三位保存了通信类型
上述第一个字节为08,转化为二进制为0000 1000,没个varint的第一个比特位为MSB位,置位表示后续还有字节。去掉MSB位后为
后三位表示类型,值为0,表示类型为Varint;右移三位获取tag值为1(即message中设置的a = 1)
下面获取消息值150,注意:字节顺序为大端序
96 01 = 1001 0110 0000 0001
→ 000 0001 ++ 001 0110 (drop the msb and reverse the groups of 7 bits)
→ 10010110
→ 128 + 16 + 4 + 2 = 150
以上介绍的时varint的编码方式,下面介绍一下其他类型的编码
Signed integer
int32和int64的实际类型都是varint,当它表示负数的时候,为10个固定字节长度的值,效率比较低。可以使用sint32和sint64来表示有符号的数值,它采用ZigZag编码,编码对应关系如下,实际就是把负数从0开始做了扩展。
| Signed Original | Encoded As |
|---|---|
| 0 | 0 |
| -1 | 1 |
| 1 | 2 |
| -2 | 3 |
| 2147483647 | 4294967294 |
| -2147483648 | 4294967295 |
Non-Varint Numbers
非varint的值比较简单,double和fixed64的类型为1,表示64位固定长度的值;类似地,float和fixed32类型为5,表示固定32为长度的值,这两种情况下以小端序存储
Strings
类型为2,假设创建message如下,
message Test2 {
required string b = ;
}
实际消息b=“testing”
74 65 73 74 69 6e 67
首字节为特殊字节:0001 0010,去除msb位:001 0010,后三位->10表示类型2,右移三位->10表示tag 2;07表示长度为7,74 65 73 74 69 6e 67为"testing"的值。
Embedded Messages
假设定义嵌入的message如下:
message Test3 {
required Test1 c = ;
}
设置Test1.c=150,获得的结果如下,可以看到后三个字节跟上述的一致
1a 08 96 01
Packed Repeated Fields
proto2中使用repeated field需要启用特殊选项[packed=true],在proto3中,默认启用packed。如果packed repeated field中包含0个元素,则它不会出现在被解析的message中。
message Test4 {
repeated int32 d = [packed=true];
}
编码如下:
// tag (field number 4, wire type 2) ->0010 0,010
// payload size (6 bytes)
// first element (varint 3)
8E // second element (varint 270)
9E A7 // third element (varint 86942)
只有使用了原始数据类型(如32-bit或64-bit的varint)的repeated fields才能称之为"packed"
可以看到string,message,repeated field是有长度字段的,而varint由每个字节的msb位表示一个varint是否有后续字节
proto的类型定义如下:
| .proto | 说明 | C++ | Java | Python | Go | Ruby | C# | PHP |
|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | Float | double | float | |
| float | float | float | float | float32 | Float | float | float | |
| int32 | 使用变长编码,对负数编码效率低,如果你的变量可能是负数,可以使用sint32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer |
| int64 | 使用变长编码,对负数编码效率低,如果你的变量可能是负数,可以使用sint64 | int64 | long | int/long | int64 | Bignum | long | integer/string |
| uint32 | 使用变长编码 | uint32 | int | int/long | uint32 | Fixnum or Bignum (as required) | uint | integer |
| uint64 | 使用变长编码 | uint64 | long | int/long | uint64 | Bignum | ulong | integer/string |
| sint32 | 使用变长编码,带符号的int类型,对负数编码比int32高效 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer |
| sint64 | 使用变长编码,带符号的int类型,对负数编码比int64高效 | int64 | long | int/long | int64 | Bignum | long | integer/string |
| fixed32 | 4字节编码, 如果变量经常大于228228 的话,会比uint32高效 | uint32 | int | int | int32 | Fixnum or Bignum (as required) | uint | integer |
| fixed64 | 8字节编码, 如果变量经常大于256256 的话,会比uint64高效 | uint64 | long | int/long | uint64 | Bignum | ulong | integer/string |
| sfixed32 | 4字节编码 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer |
| sfixed64 | 8字节编码 | int64 | long | int/long | int64 | Bignum | long | integer/string |
| bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | |
| string | 必须包含utf-8编码或者7-bit ASCII text | string | String | str/unicode | string | String (UTF-8) | string | string |
| bytes | 任意的字节序列 | string | ByteString | str | []byte | String (ASCII-8BIT) | ByteString | string |
参考:Encoding
protocol buffers的编码原理的更多相关文章
- 理解netty对protocol buffers的编码解码
一,netty+protocol buffers简要说明 Netty是业界最流行的NIO框架之一优点:1)API使用简单,开发门槛低:2)功能强大,预置了多种编解码功能,支持多种主流协议:3)定制能力 ...
- Protocol Buffers工作原理
这里记录一下学习与使用Protocol Buffer的笔记,优点缺点如何使用这里不再叙述,重点关注与理解Protocol Buffers的工作原理,其大概实现. 我们经常使用Protocol Buff ...
- Protocol Buffers编码详解,例子,图解
Protocol Buffers编码详解,例子,图解 本文不是让你掌握protobuf的使用,而是以超级细致的例子的方式分析protobuf的编码设计.通过此文你可以了解protobuf的数据压缩能力 ...
- Protocol Buffers(2):编码与解码
目录 Message Structure 解码代码一窥 varint Protobuf中的整数和浮点数 Length-delimited相关类型 小结 参考 博客:blog.shinelee.me | ...
- google protocol buffer——protobuf的使用特性及编码原理
这一系列文章主要是对protocol buffer这种编码格式的使用方式.特点.使用技巧进行说明,并在原生protobuf的基础上进行扩展和优化,使得它能更好地为我们服务. 在上一篇文章中,我们展示了 ...
- google protocol buffer——protobuf的编码原理二
这一系列文章主要是对protocol buffer这种编码格式的使用方式.特点.使用技巧进行说明,并在原生protobuf的基础上进行扩展和优化,使得它能更好地为我们服务. 在上一篇文章中,我们主要通 ...
- 【笔记】直接使用protocol buffers的底层库,对特定场景的PB编解码进行处理,编码性能提升2.4倍,解码性能提升4.8倍
接上一篇文章:[笔记]golang中使用protocol buffers的底层库直接解码二进制数据 最近计划优化prometheus的remote write协议,因为业务需要,实现了一个remote ...
- protocol buffers生成go代码原理
本文描述了protocol buffers使用.proto文件生成pb.go文件的过程 编译器 编译器需要插件来编译环境,使用如下方式安装插件:go get github.com/golang/pro ...
- Google Protocol Buffers介绍
简要介绍和总结protobuf的一些关键点,从我之前做的ppt里摘录而成,希望能节省protobuf初学者的入门时间.这是一个简单的Demo. Protobuf 简介 Protobuf全称Google ...
随机推荐
- java web开发过程中的“\”指的是什么,如何区分
- [ACM_动态规划] hdu1003 Max Sum [最大连续子串和]
Problem Description Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum ...
- 一般处理程序获取Layui上传的图片
asp.net利用一般处理程序获取用户上传的图片,上传图片利用的layui 前台页面 <%@ Page Language="C#" AutoEventWireup=" ...
- 爬虫浅谈一:一个简单c#爬虫程序
这篇文章只是简单展示一个基于HTTP请求如何抓取数据的文章,如觉得简单的朋友,后续我们再慢慢深入研究探讨. 图1: 如图1,我们工作过程中,无论平台网站还是企业官网,总少不了新闻展示.如某天产品经理跟 ...
- 11-使用EF操作数据库
本篇博客对应视频讲解 回顾 上一篇教程我们讲了XML与JSON的序列化问题,我们可以看到序列化实际上也是不同形式的转换,我们通常要以字节流的形式做中转.同时我们也可以看到,对于序列化这种常见的需求,我 ...
- Python3.5 学习十
多进程: 多线程和多进程的区别: Python多线程不适合CPU操作密集型的任务,适合IO操作密集型的任务(IO操作不占用CPU) Python折中解决多线程不能真正同步运算的方案是:起多个进程,每个 ...
- KNN算法的实现(R语言)
一 . K-近邻算法(KNN)概述 最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来,当测试对象的属性和某个训练对象的属性完全匹配时,便可以对其进行分类.但是怎么可能所有测试对象都会找到 ...
- 攻防组网之—-VMware路由器安装设置
本文作者:i春秋作家——红发香克斯 前言 VMWare功能很强大,我一直有个想法是能不能用一台高配的主机利用VM虚拟化出一个或多个真实的子网,或者子网里面还有子网,其中使用VM自动的功能可以实现多个网 ...
- 实现一个算法,寻找字符串中出现次数最少的、并且首次出现位置最前的字符 如"cbaacfdeaebb",符合要求的是"f",因为他只出现了一次(次数最少)。并且比其他只出现一次的字符(如"d")首次出现的位置最靠前。
实现一个算法,寻找字符串中出现次数最少的.并且首次出现位置最前的字符如"cbaacfdeaebb",符合要求的是"f",因为他只出现了一次(次数最少).并且比其 ...
- centOS7 下安装smb服务器
转载自:http://www.cnblogs.com/lgh344902118/p/7503620.html centOS7和windows10 samba是一款可以让linux和windows下共享 ...