ESFramework 使用技巧 -- 协议格式自动生成器(跨平台开发小工具)
一.跨平台开发需要通信消息的协议格式统一
有些客户希望将已有的基于ESFramework开发的系统,如 OrayTalk 或 GGTalk(可在广域网部署的QQ高仿版)扩展到android和iOS平台,除了在这些客户端平台使用ESFramework对应版本的开发包之外,最重要的一点就是:通信消息的格式必须达成一致。所以,必须先搞清楚OrayTalk现有的协议格式,然后让iOS或android开发遵循现有的协议格式就OK了。
二.紧凑的序列化器的协议格式
OrayTalk 内部的消息几乎都是使用ESPlus提供的紧凑的二进制序列化器(CompactPropertySerializer)来进行序列化和反序列化的,CompactPropertySerializer是一个紧凑的二进制序列化器(是基于字节流的,而非基于文本的)。CompactPropertySerializer 的基本动作是这样的:
(1)将协议类的所有可读写的Property列出来,并按名称字母顺序升序排序。
(2)针对排序后的Property,逐个序列化或反序列化。
CompactPropertySerializer 在序列化和反序列化Property时遵循的是以下策略:
- 字符串一律使用UTF-8编码。
- 如果是基础数据类型,则直接记录其字节 。
- 如果是bool,则用一个字节表示,0表示false,1表示true。
- 如果是string,先记录其长度(int,-1表示为null,0表示string.Empty),再记录UTF8编码的字节。
- 如果是byte[],先记录其长度(int,-1表示为null),再记录其内容。
- 如果是Image ,先记录图片数据(byte[])的长度(int,-1表示为null),再记录是否为Gif(一个byte),再记录序列化内容。
- 如果是Color ,长度固定为3个字节,依次记录R、G、B的值。
- 如果是DateTime,则记录与1970年1月1日的差值,并将其转化为毫秒数(long,8字节)。
- 如果是List<>,先记录元素数(int,-1表示为null),再依次记录每个元素的内容。
- 如果是Dictionary<,>,先记录元素对的个数(int,-1表示为null),再依次记录每个元素的Key,Value。
- 如果是自定义的class和struct,则先记录其序列化的长度(int,-1表示为null),再记录其序列化后的内容。
- 如果是Font,则等价于ESPlus.Serialization.SimpleFont。
- 如果是接口类型,则先记录类型全名称(string,如果为null,则记录接口类型名),再记录其序列化的长度(int,-1表示为null),再记录其序列化后的内容。
我们提供了一个工具(ContractFormatGenerator),可以查看一个协议类(contract类,用于通信的实体类)转换成字节流后的格式。其它的平台只要按照这些格式来构造消息,就可以与OrayTalk或GG的PC版本进行通信了。
ContractFormatGenerator工具运行起来后,如下所示:
点击“加载程序集”的按钮,可以选择协议类所在的dll(需要把该dll以及其所依赖的相关dll都拷贝到ContractFormatGenerator.exe所在的目录,否则“加载程序集”会报错:无法加载请求类型。),这样,下拉列表中就会列出该程序集中所有的协议类(默认只显示以Contract结尾的类,可以在文本框中修改这个后缀),选中某个协议类,点击“输出协议格式”按钮,就会生成该协议类对象序列化后的格式说明。
上面示例中选中的是“修改密码”功能用到的ChangePassword协议类,这个类的定义如下:
public class ChangePasswordContract { public ChangePasswordContract() { } public ChangePasswordContract(string oldPasswordMD5, string newPasswordMD5) { this.OldPasswordMD5 = oldPasswordMD5; this.NewPasswordMD5 = newPasswordMD5; } public string OldPasswordMD5 { get; set; } public string NewPasswordMD5 { get; set; } }
这个类只有两个字段(属性), 而生成的协议格式实际上就是对这两个字段的描述。下面我们简单讲讲协议格式的各个列的含义:
(1)FieldName:字段的名称。字段名称一般与协议类的属性名是对应的,如果某个属性的类型的长度是可变的(比如string),那么就要先加一个Field,来描述这个属性值转换给字节后的长度。
(2)Type:Field的类型。
(3)Length:当前Field的值的长度。
要注意,协议格式中,第一个int是一个长度(*TotalLen),用来记录当前协议类序列化后的总长度(这个int也包含在内) 。
所以,我们在android或iOS按照上面的协议格式来构造“修改密码”的消息(得到一个byte[]),然后,将其发送到GG的服务器,GG的服务器就可以正常的解析并完成处理了。
三.协议格式生成器下载
下面给出可执行ContractFormatGenerator及其源码下载,如果有些特殊需要修改的,可以在源码上修改。(最后更新:2019.01.12)
(1)可执行的 ContractFormatGenerator
-----------------------------------------------------------------------------------------------------------------------------------------------
Q Q:168757008