概述
protobuf 是谷歌出品一款高性能序列化框架,优点序列化后报文数据小,支持多种多种编程语言(c/c++,java,php,python等主流语言),缺点二进制不可读这倒不重要。
一. 安装
下载源码编译
二. 开发流程
2.1 准备helloworld.proto文件
package com; message helloworld{ required int32 id = 1; required string str = 3; optional int32 age = 2; }
Required 必须字段
Optional 可选字段,用的较多,便于后期平滑升级系统
Repeated 重复字段,相当于传递一个数组
Num 1,2,3 字段序号,不能重复
package 包名,c++中对应namespace,java对应包名
常用数据类型,与c/c++数据结构比较对应
bool
int32/uint32 int64/uint32
float double
string 只能处理ASCII字符
bytes 用于处理多字节语言字符,如中文
enum 枚举
2.2 生成各语言bundle
protoc -I=. --cpp_out=. helloworld.proto protoc -I=. --java_out=./java helloworld.proto
2.3 网络测试
protobuf 最大的优势是跨语言,下面通过C++ udp server来处理java客户端消息。
C++ UDP Server:
/**
* C++ Udp server
*/
void udpServer()
{
int s;
struct sockaddr_in addr_serv;
struct sockaddr_in client;
s = socket(AF_INET, SOCK_DGRAM, 0);
memset(&addr_serv, 0, sizeof(addr_serv));
addr_serv.sin_family = AF_INET;
addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);
addr_serv.sin_port = htons(PORT_SERV);
bind(s, (struct sockaddr*)&addr_serv, sizeof(addr_serv));
int n;
char buff[BUFF_LEN];
socklen_t len;
while(1)
{
len = sizeof(client);
n = recvfrom(s, buff, BUFF_LEN, 0, (struct sockaddr*)&client, &len);
// unserialize
helloworld rmsg;
rmsg.ParseFromArray( buff, BUFF_LEN );
printf( "Recv: %sn", rmsg.DebugString().c_str() );
}
}
Java UDP Client:
/**
* UDP 发送pb数据包
*/
public static void sendPbPacket() {
// Builder
Helloworld.helloworld.Builder builder = Helloworld.helloworld.newBuilder();
builder.setId(505100).setStr("hello world");
builder.setAge(18);
// Make object
Helloworld.helloworld hw = builder.build();
System.out.println( hw.toString() );
// 序列化
byte[] buf = hw.toByteArray();
try {
// 反序列化
Helloworld.helloworld hw1 = Helloworld.helloworld.parseFrom(buf);
System.out.println( hw1.toString() );
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
// UDP发送
DatagramSocket client = null;
try {
client = new DatagramSocket();
InetAddress addr = InetAddress.getByName(host);
DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, addr, port);
client.send(sendPacket);
} catch (Exception e) {
e.printStackTrace();
}finally{
client.close();
}
}
服务端打印:
Recv: id: 505100 str: "hello world" age: 18
可见 protobuf 能完美跨语言间进行序列化过程。
三. protobuf 格式分析
protobuf编码其实类似tlv(tag length value)编码,其内部就是(tag, length, value)的组合,其中tag由(field_number<<3)|wire_type计算得出,field_number由我们在proto文件中定义。
Wireshark将上述通信过程抓包。数据包:
数据段,共19字节:
08 8c ea 1e 12 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64 18 12
1. int id = 505100
08 08 = (1<<3)|0,id序号,从上表查int32对应的Type为0
8c ea 1e 三字节表示数字505100
505100为什么为0x8cea1e呢,下面是转换过程:
十进制: 505100
二进制: 1111011010100001100
按照每7位拆: 001 1110 110 1010 000 1100
交换高低位,填充高位(1或0):1000 1100 1110 1010 0001 1110
十六进制: 0x08 0x0c 0xe 0xa 0x1 0xe
2. string str = "hello world";
12 0x12 = (2<<3)|2
0b 长度为11
68 65 6c 6c 6f 20 77 6f 72 6c 64 hello world
3. int age = 18
18 0x18 = (3<<3)|0
12 十进制18
最后
以上就是苗条小兔子为你收集整理的protobuf 格式分析的全部内容,希望文章能够帮你解决protobuf 格式分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复