一、环境
操作系统 mac 10.12.2
CocosCreator 1.5.1
protobuf.js 6.7.0
二、正文
1、把与服务器协议好的.proto(我的文件是client.proto)文件放在assets/resources文件夹下。
2、在https://github.com/dcodeIO/protobuf.js/releases/上下载相应的protobuf.js,把protobuf.js放到你的项目的script文件夹下(不一定是script文件夹,这只是一个规范,因为CocosCreator是仿照u3d来做的,而u3d的做法是把脚本放在script文件夹下)。
新建ProtobufManager.js(依旧是script文件夹下或者script的子目录),然后,上代码,这是本人小封装了一下的代码:
var ProtobufManager;
(function(ProtobufManager) {
ProtobufManager.heartCount = 1;
var loadProtobuf = function() {
function callback(err, root) {
ProtobufManager.Root = root;
ProtobufManager.Command = root.lookup("proto.Command").values;
GameServerManager.connectServer();
};
protobuf.load(cc.url.raw("resources/proto/client.proto"), callback);
};
ProtobufManager.loadProtobuf = loadProtobuf;
ProtobufManager.sendAutoID = function() {
var autoID = {id : this.heartCount};
this.serialize("proto.AutoID", autoID, this.Command.client_heart_beat);
}
ProtobufManager.getAutoID = function(buffer) {
return this.decode("proto.AutoID", buffer);
}
ProtobufManager.serialize = function(proto, object, command) {
var protobufObj = this.Root.lookupType(proto);
this.serializeProtobuf(protobufObj, object, command);
}
ProtobufManager.serializeProtobuf = function(protobufObj, object, command) {
var message = this.checkMessage(protobufObj, object);
var buffer = this.serializeMessage(protobufObj, message);
this.packProtobuf(command, buffer);
}
ProtobufManager.checkMessage = function(protobufObj, object) {
var errMsg = protobufObj.verify(object);
if(errMsg) {
throw Error(errMsg);
}
var message = protobufObj.fromObject(object);
return message;
}
ProtobufManager.serializeMessage = function(protobufObj, message) {
var buffer = protobufObj.encode(message).finish();
var array = new Uint8Array(buffer);
return array;
}
ProtobufManager.decode = function(proto, buffer) {
var protobufObj = this.Root.lookupType(proto);
return this.decodeProtobuf(protobufObj, buffer);
}
ProtobufManager.decodeProtobuf = function(protobufObj, buffer) {
return protobufObj.decode(buffer);
}
ProtobufManager.packProtobuf = function(command, buffer) {
var len = buffer.length;
var byteArr = new ArrayBuffer(len + 10);
var dv = new DataView(byteArr, 0);
dv.setUint32(0, len + 6, false);
dv.setUint32(4, this.heartCount, false);
dv.setUint16(8, command, false);
for(var i = 0; i < len; i++) {
dv.setUint8(10 + i, buffer[i], false);
}
this.heartCount++;
GameServerManager.send(byteArr);
}
})(ProtobufManager || (ProtobufManager = {}));
window.ProtobufManager = ProtobufManager;
下面,给出我用的client.proto文件
// 包结构
// +------------------+-------------------+------------------+----------------+
// | length(uint32) | sequence(uint32) | command(uint16) | frame([]byte) |
// +------------------+-------------------+------------------+----------------+
// 4 byte 4 byte 2 byte
// length: 包大小
// sequence: 请求包的序号,新会话自1开始递增
// command: 协议号
// frame: 具体Message序列化后的二进制数据
// 返回包与请求包格式一致,服务器主动推送包sequence为0,请求返回包sequence同请求包一致
// server
// 113.10.201.102:3800
enum Proto {
option allow_alias = true;
UNKNOWN = 0;
PackSize = 4;
SeqIDSize = 4;
CommandSize = 2;
}
enum Command {
// 主命令 (0 ~ 99)
client_heart_beat = 0; // 心跳包.. -> AutoID
client_heart_beat_ack = 1; // 心跳响应 -> AutoID
// 测试命令 (10000 ~)
//test_any = 10001;
test_oneof = 10002;
test_map = 10003;
}
message Test {
// enum anyType {
// t_int64 = 0; // AutoID
// t_str = 1; // String
// }
// message Any {
// anyType type = 1;
// google.protobuf.Any body = 2;
// }
message Oneof {
oneof test_oneof{
string name = 1;
int32 age = 2;
}
}
message Map {
map<uint32, double> body = 1;
}
}
// 请求的通用回复
message CommonReply {
enum Code {
SUCCESS = 0;
ERR_INVALID_PROTOCOL = 1;
ERR_INVALID_DATA = 2; //客户端数据错误
ERR_INVALID_OPERATION = 3; //无效操作
ERR_USER_UNUSABLE = 6; //用户未激活
ERR_ACCOUT_LOCK = 7; //账户被锁定,禁止下注
ERR_AUTHFAIL = 401; //登录认证失败
ERR_SERVER_INTERNAL_ERROR = 500; //服务端内部错误
}
Code code = 1;
string desc = 2;
}
// 用于只传id或一个数字的结果, 如心跳, 大厅人数
message AutoID {
int64 id = 1;
}
代码里的GameServerManager是一个websocket。如果大家不了解websocket怎么使用,这里有我的下一篇文章,是和这篇protobuf衔接的。