ActionContract - ScutGame/Scut GitHub Wiki

此章节介绍如何定义客户端与服务端通讯协议

客户端与服务端之间的数据传输,需要定义一定的数据结构,Scut提供了两种方案。

Scut的通讯协议

Scut提供一套二进制的通讯协议,拥有传输小,可扩展,跨平台,向下兼容等特点。

请求结构

它与Http Get的参数格式相同(如:Key1=Val1&Key2=Val2)。

消息结构

Get Params | [双换行符 | 扩展数据流]

请求的流分两部分:

  • 第一部分是字符串的格式流(命名为Get Params)
  1. 参数之间是无顺序的;

  2. 必需参数:MsgId、ActionId、Sid、Uid、St和sign;在协议平台工具中不需要再定义

  • MsgId:Int类型,表示请求的消息编号,每次请求时此编号由客户端发送时自增,客户端接收响应时根据此Id识别所属哪个请求的响应消息流;
  • ActionId:Int类型,表示请求需要指定哪个Action处理请求,特定值:1为心跳包请求编号,2为连接中断请求编号,1000以下编号提供给Scut引擎预留使用;
  • Sid:String类型,表示请求的Session会话编号,可以为空,在客户端首次请求时会创建一个唯一的Session编号(Guid类型),客户端登录身份认证通过后服务端返回给客户端,之后的请求需要客户端将此编号通过Sid参数上传给服务端,若Sid参数为空需要重新登录身份认证;
  • Uid:Int类型,表示登录身份认证的用户ID,未认证时为0,身份认证后由服务端返回给客户端,之后的请求需要同sid参数一样必需提供值,否则需要重新登录身份认证;
  • St: String类型,表示时间缀字串,防止恶意的请求,不启用时为默认值"st";
  • sign:String类型,表示MD5的签名字串,防止恶意的请求,以请求的所有参数字串+密钥串(32位)进行MD5较验,较验不通过的请求则不处理,密钥串客户端与服务端保持相同配置;
  1. 所有参数串需要通过UrlEncode进行编码,在放入d参数中提交请求,格式如下:

Get Params: “?d=UrlEncode({Paramstr})”

{Paramstr}变量:“MsgId=1&ActionId=1005&Sid=&Uid=9999&St=st&sign=xxxxxxxx”

  • 第二部分是扩展数据流,可选

支持上传些文件流或图片流等,与第一部分之间需要以两个换行(\r\n\r\n)符分隔,协议工具平台不能定义;

客户端合并字节GetBytes("?d=MsgId=1&ActionId=1005&Sid=&Uid=9999&St=st&sign=xxxxxxxx") + GetBytes("\r\n\r\n") + GetBytes(图片的)的方式提交请求数据,服务器端通过HttpGet.InputStreamBytes属性可以获得客户端上传的图片流数据。

客户端调用示例: ```csharp

public void DoHttpUpLoad()
{
    var paramBytes = Encoding.ASCII.GetBytes("?d=MsgId%3D0%26Sid%3D%26Uid%3D0%26ActionID%3D404%26St%3D%26errorinfo%3D%26sign%3Db71755ec1556fb951ad507e59bd98873\r\n\r\n");
    var imgBytes = new byte[] { 123, 132, 132, 132, 12, 3 };
    var data = new byte[paramBytes.Length + imgBytes.Length];
    Buffer.BlockCopy(paramBytes, 0, data, 0, paramBytes.Length);
    Buffer.BlockCopy(imgBytes, 0, data, paramBytes.Length, imgBytes.Length);

    var response = HttpUtils.Post("http://192.168.1.100:9001/Service.aspx", data, null, null, null, null);
    var reader = new BufferReader(response.GetResponseStream());
    Trace.WriteLine("Response len:" + reader.Data.Length);

}
```

协议定义示例:排行榜录入接口

/*不用要求顺序*/
UserName | String
Score    | Int

开发者注意:

响应结构

Http和Socket都使用相同的二进制流返回给客户端,支持byte、bool、short、int、long、ushort、uint、ulong、float、double、date、string等基础数据类型;WebSocket以Json格式字串返回。

通过将基础类型转换为Byte[]有序拼接起来,除String类型外其它的类型转成Byte的位数都是固定的(如:Bool占1位,Short占2位,Int占4位,Long占8位等),String类型加4位Int表示内容长度;客户端通过协议工具定义的接口读流按位解析。示例:

Int: 1000
结果: [232,3,0,0]

消息结构:(注:Fields和Record之间也是有序的)

Head | Fields[Field1,Field2,...] | Record[Record1,Record2,...]
  • Head:表示固定的响应头,Fields包括如下:
   ErrorCode | MsgId | ErrorInfo | ActionId | St
   
   /*
   ErrorCode:表示错误代码,0为成功
   ErrorInfo:表示错误描述,可以为空
   MsgId:请求的MsgId编号,如果是服务器主动Send时为0
   */

  • Fields: 基础数据类型数据字段集合,是有序的,保证协议兼容,新增的Field放在结尾;

  • Record: 记录行结构,可以并行多个,子结构可以嵌套,以Record和End标记Field列的区间范围,

Record结构

RecordCount | [RecordLen1 | [Field1,Field2,...], RecordLen2 | [Field1,Field2,...]]
  • RecordCount:表示记录的行数,Int类型;
  • RecordLen:表示单行记录的数据字节长度;
  • Field1: 表示有序的Field字段,最小单元元素;

协议定义示例:排行榜列表接口

/*要求顺序,增加要放到结尾,以下是响应参数部分*/
Int      | PageCount | 分页总页数   
-------------------
Record   |           | Record结构,标记Field列范围开始位置  
  String | UserName  |
  Int    | Score     |
End      |           | Record结构,标记Filed列范围结束位置
-------------------

多个Record并行结构

Int      | Field1
Int      | Field2
-------------------
Record   | Record1
  String | item1
  Int    | item2
End      
-------------------
Record   | Record2
  String | item3
  Int    | item4
End      
-------------------
Int      | Field3
Int      | Field4

多个Record嵌套结构

Int         | Field1
Int         | Field2
-------------------
Record      | Record1
  String    | item1
  Int       | item2  
  -----------------
  Record    | Record2
     String | item3
     Int    | item4
  End      
  -----------------
End      
-------------------
Int         | Field3
Int         | Field4

消息包结构

请求结构和响应结构的流都需要增加消息内容长度(Int占4位),在粘包拆包处理时读取此长度,再读取后续的长度流,拆分成多个完整消息包。

Http和Socket协议有些差别,如下:

  • Http格式
    消息内容长度 | 消息内容
  • Socket格式
    消息内容长度 | 消息内容长度 | 消息内容
  • Socket流带Gzip压缩:Gzip压缩判断头4位是否是(16进制):[1F 8B 08 00]
    消息内容长度 | Gzip压缩(消息内容长度 | 消息内容)

自定义协议结构

参考如何定制客户端与服务端的通讯协议