zoukankan      html  css  js  c++  java
  • protocol buffer——nodejs中的实践

    本次采是使用google提供的windows版本的protoc工具,下载地址为https://github.com/protocolbuffers/protobuf/releases,选择适合自己的环境下载即可(可能还需要设置环境变量,按照提示信息进行安装即可。如果没有现成的工具,可选择编译源码)

    一、编写.proto文件

    本次所提供的.proto文件采用较为简单的格式,其他格式如引用、枚举等方式暂不举例:

    syntax="proto3";      //不指定proto3,默认为proto2格式
    package pbdata;      //包名,同一项目工程的文件可定义在一个包内
    
    message RealTrackData{ 
    	string type = 1;            //非枚举类型,首个默认值不能为0
    	string Task_i_d = 2;
    	string Time = 3;
    	string Longitude = 4;
    	string Latitude = 5;
    	string Direction = 6;
    	string Target_speed = 7;
    }
    

    注意proto文件中的命名方式,经过protol序列化与反序列化后,会对驼峰下划线进行转换,情况如下:

    字段形式 例子 set函数 例子 反序列化解析后形式 例子
    全小写 taskid set+大写开头 setTaskid 全小写 taskid
    大写开头 Taskid set+大写开头 setTaskid 全小写 taskid
    小驼峰 taskId set+大写开头 setTaskid 全小写 taskid
    下划线 task_id set+大驼峰 setTaskId 小驼峰 taskId
    连续下划线 task_i_d set+大驼峰 setTaskID 小驼峰 taskID

    所以,建议字段名使用下划线方式进行定义(最后一种方式是一种不优雅的折中办法,不是很建议使用)

    另外,枚举类型`enum`中首个属性的默认值必须为`0`,`enum`外的普通属性的默认值不能为`0`。
    

    二、使用protoc.exe编译

    按照以下方式执行(cmd和git bash皆可):

    protoc.exe --js_out=import_style=commonjs,binary:. name.proto

    commonjs是为了在nodejs中使用commonjs方式进行使用(注意commonjs与binary中间是英文逗号,),待编译文件.proto最好放在与proto.exe相同的目录,免得手动输入路径。编译过后会生成js文件(原文件名+pb.js),其中有些关键函数:

    2.1 类定义

    /**
     * Generated by JsPbCodeGenerator.
     * @param {Array=} opt_data Optional initial data array, typically from a
     * server response, or constructed directly in Javascript. The array is used
     * in place and becomes part of the constructed object. It is not cloned.
     * If no data is provided, the constructed object will be empty, but still
     * valid.
     * @extends {jspb.Message}
     * @constructor
     */
    proto.pbdata.RealTrackData = function(opt_data) {
      jspb.Message.initialize(this, opt_data, 0, -1, null, null);
    };
    goog.inherits(proto.pbdata.RealTrackData, jspb.Message);
    if (goog.DEBUG && !COMPILED) {
      /**
       * @public
       * @override
       */
      proto.pbdata.RealTrackData.displayName = 'proto.pbdata.RealTrackData';
    }
    

    2.2 set函数(以taskID为例)

    /**
     * @param {string} value
     * @return {!proto.pbdata.RealTrackData} returns this
     */
    proto.pbdata.RealTrackData.prototype.setTaskID = function(value) {
      return jspb.Message.setProto3StringField(this, 2, value);
    };
    

    2.3 get函数(以taskID为例)

    /**
     * optional string Task_i_d = 2;
     * @return {string}
     */
    proto.pbdata.RealTrackData.prototype.getTaskID = function() {
      return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
    };
    

    2.4 SerializeBinary 序列化函数

    /**
     * Serializes the message to binary data (in protobuf wire format).
     * @return {!Uint8Array}
     */
    proto.pbdata.RealTrackData.prototype.serializeBinary = function() {
      var writer = new jspb.BinaryWriter();
      proto.pbdata.RealTrackData.serializeBinaryToWriter(this, writer);
      return writer.getResultBuffer();
    };
    

    2.5 DeserializeBinary 反序列化函数

    /**
     * Deserializes binary data (in protobuf wire format).
     * @param {jspb.ByteSource} bytes The bytes to deserialize.
     * @return {!proto.pbdata.RealTrackData}
     */
    proto.pbdata.RealTrackData.deserializeBinary = function(bytes) {
      var reader = new jspb.BinaryReader(bytes);
      var msg = new proto.pbdata.RealTrackData;
      return proto.pbdata.RealTrackData.deserializeBinaryFromReader(msg, reader);
    };
    

    2.6 exports暴露方式

    goog.object.extend(exports, proto.pbdata);
    

    注意序列化函数SerializeBinary是原型函数,需要new对象后进行调用,反序列化DeserializeBinary是静态函数,直接通过类名进行调用。
    一般的是先将数据经过set函数进行设值,再使用序列化函数转换为Unit8Array的二进制数据,通过一定的网络通讯(ws || HTTP),将二进制数据发送出去。接收端收到后,再通过反序列化得到实际的数据。

    三、测试ws方式进行解析

    前后端采用websocket进行通讯,为了方便测试,直接在ws建立连接时,就发送二进制数据。
    该数据的通过上述的插件中set函数设置(有点像设置类中变量),例如:

    let realtrack = new RealTrackData_pb();
    realtrack.setTaskID('2');
    //set其他值
    let buf = realtrack.serializeBinary();
    
    ws.send(buf);//伪代码,ws发送
    

    此处,要注意set函数的参数类型要与proto中的类型一致,否则会设置无效。例如taskID定义为string,但set时传入的是数字0,则后面解析出来的就是无效的空字符串。切记勿照搬JS中类型灵活的特点

    四、编写简单前端页面

    前端采用H5的webSocket对象建立长连接,这里注意要设置以下接收数据的方式(只需要在client端设置):

    ws.binaryType = 'arraybuffer'
    

    如果不设置为arraybuffer,得到的二进制数据为默认的blob类型(还不知blob数据应该怎么使用)。
    因为前端需要对接收到的二进制数据进行反序列化,所以要将proto工具转换的nodejs插件转化成浏览器可用的js文件,再在html上引用(vue这种可以直接用import引用的不需要转换)。这里采用browserify(需要npm 全局安装)将commonjs形式的模块转换成浏览器可使用的模块:(函数名和功能不变)

    browserify RealTrackData_pb.js > realtrack.js
    

    生成的js文件,可直接在html上通过script引用,在ws监听message中进行deserializeBinary反序列化操作:

    ws.onmessage = function(evt) {
        var buf = new Uint8Array(evt);
        var data = proto.pbdata.RealTrackData.deserializeBinary(buf);
        console.log(data.toObject());
    }
    

    会发现打印结果与服务端通过set函数设置的值一致。

    google-protocol与protocol.js的区别:
    google-protocol生成相应语言的插件进行使用,protocol.js通过直接load需要使用的proto文件,配合一些封装好的函数使用(还有待测试验证)。
    
  • 相关阅读:
    数据库表结构变动发邮件脚本
    .net程序打包部署
    无法登陆GitHub解决方法
    netbeans 打包生成 jar
    第一次值班
    RHEL6 纯命令行文本界面下安装桌面
    C语言中格式化输出,四舍五入类型问题
    I'm up to my ears
    How to boot ubuntu in text mode instead of graphical(X) mode
    the IP routing table under linux@school
  • 原文地址:https://www.cnblogs.com/Mr-Kahn/p/12728374.html
Copyright © 2011-2022 走看看