IT评测·应用市场-qidao123.com

标题: 使用脚本收发 protobuf 协议数据 [打印本页]

作者: 悠扬随风    时间: 2023-6-6 11:51
标题: 使用脚本收发 protobuf 协议数据
问题背景

最近做了一个 ipv6 相关的功能,发现使用 getifaddrs 获取的本地 ipv6 地址有可能不是真实的网络 ipv6 地址:

例如上图中通过 getifaddrs 获得了多个本地 ipv6 地址,其中  开头的已知是本地 ipv6 地址,被排除;还有  这种,其实也是 "假 ipv6" 地址,对应的设备并不能访问 ipv6 网络。
对于这种假 v6 地址,无法通过遍历的方式进行枚举排除,而一旦将 v4 网络环境错认为是 v6 环境,对后面的网络操作影响比较大。需要引入一种准确判断当前网络是否有 ipv6 访问能力的方法,为此 server 端同学专门给了一个判断接口。
probe_v6_addr

出于安全考虑,这里只列出接口名称部分:
  1. http://xxx.xxxxxxxxxx.xxxxxxx.xxxxx.xxx/xxx/probe_v6_addr
复制代码
访问这个接口有两种返回,当不存在 v6 网络环境时:
  1. no v6 addr
复制代码
当存在时,返回本机的 ipv6 地址:
  1. $ curl -s http://xxx.xxxxxxxxxx.xxxxxxx.xxxxx.xxx/xxx/probe_v6_addr
  2. +
  3. %240e:304:8183:2bcc:c16d:22d0:74ba:23e??-
  4. '2408:832e:c272:b36e:55bc:554a:8952:553e,
  5. &240e:3a0:7005:6ae2:d05a:754a:c21b:6c35??+
  6. %240e:310:915:d939:9041:c01c:82db:a043??-
  7. '2408:832e:c271:3851:6926:e953:e741:b1a3??+
  8. %240e:378:1e0c:db62:7088:a216:87c:4ccd??OP46C3:/
复制代码
虽然有部分二进制信息干扰,但是 ipv6 地址部分还是看得比较清楚的。返回的地址和 ifconfig 的结果可以相互印证:
  1. $ ifconfig | grep inet6
  2.           inet6 addr: fe80::fc8e:84ff:fec0:1534/64 Scope: Link
  3.           inet6 addr: 240e:505:7e01:2994:f43c:5fc9:609e:5de6/64 Scope: Global
  4.           inet6 addr: fe80::f43c:5fc9:609e:5de6/64 Scope: Link
  5.           inet6 addr: fe80::8fd0:cd9e:52cd:5bc3/64 Scope: Link
  6.           inet6 addr: 2409:8100:7b00:5781:a4a8:71ce:b11:3c5e/64 Scope: Global
  7.           inet6 addr: fe80::a4a8:71ce:b11:3c5e/64 Scope: Link
  8.           inet6 addr: ::1/128 Scope: Host
  9.           inet6 addr: fe80::29f8:41f:7564:501d/64 Scope: Link
  10.           inet6 addr: 240e:404:7e01:5d77:29f8:41f:7564:501d/64 Scope: Global
  11.           inet6 addr: fe80::3d14:7716:4771:88fa/64 Scope: Link
  12.           inet6 addr: 240e:304:8183:2bcc:c16d:22d0:74ba:23e/64 Scope: Global
  13.           inet6 addr: 240e:304:8183:2bcc:d8c5:dce4:a89c:8a88/64 Scope: Global
复制代码
其中 ipv6 地址240e:304:8183:2bcc:c16d:22d0:74ba:23e/64在两边都存在。
protobuf

上面的接口确实是基于二进制数据的协议,虽然是私有协议,但是采用了 protobuf 来进行规范,在提高性能的同时,也保留了一定的通用性。
但是这样一来,往常惯用的 curl + shell 大法要失灵了,给测试和验证工作带来了不小的麻烦。
不过好在有 proto 文件,生成一段解析的 c++ 代码也不是不可能:
  1. > cat msg.proto
  2. message ProbeIpv6Request {
  3.     string xxxxx     = 1;
  4.     string xxxx      = 2;
  5.     string xxxxxxxx  = 3;
  6.     string xxxxxxx   = 4;
  7. }
  8. message V6AddrType {
  9.     string addrV6 = 1;
  10.     uint32 portV6 = 2;
  11. }
  12. message ProbeIpv6Response {
  13.     string              xxxxx    = 1;
  14.     V6AddrType          selfAddr = 2;
  15.     repeated V6AddrType brosAddr = 3;
  16. }
复制代码
这个 proto 文件揭示了两点:
如果使用 protoc 程序根据 msg.proto 生成 c++ 代码,再写程序解析数据,就用不着写这篇文章了。毕竟那种方式太牛刀杀鸡了,下面演示一种使用 shell 脚本就能搞定 protobuf 协议的新方法。
pbjs

在介绍新方法之前,先介绍本文的主角 pbjs。首先是在 mac 上的安装:
  1. brew install node
  2. brew install npm
  3. npm install -g protobufjs
  4. npm install -g pbjs
复制代码
pbjs 是 nodejs 提供的,用来将 protobuf 二进制数据转换为 json,所以需要先安装 nodejs、npm 环境,linux 上的安装大同小异,此处不再赘述。
执行成功后验证 pbjs 是否安装:
  1. > pbjs
  2. Usage: pbjs [options] <schema_path>
  3. Options:
  4.   -V, --version        output the version number
  5.   --es5 <js_path>      Generate ES5 JavaScript code
  6.   --es6 <js_path>      Generate ES6 JavaScript code
  7.   --ts <ts_path>       Generate TypeScript code
  8.   --decode <msg_type>  Decode standard input to JSON
  9.   --encode <msg_type>  Encode standard input to JSON
  10.   -h, --help           output usage information
  11. > which pbjs
  12. /Users/yunhai01/tools/node-v14.17.0-darwin-x64/bin/pbjs
  13. > ls -lh /Users/yunhai01/tools/node-v14.17.0-darwin-x64/bin/pbjs
  14. lrwxr-xr-x  1 yunhai01  staff    31B Apr 16 18:26 /Users/yunhai01/tools/node-v14.17.0-darwin-x64/bin/pbjs -> ../lib/node_modules/pbjs/cli.js
复制代码
看起来这就是一个 node module 的软链接。
pbjs 的功能有很多,help 信息中已经罗列出来了,例如生成 js 代码 (--es5/--es6),生成 ts 代码 (--ts),不过最让我感兴趣的还是 --decode,意思是可以将数据解析为 json,下面用上一节的二进制数据做个练手,假设数据已经保存在名为 response.bin 的文件:
  1. > pbjs msg.proto --decode ProbeIpv6Response < response.bin
  2. {
  3.   "selfAddr": {
  4.     "addrV6": "240e:304:8183:2bcc:c16d:22d0:74ba:23e",
  5.     "portV6": 47832
  6.   },
  7.   "brosAddr": [
  8.     {
  9.       "addrV6": "240e:333:6b00:b00e:38db:2815:306b:3d9b",
  10.       "portV6": 18947
  11.     },
  12.     {
  13.       "addrV6": "240e:333:1707:ca6f:24d3:61ae:86cf:a6fa",
  14.       "portV6": 18112
  15.     },
  16.     {
  17.       "addrV6": "2409:8a38:9002:70b3:19a3:66a3:d778:65cc",
  18.       "portV6": 18780
  19.     },
  20.     {
  21.       "addrV6": "2408:8266:700:1a62:8ad0:4097:9220:577b",
  22.       "portV6": 18595
  23.     },
  24.     {
  25.       "addrV6": "240e:3a0:9001:4013:99c0:11c4:7d3b:e8e5",
  26.       "portV6": 18319
  27.     }
  28.   ]
  29. }
复制代码
哈哈,果然成功,过程异常丝滑!
jq

有了 json 数据就好办了,下面上 jq 提取设备 IP,假设已经将数据保存在了 response.json 文件中:
  1. > jq -r '.selfAddr.addrV6'  probe_v6.json
  2. 240e:304:8183:2bcc:c16d:22d0:74ba:23e
复制代码
和之前猜测的 IP 地址结果一致。
结语

pbjs 不光可以用来解析响应,也可以用来构造 protobuf 格式的请求,主要就是依赖它的 --encode 参数:
  1. pbjs msg.proto --encode ProbeIpv6Request < request.json > request.bin
复制代码
注意 --decode/--encode 一次只能处理一个消息类型,而协议文件中可能包括多个,所以需要在这里为它们进行指定,之前指定的是 ProbeIpv6Response 消息,这里改为 ProbeIpv6Request 消息。
关于 request.json 文件,简单的可以直接手动构造,复杂的可以借助 jq --arg 动态生成,这方面详细的信息可以参考我之前写的这几篇文章:《用 shell 脚本做 tcp 协议模拟》、《使用 shell 脚本自动申请进京证 (六环外)》。
至此 protobuf 二进制数据也不再是脚本不可触控的区域,有这方面接口测试需求的同学们快用起来吧 ~
后记

使用基于 pbjs 的脚本在 android 设备上验证上述接口后,能正确返回结果,并且发现了几个小问题,为后面写 c++ 代码接入铺平了道路,比起直接使用 adb 跑脚本,编译 sdk 再打 apk 包验证成本实在是太高了,pbjs 确确实实提升了我的效率。
参考

[1]. Protocol Buffers for JavaScript
[2]. 工作笔记:protobufjs使用教程,支持proto文件打包成typescript或javascript脚本
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4