CustomAction - ScutGame/Scut GitHub Wiki
此章节介绍如何定制客户端与服务端的通讯协议
定义通讯结构
如果使用Scut提供的协议不能满足你的开发需求时, Scut还提供了扩展自定义的协议结构,如现在常用Google的Protobuf序列化的二进制流或者Json。
约束条件
当你自定通讯消息结构时,请求的消息中需要能够获得以下必须参数,Scut的自定义协议需要依赖以下几个参数处理;
参数如下:
- MsgId:数据包的自增序号
- SessionId:客户端与服务器通讯的会话编号
- ActionId:服务器用来分发请求具体处理的Action类
- UserId:用户ID标识,6.7.9.7以后版本是【可选】参数,通过sessionId可以获得
传输安全性
根据需求可以对整个消息包进行加密传输,也可只对消息包(或消息头部分)加上自定Key
进行MD5加密,作为其中一个Sign参数上传给服务器,服务器以相同的方式对消息包进行MD5加密来比对Sign的值是否一致。
可以参考Ranking Sample中的示例。
排行榜示例
消息结构
分为两段Byte数组,第一段Byte数组表示Protobuf序列化后的消息头,第二段表示Protobuf序列化后的消息主体,如下:
消息头长度(Int) + 消息头Head(Byte[]) + 消息主体Body(Byte[])
消息头结构
在收到客户端上传的数据后,消息头需要进行解包再分发到Action层处理,因此需要实现IActionDispatcher接口来扩展处理自定的解包算法逻辑,在里建立CustomActionDispatcher类。
定义消息头MessagePack类,代码如下:
[ProtoContract]
public class MessagePack
{
[ProtoMember(1)]
public int MsgId { get; set; }
[ProtoMember(2)]
public int ActionId { get; set; }
[ProtoMember(3)]
public string SessionId { get; set; }
[ProtoMember(4)]
public int UserId { get; set; }
//以下扩展新属性
}
读取头部流的方法,代码如下:(ProtoBufUtils是Scut提供的Protobuf序列化工具类)
private MessagePack ReadMessageHead(byte[] data, out byte[] content)
{
MessagePack headPack = null;
content = new byte[0];
try
{
//解头部(解之前当然还需要对byte[]解密,这里跳过这步)
int pos = 0;
byte[] headLenBytes = new byte[4];
Buffer.BlockCopy(data, pos, headLenBytes, 0, headLenBytes.Length);
pos += headLenBytes.Length;
int headSize = BitConverter.ToInt32(headLenBytes, 0);
if (headSize < data.Length)
{
byte[] headBytes = new byte[headSize];
Buffer.BlockCopy(data, pos, headBytes, 0, headBytes.Length);
pos += headBytes.Length;
headPack = ProtoBufUtils.Deserialize<MessagePack>(headBytes);
//解消息的内容
if (data.Length > pos)
{
int len = data.Length - pos;
content = new byte[len];
Buffer.BlockCopy(data, pos, content, 0, content.Length);
//内容数据放到具体Action业务上处理
}
}
else
{
//不支持的数据格式
}
}
catch (Exception ex)
{
//不支持的数据格式
}
return headPack;
}
消息体结构
消息体会根据Action处理需要的数据结构不同,因此是放在具体的Action内部再解包。
如在Action1001类的GetUrlElement方法解消息体包,代码如下:
public override bool GetUrlElement()
{
byte[] data = (byte[])actionGetter.GetMessage();
if (data.Length > 0)
{
requestPack = ProtoBufUtils.Deserialize<Request1001Pack>(data);
return true;
}
return false;
}
Request1001Pack消息体代码如下:
[ProtoContract]
public class Request1001Pack
{
[ProtoMember(101)]
public int PageIndex { get; set; }
[ProtoMember(102)]
public int PageSize { get; set; }
}
使用自定解包类
上面我们已经实现好了自己的CustomActionDispatcher解包类,那怎么让它生效呢?
在MainClass类中配置GameEnvironment.Setting.ActionDispatcher属性即可,代码如下:
public class MainClass : GameHttpHost
{
public MainClass()
{
GameEnvironment.Setting.ActionDispatcher = new CustomActionDispatcher();
}
}
- (注:由于消息头与消息体结构需要提供给客户端(Untity3d)使用,因此我们将Request1001Pack类和MessagePack消息头独立到另一个类库项目)