Skip to content
目录

悟空 IM 协议

控制报文结构

参数名类型说明
Fixed header2 byte固定报头
Variable headerbytes可变报头
Payloadbytes消息体

固定报头

每个 悟空 IM 控制报文都包含一个固定报头

Bit76543210
byte 1悟空IM控制报文的类型用于指定控制报文类型的标志位
byte 2...剩余长度

悟空 IM 控制报文的类型

名字描述
Reserved0保留位
CONNECT1客户端请求连接到服务器(c2s)
CONNACK2服务端收到连接请求后确认的报文(s2c)
SEND3发送消息(c2s)
SENDACK4收到消息确认的报文(s2c)
RECV5收取消息(s2c)
RECVACK6收取消息确认(c2s)
PING7ping请求
PONG8对ping请求的相应
DISCONNECT9请求断开连接

用于指定控制报文类型的标志位

Send和Recv协议中的标志位

bit3210
byteDUPSyncOnceRedDotNoPersist

Connack协议中的标志位

bit3210
byteReservedReservedReservedHasServerVersion

备注:

DUP: 是否是重复的消息(客户端重发消息的时候需要将DUP标记为1)
 SyncOnce: 只同步一次 在多端设备的情况下 如果有一个设备拉取过此消息,其他设备将不会再拉取到此消息(比如加好友消息)
 RedDot: 客户端收到消息是否显示红点
 NoPersist: 是否不存储此消息
 Reserved:保留位
 HasServerVersion:是否有服务端版本号
DUP: 是否是重复的消息(客户端重发消息的时候需要将DUP标记为1)
 SyncOnce: 只同步一次 在多端设备的情况下 如果有一个设备拉取过此消息,其他设备将不会再拉取到此消息(比如加好友消息)
 RedDot: 客户端收到消息是否显示红点
 NoPersist: 是否不存储此消息
 Reserved:保留位
 HasServerVersion:是否有服务端版本号

剩余长度

在当前消息中剩余的 byte(字节)数,包含可变头部和负荷(内容)。

单个字节最大值:01111111,16 进制:0x7F,10 进制为 127。

悟空 IM 协议规定,第八位(最高位)若为 1,则表示还有后续字节存在。

悟空 IM 协议最多允许 4 个字节表示剩余长度。最大长度为:0xFF,0xFF,0xFF,0x7F,二进制表示为:11111111,11111111,11111111,01111111,十进制:268435455 byte=261120KB=256MB=0.25GB 四个字节之间值的范围:

DigitsFromTo
10 (0x00)127 (0x7F)
2128 (0x80, 0x01)16 383 (0xFF, 0x7F)
316 384 (0x80, 0x80, 0x01)2 097 151 (0xFF, 0xFF, 0x7F)
42 097 152 (0x80, 0x80, 0x80, 0x01)268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

其实换个方式理解:第 1 字节的基数是 1,而第 2 字节的基数:128,以此类推,第三字节的基数是:128128=2 的 14 次方,第四字节是:128128*128=2 的 21 次方;

例如,需要表达 321=2*128+65.(2 字节):10100001 0000 0011.

(和我们理解的低位运算放置顺序不一样,第一个字节是低位,后续字节是高位,但字节内部本身是低位右边,高位左边)。

字符串 UTF-8 编码

有关字符串,悟空 IM 采用的是修改版的 UTF-8 编码,一般形式为如下:

bit76543210
byte 1String Length MSB
byte 2String Length MSB
bytes 3...Encoded Character Data

可变报头

某些控制报文包含一个可变报头部分。它在固定报头和有效载荷之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。


CONNECT 连接报文

参数名类型说明
Packet Type0.5 byte报文类型(1)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
Protocol Versionint8协议版本号
Device Flagint8设备标示(同标示同账号互踢)
Device IDstring设备唯一ID
UIDstring用户ID
Tokenstring用户的token
Client Timestampint64客户端当前时间戳(13位时间戳,到毫秒)
Client Keystring客户端KEY (客户端KEY (base64编码的DH公钥))

CONNACK 连接确认

CONNACK 报文由服务端所发送,作为对来自客户端的 CONNECT 报文的响应,如果客户端在合理的时间内没有收到服务端的 CONNACK 报文,客户端应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。

参数名类型说明
Packet Type0.5 byte报文类型(2)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
ServerVersionuint8服务器支持的最大版本 flag包含HasServerVersion的时候有效
Time Diffint64客户端时间与服务器的差值,单位毫秒。
Reason Codeuint8连接原因码(见附件)
Server Keystring服务端base64的DH公钥
Saltstring安全码

SEND 发送消息

参数名类型说明
Packet Type0.5 byte报文类型(3)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
Setting1 byte消息设置
Client Sequint32客户端消息序列号(由客户端生成,每个客户端唯一)
Client Msg Nostring客户端唯一标示,用于客户端消息去重
StreamNostring流式消息编号(setting里需要开启Stream)
Channel Idstring频道ID(如果是个人频道ChannelId为个人的UID)
Channel Typeint8频道类型(1.个人 2.群组)
Expireuint32消息过期时间(单位秒) version>=3
Msg Keystring用于验证此消息是否合法(仿中间人篡改)
Topicstring话题ID(只有setting开启了topic才有此字段)
Payload... byte消息内容

SENDACK 发送消息确认

参数名类型说明
Packet Type0.5 byte报文类型(4)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
Message IDuint64服务端的消息ID(全局唯一)
Client Sequint32客户端消息序列号
Message Sequint32消息序号(有序递增,用户唯一)
Reason Codeuint8发送原因代码 1表示成功

SUB 订阅消息(研发中)

参数名类型说明
Packet Type0.5 byte报文类型(5)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
Setting1 byte频道设置
SubNostring订阅编号
Channel IDstring频道ID
Channel Typeint8频道类型
Actionint80.订阅 1.取消
Paramstring订阅参数

SUBACK 订阅消息回执(研发中)

参数名类型说明
Packet Type0.5 byte报文类型(5)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
SubNostring订阅编号
Channel IDstring频道ID
Channel Typeint8频道类型
Actionint80.订阅 1.取消
Reason Codeuint8订阅状态 1表示成功,其他代码见文档

RECV 收消息

参数名类型说明
Packet Type0.5 byte报文类型(5)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
Setting1 byte消息设置(见下 版本4有效)
Msg Keystring用于验证此消息是否合法(仿中间人篡改)
From UIDstring发送者UID
Channel IDstring频道ID
Channel Typeint8频道类型
Expireuint32消息过期时间(单位秒) version>=3
Client Msg Nostring客户端唯一标示,用于客户端消息去重
StreamNostring流式消息编号,根据setting是否开启stream判断是否有此字段
StreamSequint32流序号,根据setting是否开启stream判断是否有此字段
StreamFlaguint8流标记(0.开始 1.进行中 2. 结束),根据setting是否开启stream判断是否有此字段
Message IDuint64服务端的消息ID(全局唯一)
Message Sequint32服务端的消息序列号(有序递增,用户唯一)
Message Timestampint32服务器消息时间戳(10位,到秒)
Topicstring话题ID(只有setting开启了topic才有此字段)
Payload... byte消息内容

RECVACK 收消息确认

参数名类型说明
Packet Type0.5 byte报文类型(6)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
Message IDuint64服务端的消息ID(全局唯一)
Message Sequint32序列号

PING

参数名类型说明
Packet Type0.5 byte报文类型(7)
Flag0.5 byte 标示位

PONG

参数名类型说明
Packet Type0.5 byte报文类型(8)
Flag0.5 byte 标示位

DISCONNECT

参数名类型说明
Packet Type0.5 byte报文类型(9)
Flag0.5 byte 标示位
Remaining Length... byte报文剩余长度
ReasonCodeuint8原因代码
Reasonstring原因

消息设置

bit76543210
byteReceiptReservedSignalNoEncryptTopicStreamReservedReserved

消息设置目前大小为 1byte 8 个 bit

Receipt: 消息已读回执,此标记表示,此消息需要已读回执

NoEncrypt: 消息是否不开启加密

Signal: 消息是否开启了 signal 端对端加密

Topic:消息是否包含 topic(如果为 1 则发送包和接受包都将包含 topic 字段)

Stream: 流式消息

Reserved:保留位,暂未用到

Payload 推荐结构

普通消息

文本

{
    "type": 1,
    "content": "这是一条文本消息"
}
{
    "type": 1,
    "content": "这是一条文本消息"
}

文本(带@)

{
    "type": 1,
    "content": "这是一条文本消息",
    "mention":{
        "all": 0, // 是否@所有人  0. @用户 1. @所有
        "uids":["1223","2323"] // 如果all=1 此字段为空
    }
}
{
    "type": 1,
    "content": "这是一条文本消息",
    "mention":{
        "all": 0, // 是否@所有人  0. @用户 1. @所有
        "uids":["1223","2323"] // 如果all=1 此字段为空
    }
}

文本(带回复)

{
    "type": 1,
    "content": "回复了某某" ,
    "reply": {
        "root_mid": "xxx",  // 根消息的message_id
        "message_id": "xxxx", // 被回复的消息ID
        "message_seq": xxx, // 被回复的消息seq
        "from_uid": "xxxx", // 被回复消息的发送者
        "from_name": "xxx", // 被回复消息的发送者名称
        "payload": {}     //   被回复消息的payload
    }
}
{
    "type": 1,
    "content": "回复了某某" ,
    "reply": {
        "root_mid": "xxx",  // 根消息的message_id
        "message_id": "xxxx", // 被回复的消息ID
        "message_seq": xxx, // 被回复的消息seq
        "from_uid": "xxxx", // 被回复消息的发送者
        "from_name": "xxx", // 被回复消息的发送者名称
        "payload": {}     //   被回复消息的payload
    }
}

图片

{
    "type": 2,
     "url": "http://xxxxx.com/xxx", // 图片下载地址
     "width": 200, // 图片宽度
     "height": 320 // 图片高度
 }
{
    "type": 2,
     "url": "http://xxxxx.com/xxx", // 图片下载地址
     "width": 200, // 图片宽度
     "height": 320 // 图片高度
 }

GIF

{
    "type": 3,
     "url": "http://xxxxx.com/xxx", // gif下载地址
     "width": 72, // gif宽度
     "height": 72  // gif高度
 }
{
    "type": 3,
     "url": "http://xxxxx.com/xxx", // gif下载地址
     "width": 72, // gif宽度
     "height": 72  // gif高度
 }

语音

{
    "type": 4,
     "url": "http://xxxxx.com/xxx", // 语音下载地址
     "timeTrad": 10 // 语音秒长
}
{
    "type": 4,
     "url": "http://xxxxx.com/xxx", // 语音下载地址
     "timeTrad": 10 // 语音秒长
}

文件

{
    "type": 8,
     "url": "http://xxxxx.com/xxx", // 文件下载地址
     "name":"xxxx.docx", // 文件名称
     "size": 238734 // 大小 单位byte
}
{
    "type": 8,
     "url": "http://xxxxx.com/xxx", // 文件下载地址
     "name":"xxxx.docx", // 文件名称
     "size": 238734 // 大小 单位byte
}

命令消息

{
    "type": 99,
     "cmd": "groupUpdate", // 命令指令标示
     "param": {} // 命令对应的数据
}
{
    "type": 99,
     "cmd": "groupUpdate", // 命令指令标示
     "param": {} // 命令对应的数据
}

系统消息

系统消息的 type 必须大于 1000

创建群聊 (NoPersist:0,RedDot:0,SyncOnce:1)

张三邀请李四、王五加入群聊

{
    "type": 1001,
    "creator": "xxx",  // 创建者uid
    "creator_name": "张三", // 创建者名称
    "content": "{0}邀请{1}、{2}加入群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"},{"uid":"xx02","name":"王五"}]
}
{
    "type": 1001,
    "creator": "xxx",  // 创建者uid
    "creator_name": "张三", // 创建者名称
    "content": "{0}邀请{1}、{2}加入群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"},{"uid":"xx02","name":"王五"}]
}

添加群成员 (NoPersist:0,RedDot:0,SyncOnce:1)

张三邀请李四、王五加入群聊

{
    "type": 1002,
    "content": "{0}邀请{1}、{2}加入群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"},{"uid":"xx02","name":"王五"}]
}
{
    "type": 1002,
    "content": "{0}邀请{1}、{2}加入群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"},{"uid":"xx02","name":"王五"}]
}

移除群成员 (NoPersist:0,RedDot:0,SyncOnce:1)

张三将李四移除群聊

{
    "type": 1003,
    "content": "{0}将{1}移除群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"}]
}
{
    "type": 1003,
    "content": "{0}将{1}移除群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"}]
}

群成员被踢 (NoPersist:0,RedDot:1,SyncOnce:0)

{
    "type": 1010,
    "content": "你被{0}移除群聊",
    "extra": [{"uid":"xxx","name":"张三"}]
}
{
    "type": 1010,
    "content": "你被{0}移除群聊",
    "extra": [{"uid":"xxx","name":"张三"}]
}

张三将李四移除群聊

{
    "type": 1003,
    "content": "{0}将{1}移除群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"}]
}
{
    "type": 1003,
    "content": "{0}将{1}移除群聊",
    "extra": [{"uid":"xxx","name":"张三"},{"uid":"xx01","name":"李四"}]
}

更新群名称 (NoPersist:0,RedDot:0,SyncOnce:1)

张三修改群名称为"测试群"

{
    "type": 1005,
    "content": "{0}修改群名为\"测试群\"",
    "extra": [{"uid":"xxx","name":"张三"}]
}
{
    "type": 1005,
    "content": "{0}修改群名为\"测试群\"",
    "extra": [{"uid":"xxx","name":"张三"}]
}

更新群公告 (NoPersist:0,RedDot:0,SyncOnce:1)

张三修改群公告为"这是一个群公告"

{
    "type": 1005,
    "content": "{0}修改群公告为\"这是一个群公告\"",
    "extra": [{"uid":"xxx","name":"张三"}]
}
{
    "type": 1005,
    "content": "{0}修改群公告为\"这是一个群公告\"",
    "extra": [{"uid":"xxx","name":"张三"}]
}

撤回消息 (NoPersist:0,RedDot:0,SyncOnce:1)

张三撤回了一条消息

{
    "type": 1006,
    "message_id": "234343435",  // 需要撤回的消息ID
    "content": "{0}撤回了一条消息",
    "extra": [{"uid":"xxx","name":"张三"}]
}
{
    "type": 1006,
    "message_id": "234343435",  // 需要撤回的消息ID
    "content": "{0}撤回了一条消息",
    "extra": [{"uid":"xxx","name":"张三"}]
}

命令类消息

命令类消息 (SyncOnce:1)

{
    "type": 99,
    "cmd": "cmd",  // 命令标示
    "param": {} // 命令参数
}
{
    "type": 99,
    "cmd": "cmd",  // 命令标示
    "param": {} // 命令参数
}

群成员信息有更新(收到此消息客户端应该增量同步群成员信息)

{
    "type": 99,
    "cmd": "memberUpdate",
    "param": {
        "group_no": "xxxx"
    }
}
{
    "type": 99,
    "cmd": "memberUpdate",
    "param": {
        "group_no": "xxxx"
    }
}

红点消除(收到此命令客户端应将对应的会话信息的红点消除)

{
    "type": 99,
    "cmd": "unreadClear",
    "param": {
        "channel_id": "xxxx",
        "channel_type": 2
    }
}
{
    "type": 99,
    "cmd": "unreadClear",
    "param": {
        "channel_id": "xxxx",
        "channel_type": 2
    }
}

本文档内容版权属于 上海信必达网络科技有限公司,保留所有权利