本系列主要是对此次项目从0~1的一个复盘
一来方便自己后期回顾,暴露问题
二来希望可以给新入坑的朋友们一些帮助
不会讲的太细,主要是提供一个思路和大的框架,话不多说,我们开始吧。
项目中如果有不对的地方或者有更好的解决方案,欢迎大家指正!
之前项目都是用的protobuf,此次使用sproto主要原因如下:
① 项目初期定的客户端跟服务器都是用lua开发
② 服务端基于skynet框架开发,而sproto正是由skynet提供的一套协议,它类似 protobuf ,但设计更加简洁,也更利于 lua 使用
官方介绍:https://github.com/cloudwu/skynet/wiki/Sproto
sprotojs插件:https://gitee.com/zwguang/sproto-js
① 插件准备:文档中有案例介绍可以自行了解下,项目中只需要引入sproto.js、utils.js这两个文件
② 协议文件准备: 服务器会提供一个xxx.sproto源文件,将其转换为.spb就可以使用了
③ 协议加载:之前的一篇文章有过记录https://blog.csdn.net/zwg739424406/article/details/111239661
客户端跟服务器商定好每条消息的结构之后,便可以进行对应的 拆包 跟 封包 的工作了。
我们目前的消息结构如下
/* Head
* 2 byte是包头,读取后面body的size
* body
* 2 byte eventid
* 2 byte datasize
* n byte data -- 这个data才是用sproto解析出来的,(pakename, data),pakename是对应的数据结构名字(或者说是table变量的变量名)
*/
核心的拆包和封包代码如下,主要通过对协议加载得到的 m_sproto 进行 encode 和 decode,与之对应的还有 pencode 和 pdecode ,带p的意思是对封好的数据包进一步的压缩,以减少消息的包体。
文中的 sprotoMap 存储的是协议eventid与pakeName的相互映射
/**
* 数据封包 发送
* @param eventId
* @param data
*/
public static encodeBuffer(eventId: number, data: any) {
let id: number = eventId;
let protoName = sprotoMap[id.toString()]
let dataArr = this.m_sproto.pencode(protoName, data);
let dataBuff = this.arr2Buffer(dataArr);
// printf.Log("eventId:", eventId, "protoName:" , protoName, " datass:" , datass)
// printf.Log("dataBuff:", dataBuff, " dataBuff.byteLength:" , dataBuff.byteLength)
let datasizeBuff = this.createBuffer(dataBuff.byteLength);
let eventidBuff = this.createBuffer(eventId);
let bodyBuff = this.ConnectArrayBuff(eventidBuff, datasizeBuff, dataBuff);
let headBuff = this.createBuffer(bodyBuff.byteLength);
return this.ConnectArrayBuff(headBuff, bodyBuff);
}
/**
* 数据解包 派发
* @param buffer
*/
public static decodeBuffer(buffer) {
let view: DataView = new DataView(buffer);
let dataBuff = buffer.slice(6, buffer.length)
let eventId = view.getInt16(2, false)
let name = sprotoMap[eventId];
let data = this.m_sproto.pdecode(name, new Uint8Array(dataBuff));
let message = new sprotoMessage()
message.pakeName = name
message.body = data
return message;
}
private static arr2Buffer(arr){
let len = arr.length;
let arrBuffer = new Uint8Array(len);
for(let i=0; i<len; i++){
arrBuffer[i] = arr[i];
}
return arrBuffer.buffer;
}
private static createBuffer(data) {
var _typeBuff = new ArrayBuffer(2);
var total = data;
var view = new DataView(_typeBuff);
view.setInt16(0, total, false);
return _typeBuff;
}
/**
* 字节头和数据组合成arrayBuff
* @param args 字节头和数据buff
*/
private static ConnectArrayBuff(...args: any[]): ArrayBuffer {
var length = 0;
var buffer = null;
for (let i = 0; i < args.length; i++) {
buffer = args[i];
length += buffer.byteLength;
}
var joined = new Uint8Array(length);
var offset = 0;
for (let i = 0; i < args.length; i++) {
buffer = args[i];
joined.set(new Uint8Array(buffer), offset);
offset += buffer.byteLength;
}
return joined.buffer;
};