前言
记录一下遇到的数据交换协议,不定时更新
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.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