美文网首页
项目中IM项目自定义协议解析说明

项目中IM项目自定义协议解析说明

作者: Fizzzzer | 来源:发表于2020-05-26 22:08 被阅读0次

IM自定义协议解析说明

一、IM协议简介

在IM的通信中,采用的是自定义的协议 + PB协议构成,所有的负载都是采用PB格式进行序列化和反序列化构成的

1.1协议的构成:

StartMagic + FixedHeader + AccountInfo + Payload

1.1.1、startMagic:魔数 0x22

魔数标记着协议的开始,或者说当有一条通信过来后,当检测到第一个字节是0x22后,我们认为这是我们需要的一条通信,魔数的大小占一个字节(1byte)

1.1.2、FixedHeader:固定头部

固定头部由一个命令字和可变的包体长度构成

  • 命名字(cmd):请求的命令字,占一个字节(1byte),包含256个命令字
  • 包体长度(remain_len) 包含AccountInfo 和 Payload的包体长度,占2-6个字节(2-6byte)
1.1.3、AccountInfo:账号信息

账号信息包含三个字段业务ID终端的设备类型账号名称

  • 业务ID(AppID):用户接入的业务ID,用于区分是哪一个应用,占28位(28bit)
  • 终端的设备类型(DeviceType):用于区分设备的终端类型,占4位(4bit)
  • 账号名称(Username):用以唯一区分一个用户,统一使用UTF编码,传输的时候以0结尾
1.1.4、Payload:负载信息

协议通信的具体内容都是采用Protobuf来进行序列化和反序列化的

二、IM协议解析

通讯时序图.jpg

ReadThread是一个一直在后台运行的线程,会不断的读取服务端下发下来的数据流,首次会默认读一个字节,就是魔数,如果魔数是我们定义的,就认为是我们自己的协议,在ReadController里面进行解析,ReadController这次解析完成后,会返回下次需要解析的字节大小给ReadThread,然后ReadThread在读相应的字节数组继续给ReadController进行解析,如此往复,直到整个数据流解析完成

2.1 解析魔数

魔数占一个字节,所以我们ReadThread在第一次收到数据流的时候,都是默认读一个字节,这里只需要将读到的魔数与我们规定的魔数(0x22)作比较,如果想等,代表是我们的自定义协议,下一步读取命令字,返回需要读取的一个字节给ReadThread

2.2 解析命令字

命令字(cmd)占一个字节,所以直接读取这一个字节的内容,下一步读取包体长度,返回需要读取的一个字节给ReadThread

2.3 解析包体长度

包体长度(payload_len)最长占4个字节,所以这里一个字节一个字节的读,如果这个字节的最高位为1,说明包体长度数据还没有读完,则继续读取下一个字节,如果最高位为0,说明包体长度的数据读完了。读完包体长度后读取APPID和DeviceType,返回需要读取的4个字节给ReadThread

示例代码:

//取一个字节中的低7位,并加上之前读取到的数
mPayLoadLen = (rec[0] & 0x7F) + (mPayLoadLen << 7);
//取最高位  最高位为0 代表payload的长度读完,最高位为1,说明后面还有数值,还需要继续读
if ((rec[0] & 0x80) == 0) {
     if (mPayLoadLen > PAYLOAD_MAX_LENGTH) {
         DebugLog.e(TAG, "recv:长度错误:mPayLoadLen: " + mPayLoadLen);
         clear();
         return 1;
      }
     mCurrent = ReadStep.READ_APPID;
     DebugLog.d(TAG, "recv:mPayLoadLen: " + mPayLoadLen);
     return APPID_LENGTH;
 }

2.4 解析Appid和DeviceType

appid(28bit)和DeviceType(4bit)一共占四个字节

所以DeviceType是最好获取的,直接用第四个字节的的数据取最低四位就行了

示例代码:

//deviceType只占4位,所以只取最后一个字节的低四位
mDeviceType = rec[3] & 0xF;

读取Appid的时候,要注意一下,因为Appid占28位,所以在取第四个字节的时候,我们取的是高四位

代码示例:

/**
* 读取appid,Appid占28位,所以需要取第四个字节的最高四位
*/
mAppId = ((rec[0] << 24) + (rec[1] << 16) + (rec[2] << 8) + (rec[3] & 0xF0)) >> 4;

2.5读取Username

userName有一个结尾标志,所以每次读的时候,只能一个字节一个字节的读,当读到0的时候,就代表username读完了,没读完的话就继续读,并将这次读取的数据存在buf中数组中

另外需要注意的是,有些通信指令是没有payload信息的,比如登出,心跳等,所以如果在读完userName后,读取到的payload长度为0 了,就说明读完了,这时候就开始分发消息,如果payload长度不为0,就再次接着读

2.6读取paylaod信息

读取payload的时候,一次性最多读1024个字节,如果payload长度小于1024,就读取剩下的长度,把读取到的数据缓存在buf数组中

三、协议的封装

协议的封装主要就是按照上面的协议顺序,依次需要的数据添加到字节数组中

这一块就比较的简单,具体的代码参见ProtoedPayload

相关文章

网友评论

      本文标题:项目中IM项目自定义协议解析说明

      本文链接:https://www.haomeiwen.com/subject/xuybahtx.html