ProtoBuf 是 Protocol Buffer的简称,是Google旗下一款平台无关、可扩展的序列化结构数据格式,适用于数据存储,作为不同应用、语言之间相互通信的数据交换格式,序列化后的数据为二进制数据(pb格式的数据),类似XML、JSON。由于ProtoBuf的序列化效率和大小比较不错,网络通信上越来越多开始应用,结合WebSocket则是一种趋势。
protobufjs
protobufjs可以直接反射proto文件,不需要生成任何文件。
下载安装protobufjs
https://github.com/protobufjs/protobuf.js
使用NPM全局安装protobufjs
$ npm i -g protobufjs
$ npm i -g @egret/protobuf
将下载的protobuf.js文件放入bin/libs/
文件夹下,然后在bin/index.html
文件加载的index.js
文件中添加loadLib("libs/protobuf.js")
,即完成加载。
$ vim /bin/index.js
loadLib("libs/protobuf.js");
创建ProtoBuf协议文件
$ vim /bin/proto/msg.proto
syntax = "proto3";
package ns;
message Address{
required string province = 1;
required string city = 2;
required string area = 3;
}
在Laya中使用protobuf.js
$ vim src/Test.ts
class Test{
private pb = null;
constructor(){
Laya.init(Laya.Browser.clientWidth, Laya.Browser.clientHeight);
Laya.Stat.show();
this.initStage();
this.run();
}
initStage():void{
Laya.stage.autoSize = false;
Laya.stage.frameRate = "slow";
Laya.stage.screenMode = "horizontal";
Laya.stage.scaleMode = "exactfit";
Laya.stage.mouseEnabled = false;
Laya.stage.alignH = "center";
Laya.stage.alignV = "middle";
Laya.stage.bgColor = "#808080";
}
run(){
this.pb = Laya.Browser.window.protobuf;
if(!this.pb){
throw Error("your browser is not support protobuffer");
}
this.pb.load("proto/msg.proto", this.onProtoLoaded);
}
onProtoLoaded(error, root){
if(error){
throw error;
}
//查找
let Message = root.lookup("ns.Address");
console.log(Message);
//创建
let message = Message.create({province:"hunan"});
console.log(message);//Address {province: "hunan"}
//验证
let errmsg = Message.verify(message);
console.log(errmsg);//null
if(errmsg){
throw Error(errmsg);
}
//编码
let buffer = Message.encode(message).finish();
console.log(buffer);//Uint8Array(11) [10, 5, 104, 117, 110, 97, 110, 18, 0, 26, 0]
//解码
try{
let message = Message.decode(buffer);
console.log(message);//Address {province: "hunan", city: "", area: ""}
}catch(e){
if(e instanceof this.pb.util.ProtocolError){
console.error("missing required field");
}else{
console.error("wire format is invalid");
}
}
}
}
new Test();
封装
export default class Protobuf{
/**
* 单例模式
*/
private static _instance:Protobuf;
public static getInstance():Protobuf{
if(!this._instance){
this._instance = new Protobuf();
}
return this._instance;
}
private constructor(){}
/**
* 获取protobuf文件中的消息类
* @param protoUrl {string} protobuf文件地址
* @param messageName {string} 消息类名
* @param callback {Function} 回调函数
* @param thisObj {any} this对象
*/
public getMessage(protoUrl:string, messageName:string, callback:Function, thisObj:any = null):void{
let pb = Laya.Browser.window.protobuf;
if(!pb){
throw Error("your browser is not support protobufjs");
}
pb.load(protoUrl, (error, root)=>{
if(error){
throw error;
}
let messageClass = root.lookup(messageName);
callback && callback.apply(thisObj, [messageClass]);
});
}
/**
* 创建消息
* @param messageClass {Message} 消息类
* @param data {object} 消息字段集合
*/
public create(messageClass, data:Object){
return messageClass.create(data);
}
/**
* 消息验证
* @param messageClass {Message} 消息类
* @param message {Object} 验证消息
*/
public verify(messageClass, message){
let errmsg = messageClass.verify(message);
if(errmsg){
throw new Error(errmsg);
}
}
/**
* 序列化编码生成protobuf
* @param messageClass {Meesage} 消息类
* @param message {object} 消息
*/
public encode(messageClass, message){
return messageClass.encode(message).finish();
}
/**
* 反序列化解码生成对象
* @param messageClass {Message} 消息类
* @param buffer {} protobuf
*/
public decode(messageClass, buffer){
return messageClass.decode(buffer);
}
}
测试
import Protobuf from "./libs/Protobuf";
class Test{
private pb = null;
constructor(){
Laya.init(Laya.Browser.clientWidth, Laya.Browser.clientHeight);
Laya.Stat.show();
this.initStage();
this.run();
}
initStage():void{
Laya.stage.autoSize = false;
Laya.stage.frameRate = "slow";
Laya.stage.screenMode = "horizontal";
Laya.stage.scaleMode = "exactfit";
Laya.stage.mouseEnabled = false;
Laya.stage.alignH = "center";
Laya.stage.alignV = "middle";
Laya.stage.bgColor = "#808080";
}
run(){
Protobuf.getInstance().getMessage("proto/msg.proto", "ns.Address", (Message)=>{
console.log(Message);//Type {options: undefined, name: "Address", parent: Namespace, resolved: false, comment: null, …}
let data = {province:"", city:"", area:""};
data.province = "hunan";
data.city = "changsha";
data.area = "yuelu";
let message = Message.create(data);
console.log(message);//Address {province: "hunan", city: "changsha", area: "yuelu"}
let errmsg = Message.verify(message);
console.log(errmsg);//null
if(errmsg){
throw new Error(errmsg);
}
let buffer = Message.encode(message).finish();
console.log(buffer);//Uint8Array(24) [10, 5, 104, 117, 110, 97, 110, 18, 8, 99, 104, 97, 110, 103, 115, 104, 97, 26, 5, 121, 117, 101, 108, 117]
let msg = Message.decode(buffer);
console.log(msg);//Address {province: "hunan", city: "changsha", area: "yuelu"}
});
}
}
new Test();