MQTT协议如何发送protobuf数据

1,417 阅读2分钟

本篇文章主要介绍如何通过静态or动态proto schema,校验json并转换成buffer;读取buffer转换成可读性的json格式。

依赖库

vite@2.9.9
vue@2.6.11
mqtt@4.1.0
protobufjs~6.11.3

protobufjs使用参考文档:febeacon.com/protobuf_do…

安装protobufjs

npm install protobufjs [--save --save-prefix=~]

静态.proto文件转成.js文件

  1. 在src下新建proto目录,将.proto文件放进去
  2. package.json里定义命令,该命令会将.proto文件转换.js文件
    "proto": "pbjs -t json-module -w es6 -o src/proto/proto.js src/proto/transport.proto"
  3. 执行npm run proto命令,会发现proto目录下会生成proto.js文件

image.png

引入protobufjs

import protobuf from "protobufjs";

引入转换后的proto.js文件

import protoRoot from "../../proto/proto.js";

根据静态.proto文件,buffer和json互相转换

将buffer转成json(根据proto schema)

.proto案例

syntax = "proto3";
package transport;

message ToServerRpcResponseMsg {
  int32 requestId = 1;
  string payload = 2;
  string error = 3;
}

转换方法

// 此处message需要传入buffer格式
function getJSON(message) {
    let msg = ""
    const MyMessage = protoRoot.lookup("transport.ToServerRpcResponseMsg");
    if (!MyMessage) {
        msg = "";
    }

    try {
        const decodedMessage = MyMessage.decode(message);
        msg = JSON.stringify(decodedMessage);
    } catch (e) {
        console.log("e", e);
    }
    return msg;
}

校验json并转成buffer(根据proto schema)

.proto案例

syntax = "proto3";
package transportapi;

message RpcRequest {
  string method = 1;
  string params = 2;
}

转换方法

// 此处payload需要是json格式
function toProtobufBuffer(payload) {
    const MyMessage = protoRoot.lookup("transportapi.RpcRequest");
    if (errMsg) {
        Message.error("内容格式不正确。");
        return null;
    }
    const req = MyMessage.create(JSON.parse(payload));
    const buffer = MyMessage.encode(req).finish();
    if (!buffer.length) {
        // 若不符合proto规则定义,转换出来则是空
        Message.error("内容格式不正确。");
        return null;
    }
    return buffer;
}

根据动态proto schema,buffer和json互相转换

import protobuf from "protobufjs";

// 入参是动态proto schema,返回值是可操作的MyMessage,拿到MyMessage后,操作和静态一样。
function getMyMessage(proto) {
    const root = protobuf.parse(proto, { keepCase: true }).root;
    // 找出类型名称typeName
    let typeName = "";
    traverseTypes(
        root,
        type => {
          typeName = type.fullName.substr(1, type.fullName.length - 1); // 此方法可根据定义的schema做响应调整
        },
        0
    );
    const MyMessage = root.lookup(typeName);
    if (!MyMessage) {
        // 定义的proto schema格式不正确。
        return null;
    }
    return MyMessage;
}


function traverseTypes(current, fn, count) {
  if (count === 2) {
    // 取出package和最外面的message
    fn(current);
  }
  if (current.nestedArray)
    current.nestedArray.forEach(function (nested) {
      traverseTypes(nested, fn, count + 1);
    });
}

使用vite打包后异常

参考issue:github.com/vitejs/vite…
使用vite打包构建后,页面渲染异常,但没有具体的报错信息
解决方式:通过在vite.config.js里定义别名

defineConfig({
   resolve: {
      alias: [
        {
          find: new RegExp("protobufjs/light$"),
          replacement: "protobufjs/dist/light/protobuf.min.js"
        },
        {
          find: new RegExp("protobufjs/minimal$"),
          replacement: "protobufjs/dist/minimal/protobuf.min.js"
        },
        {
          find: new RegExp("protobufjs$"),
          replacement: "protobufjs/dist/protobuf.min.js"
        }
      ]
    }
})