cocos creator 教程:框架 - 网络
【muzzik 教程】:框架 - 网络
实现功能
-
基础功能(连接、发送、接收、重连、心跳、编解码)
-
多网络连接
-
消息请求(等待返回)
-
发送潮(批量发送)
-
服务端消息模拟
模块结构
平台适配
// ...
protobufjs 使用
安装
npm i protobufjs
npm i --save-dev protobufjs-cli
这是 pbjs/pbts 的命令行工具,旧版的 protobufjs pbjs/pbts 是直接在包内的,最新的分开了
导入
- 默认:
import protobufjs from "protobufjs";
- 指定版本:
import protobufjs from "protobufjs/minimal.js";
pbjs/pbts 生成
pbjs
注意:如果你们项目内 package.json 存在属性 "type": "module"
则使用 es6 的生成类型,否则使用 commonjs 的生成类型
使用 commonjs 的生成类型
npx pbjs -t static-module -w commonjs -l eslint-disable --keep-case -o ./test.js ./*.proto
后处理
-
-------------------------------分割-----------------------------
var $protobuf = require("protobufjs/minimal");
替换为
var $protobuf = require("protobufjs/minimal.js");
-
-------------------------------分割-----------------------------
var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
替换为
var $root = {};
使用 es6 的生成类型
npx pbjs -t static-module -w es6 -l eslint-disable --es6 --keep-case -o ./test.js ./*.proto
后处理
-
-------------------------------分割-----------------------------
import * as $protobuf from "protobufjs/minimal";
替换为
import $protobuf from "protobufjs/minimal.js";
-
-------------------------------分割-----------------------------
const $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
替换为
const $root = {};
pbts
由于 creator 模块规范限制导入 js 必须存在扩展名(.js),所以我们导出的文件名后缀必须是 *.js.d.ts
npx pbts -m -o ./声明文件名.js.d.ts ./proto静态模块.js
后处理
可选:$protobuf
替换为 protobuf
避免 vscode 飘红
怎么添加消息号?
目前论坛里的方法都是通过 二次包装 或者 拼接消息号 实现
- 二次包装:消息号的消息体包装消息 bytes 类型的消息体
proto 定义
message send_data { uint32 id = 1; bytes body = 2; } message send_data_body { string data = 1; }
使用
/** 消息体字节 */ const body = test.send_data_body.encode(test.send_data_body.create({ data: "1" })).finish(); /** 消息 */ const mess = test.send_data .encode( test.send_data.create({ id: 100, body: body, }) ) .finish();
- 拼接消息号:将消息号添加到消息数据头部
使用
const buff = new ArrayBuffer(消息号占用字节 + 消息体占用字节); const data2 = new Uint8Array(buff); const data3 = new DataView(buff); // 设置消息号 data3.setUint16(0, 消息号); // 设置消息体 data2.set(消息体, 2);
有没有更高效更方便的方式呢?当然是有的,我在自己的框架中实现了这种方式,也就是使用 默认值
proto 定义
message test { uint32 __id = 1 [default = 100]; string data = 2; }
使用
// 编码 const body = test.test.create({ data: "1" }); body["__id"] = body["__id"]; const mess = test.test.encode(body).finish();
// 解码 /** 消息体 */ const data_uint8_as = new Uint8Array(data_); /** 消息号 */ const id_n = protobufjs.Reader.create(data_uint8_as).skipType(0).uint32(); /** 消息 */ const mess = this._mess_map.get(id_n); return mess.decode(data_uint8_as)
使用默认值的好处
- 发送接口不需传递消息号,只需消息体就可知消息号
- 更省传输字节
- 更好的性能
参考
性能比较
-
1w 次
-
10w 次
proto 定义
package test; >syntax = "proto3"; message test { uint32 __id = 1 [default = 100]; string data = 2; } message send_data { uint32 id = 1; bytes body = 2; } message send_data_body { string data = 1; }
测试代码
// 二次包装 { this._log.time_start("二次包装"); let temp: any; for (let k_n = 0; k_n < for_n; ++k_n) { /** 消息体字节 */ const body = test.send_data_body.encode(test.send_data_body.create({ data: "1" })).finish(); /** 消息 */ temp = test.send_data .encode( test.send_data.create({ id: 100, body: body, }) )> .finish(); } this._log.time_end("二次包装"); } // 拼接消息号 { this._log.time_start("拼接消息号"); for (let k_n = 0; k_n < for_n; ++k_n) { /** 消息体字节 */ const body = test.send_data_body.encode(test.send_data_body.create({ data: "1" })).finish(); const buff = new ArrayBuffer(2 + body.length); const mess = new Uint8Array(buff); const data_view = new DataView(buff); // 设置消息号 data_view.setUint16(0, 100); // 设置消息体 mess.set(body, 2); } this._log.time_end("拼接消息号"); } // 默认消息号(mk 框架) { this._log.time_start("默认消息号"); let temp: any; for (let k_n = 0; k_n < for_n; ++k_n) { const body = test.test.create({ data: "1" }); // eslint-disable-next-line no-self-assign body["__id"] = body["__id"]; temp = test.test.encode(body).finish(); } this._log.time_end("默认消息号"); }
本文来自博客园,作者:Muzzik,转载请注明原文链接:https://www.cnblogs.com/muzzik/p/17060955.html