一种可高度扩展的IM通讯协议

1、前言

      说到即时通讯,那就不能了解下IM通讯协议,它是即时通讯的核心与灵魂,一个好的通讯协议可以减少软件代码的复杂度以及维护难度,增加软件的可扩展性与可读性,反之会让实现代码结构变得臃肿不堪,甚至难以阅读。

2.常用IM通信协议及优缺点

  1. XMPP协议:
    优点:基于xml协议,容易理解,使用广泛,易于扩展。
    缺点:流量大,在移动终端也耗电。交互过程复杂。多被pc时代的产品使用,不适合移动时代的IM产品,即使我们基于xmpp进行改进,简化握手过程,改进文件传输机制,但是它的基因决定了如何改进,他都不适合移动互联网时代的IM产品
  2. MQTT协议:
    优点:轻量级的、基于代理的“发布/订阅”模式的消息传输协议。适合推送服务
    缺点:协议简单,但是需要自己扩展好友,群组等功能。
  3. Protobuf协议:
    优点:非常小、非常快、非常简单,一条消息数据用Protobuf序列化后的大小是JSON的1/10、XML格式的1/20、是二进制序列化的1/10。
    缺点:不能表示复杂的数据结构
    需要和其它系统做消息交换的,对消息大小很敏感的。那么protobuf适合了,它语言无关,消息空间相对xml和json等节省很多。
    小数据的场合。如果你是大数据,用它并不适合。

以上协议中Protobuf算是比较不错的一个选择方案,但是对于复杂一些的数据结构定义有些不足。

3.自定义的一种通用通讯协议

      通过以上协议的对比,我定义了一种json文本+二进制文件的通讯协议,之所以采用json文本是因为json在常见平台都有实现方案,算是一种跨平台的解析方式吧,并且其数据结构清晰,能够描述复杂数据,易于扩展,比xml要小,虽然比Protobuf要大,但是不用像Protobuf那样定义 .proto 描述文件,可以将对象直接转成json文本,二进制文件指json对象中的指向文件(如图片、文档、音频、视频等)。该协议已在我自己的软件中运用,使用起来还是挺方便的;以下是该协议的一些特点:
      1、基于对象的通讯协议(即发送数据的最小单元为一个对象)
      2、基于json的通讯协议,可以将json直接解析成对应的实体类
      3、基于文本+二进制文件的通讯协议,将文本与文件一起传输

4.协议结构

废话不多说,下面贴出该协议的结构图:
一种可高度扩展的IM通讯协议
结构说明

start区
    1.头部码:
        1byte,用来描述协议解析方式,例如心跳包与正常对象,如果是心跳包,他的结构只有start区的头部码,即一个心跳包只有1byte大小,其他按上图完整结构解析
    2.总长度
        8byte,文本区和文件区的总长度。在协议处理中并没有实际用到,可以去掉
    3文本长度
        4byte,文本区的总长度,协议中根据文本长度截取文本区并将其转为string文本对象
文本区
    1.HEAD
        用描述Json对象(主体对象)的json来对象(自定义的header类),该对象中至少应包含json对象(主体对象)类名的字段,并将主体对象解析成对应实体类
    2.\n
        将HEAD与json对象(主体对象)区分开,应保证\n是文本区第一个出现的换行符
    3.Json对象
        即解析的主体对象,json文本格式
文件区
      描述:如果json对象中包含文件(如包含本地文件路径),则按照文件在json文本对象中出现的顺序依次将本地文件转换成二进制数据拼接到文本区后面进行网络传输。

    1.是否有文件
        1byte,如果json对象中包含本地文件, 则值为1,即有文件,后面需拼接该本地文件的长度以及本地文件数据;如果没有,值为0,后面不再拼接如何数据,并表示主体对象已解析完毕
    2.文件长度
        8byte,表示对应文件的大小长度,通过文件长度将相应的文件数据写入到接收数据的设备端
    3.文件
        对应文件的二进制数据。传输时应该检测本地文件是否存在以及不存在的处理方式
end
      属于文件区,即是否有文件的值为0,表示该对象已解析完毕

      为了更方便的描述实体json对象与本地文件的对应关系,这里引入一个概念StringFile,即文本描述文件,该对象应该具备以下特征:
          1.需要有一个storageType字段描述该对象的存储方式:本地存储或者是网络存储,如果是本地存储,在传输该对象时应该把本地文件数据也进行传输;如果是网络存储方式(即http或https存储方式),则传输该对象json文本即可
          2.需要有一个path字段描述文件的存储路径
          3.path值在网络传输时可以标记一些特殊字符方便在接收设备端接收到该文件时修正其对应的文件路径

5.解析流程

如果你将一个传输对象按以上结构拼接完成,那接收端的流程应该是:
      1.读取1Byte数据,如果该数据值为0(代表心跳包),则回到步骤1,其他值则进入步骤2
      2.读取12byte数据,并截取后四位(文本区长度)获取文本区长度textLength
      3.读取textLength长度数据,并将其转换成文本对象(编码格式UTF-8首选),根据换行符将HEAD与主体json对象文本分割开来
      4.读取1byte数据,判断后面是否拼接了文件数据,如果没有(值为0),将主体json对象转换为实体类(HEADE中应包含主体json对象的类名描述字段),然后一个传输对象解析完毕,并回到步骤1;如果存在(值为1),则继续步骤5
      5.读取8byte数据,获取文文件长度fileLength
      6.读取fileLength数据,将其写入到指定的存储路径,并将新的路径赋值到对应的StringFile的path字段中
      7.回到步骤4

以上是该协议的全部内容,本人已用java实现了该协议并运用到了自己的手机应用中, 欢迎大家前去下载体验,过段时间我会整理一下将协议实现的java代码上传给大家参考

下载地址:http://47.93.31.206:8080/detail.html