前言

记录一下遇到的数据交换协议,不定时更新

JSON

json基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var obj ={
"skillz": {
"web": [
{
"name": "html"
"flag":true
},
{
"name": "css"
"runoob":null
}
],
"database": [
{
"name": "sql"
"years": "7"
}
]
}
}

花括弧表示一个容器,通过名称访问
方括号装载数组,通过序号访问
obj[skillz][web][0][flag]
名称和值冒号隔开
数组元素通过逗号隔开

而 JSONL 文件(JSON Lines),则是一种每行包含一个独立的 JSON 对象的文本文件格式

1
2
3
{"category":"web","name":"html","flag":true}
{"category":"web","name":"css","runoob":null}
{"category":"database","name":"sql","years":"7"}

Protobuf

Protocol Buffers(简称protobuf)是谷歌开发的一种数据序列化格式。在网络通信或数据存储场景中,protobuf可以将数据转化为紧凑的字节流进行传输或保存,接收方可以将字节流还原回原始的数据结构。

试想一下这样的场景,json是字段和值传输的,根据key获取值,但是key字段值我们是知道的,那么我们没必要在传输的数据中保留key,我们可以在服务端保留一个文件,根据这个文件去对照,这样我们就可以省略一半的传输空间,这就是Protocol协议的设计思路

通讯过程

比如说我们想传输以下这个json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"info": {
"some_number": 2824952346709066335,
"pair": {
"a": 2715546123191190817,
"b": 2824925449275048782
}
},
"kv": [
{
"key": "s_device_identity",
"value": "12345"
},
{
"key": "idp_id",
"value": "2824952346709066335"
},
{
"key": "s_user_subject",
"value": "daddaD"
}
]
}

我们可以在本地写个.proto 文件,对应上面的json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
syntax = "proto3";  //声明proto版本,proto3和proto2文件写法不同,功能都一样

message Wrapper {
IdpInfo info = 2;
repeated KV kv = 3; //repeated表示列表
}
message IdpInfo {
int64 some_number = 2;
Pair pair = 3;
}
message Pair {
int64 a = 1; // 2715546123191190817
int64 b = 2; // 2824925449275048782
}
message KV {
string key = 1; // "s_device_identity" / "idp_id" / "s_user_subject"
string value = 2; // uuid / id string / "daddaD"
}

在传输的时候我们省略key(实际为二进制数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2 {
2: 2824952346709066335
3 {
1: 2715546123191190817
2: 2824925449275048782
}
}
3 {
1: "s_device_identity"
2: "28474f19-e4b3-49c8-9ab5-283d5dc4efb9"
}
3 {
1: "idp_id"
2: "2824952346709066335"
}
3 {
1: "s_user_subject"
2: "daddaD"
}

可见传输数据中根本没有 “Pair”、”IdpInfo” 等字段名,哪怕你修改proto文件中的字段名,传输数据依旧不变

当反序列化后,查找Wrapper.info.pair.a的值2715546123191190817时,实际上就是根据field number 来映射,Wrapper.info.pair.a在proto中的映射就是2.3.1

python 实现

定义一个简单的 Person 消息:

1
2
3
4
5
6
7
syntax = "proto3";

message Person {
string name = 1;
int32 id = 2;
string email = 3;
}

使用 protoc 编译 .proto 文件为 Python 代码
protoc --python_out=. person.proto
会在当前目录下生成 person_pb2.py 文件

使用生成的 Python 文件进行序列化和反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
import person_pb2
person = person_pb2.Person()# 创建一个 Person 实例并设置字段值
person.name = "Alice"
person.id = 123
person.email = "alice@example.com"
data = person.SerializeToString()# 序列化数据到字节字符串
print("Serialized data:", data)# 打印序列化结果

person.ParseFromString(data)# 从字节字符串反序列化数据
# 打印反序列化后的字段值
print("Name:", person.name)
print("ID:", person.id)
print("Email:", person.email)

传输数据解析
生成上面的json格式

1
2
3
4
5
6
7
8
9
10
11
12
import base64
import subprocess
try:
decoded_bytes = base64.b64decode("EiAQ34SA6JLmj5onGhQIoYqA4PvZ49clEM6GgIiq14maJxo5ChFzX2RldmljZV9pZGVudGl0eRIkMjg0NzRmMTktZTRiMy00OWM4LTlhYjUtMjgzZDVkYzRlZmI5Gh0KBmlkcF9pZBITMjgyNDk1MjM0NjcwOTA2NjMzNRoYCg5zX3VzZXJfc3ViamVjdBIGZGFkZGFE")
process = subprocess.Popen(['E:\\user\\Desktop\\web\\encode\\protoc\\bin\\protoc.exe', '--decode_raw'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output, error = process.communicate(decoded_bytes)
print ("Result:\n" + str(output))
except KeyboardInterrupt:
pass

gRPC