目录:
- IM 系统 SDK 研读文档 (Web)
IM 系统 SDK 研读文档 (Web)
文档描述了 IM Web SDK 设计时参考产品的解决方案、IM SDK 自身的架构设计和业务流程设计、IM SDK 和后端的交互模式以及通信字段定义、IM SDK 的接口详细设计以及示例代码等。
一、IM 系统的概念
即时通信是一个终端连往即时通信网络的服务。即时通信不同于电子邮件在于它的交谈是即时(实时)的。大部分的即时通信服务提供了状态信息的特性──显示联系人名单,联系人是否在在线与能否与联系人交谈。 – 《维基百科》
一个典型的 IM 系统整体架构:
- 客户端:作为与服务端进行消息收发通信的终端。
- 接入层:也叫网关层,为客户端收发消息提供入口。
- 逻辑层:负责 IM 系统各功能的核心逻辑实现。
- 存储层:负责 IM 系统相关数据的持久化存储,包括消息内容、账号信息、社交关系链等。
- 第三方服务:保证 APP 在未打开或后台运行时也能收到消息通知(这主要是第第 3 方消息推送服务)。
二、设计方案参考
➣ 网易云信
1. 官方文档
2. 整体架构
服务端分为 应用服务器
和 云信服务器
。云信服务器用于客户端的收发消息功能,应用服务器用于好友关系、用户信息等业务数据获取以及登录功能。同时云信服务器和应用服务器之间也会进行接口调用以同步用户数据和状态。
3. 消息通信机制和兼容性处理
网易云信 SDK 兼容到 IE8, IE8/IE9 使用 xhr-polling 来模拟长连接,也就是 http 长轮询, 其它高级浏览器使用 WebSocket 建立长连接。
➣ 腾讯 IM
1. 官方文档
2. 整体架构
腾讯 IM 架构和网易云信类似,服务端分为 应用服务器
和 IM 服务器
。IM 服务器用于客户端的消息管理、用户管理、群组管理、好友关系功能,应用服务器用于用户信息数据获取。同时 IM 服务器和应用服务器之间也会进行接口调用以同步用户数据和状态。
从整体架构上来看,两者不同之处体现在:对于好友关系和用户登录方面的处理,网易云信放在应用服务器,而腾讯 IM 放在 IM 服务器。网易的应用服务器含有通信业务方面的处理,而腾讯的应用服务器更偏向于纯数据存储和读取。
3. 消息通信机制和兼容性处理
腾讯 IM 从 v2.11.2 起,SDK 使用 WebSocket 方案替代之前的 HTTP 长轮询方案作为底层传输技术。不过接口都未更改,应该是默认会降级兼容。
三、IM Web SDK 功能设计
➣ 关于 HTTP 长连接和长轮询的说明
1. HTTP 长连接 / 短连接
在 HTTP/1.0 中默认使用 http 短连接。也就是说,客户端和服务器每进行一次 HTTP 操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源(如 JavaScript 文件、图像文件、CSS 文件等),每遇到这样一个 Web 资源,浏览器就会重新建立一个 HTTP 会话。
而从 HTTP/1.1 起,默认使用长连接,用以保持连接特性,提高传输的效率和减低服务器的开销。使用长连接的 HTTP 协议,会在响应头加入这行代码:
1 | Connection:keep-alive |
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。
HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。
2. HTTP 长轮询
HTTP 长轮询是人为编程实现的一种通信方式,它在短轮询的基础上加入服务端的连接保持、连接超时和数据变化监听等功能。而 HTTP 长连接本质上是 http 协议的特性,主要用于优化客户端的连接重用,减少资源占用,不可人为编程。
➣ 通信机制
推荐使用 WebSocket + HTTP 长轮询兼容的方案。
1. 短轮询 (不使用,仅了解)
在短轮询模式中,服务器接到请求后,如果有新消息就会将新消息返回给客户端,如果没有新消息就返回空列表,并关闭连接。
这种短轮询的方式就好像一位焦急等待重要信件的人,每天骑车跑到家门口的邮局去问是否有自己的信件,有就拿回家,没有第二天接着去邮局问。
作为从一问一答的请求响应模式孵化出来的短轮询模式,具有较低的迁移升级成本,比较容易落地。但劣势也很明显:
- 为了提升实时性,短轮询的频率一般较高,但大部分轮询请求实际上是无用的,客户端既费电也费流量;
- 高频请求对服务端资源的压力也较大,一是大量服务器用于抗高频轮询的 QPS(每秒查询率),二是对后端存储资源也有较大压力。
因此,“短轮询” 这种方式,一般多用在用户规模比较小,且不愿花费太多服务改造成本的小型应用上。
2. 长轮询 (作为兼容方案)
正是由于 “短轮询” 存在着高频无用功的问题,为了避免这个问题,IM 逐步进化出 “长轮询” 的消息获取模式。
长轮询和短轮询相比,一个最大的改进之处在于:短轮询模式下,服务端不管本轮有没有新消息产生,都会马上响应并返回。而长轮询模式当本次请求没有获取到新消息时,并不会马上结束返回,而是会在服务端 “悬挂(hang)”,等待一段时间;如果在等待的这段时间内有新消息产生,就能马上响应返回。
这种方式就像等待收信的人每天跑到邮局去问是否有自己的信件,如果没有,他不是马上回家,而是在邮局待上一天,如果还是没有就先回家,然后第二天再来。
长轮询能大幅降低短轮询模式中客户端高频无用的轮询导致的网络开销和功耗开销,也降低了服务端处理请求的 QPS,相比短轮询模式而言,显得更加先进。
但是长轮询并没有完全解决服务端资源高负载的问题,仍然存在以下问题:
服务端保持(hang)住请求,只是降低了入口请求的 QPS,并没有减少对后端资源轮询的压力。假如有 1000 个请求在等待消息,可能意味着有 1000 个线程在不断轮询消息存储资源。
长轮询在超时时间内没有获取到消息时,会结束返回,因此仍然没有完全解决客户端 “无效” 请求的问题。
长轮询的使用场景多见于: 对实时性要求比较高,但是整体用户量不太大。它在不支持 WebSocket 的浏览器端的场景下还是有比较多的使用。
3. WebSocket (首选协议)
WebSocket 正是一种服务端推送的技术代表。
随着 HTML5 的出现,基于单个 TCP 连接的全双工通信的协议 WebSocket 在 2011 年成为 RFC 标准协议,逐渐代替了短轮询和长轮询的方式,而且由于 WebSocket 协议获得了 Web 原生支持,被广泛应用于 IM 服务中,特别是在 Web 端基本属于 IM 的标配通信协议。
和短轮询、长轮询相比,基于 WebSocket 实现的 IM 服务,客户端和服务端只需要完成一次握手,就可以创建持久的长连接,并进行随时的双向数据传输。当服务端接收到新消息时,可以通过建立的 WebSocket 连接,直接进行推送,真正做到 “边缘触发”,也保证了消息到达的实时性。
WebSocket 的优点是:
- 支持服务端推送的双向通信,大幅降低服务端轮询压力;
- 数据交互的控制开销低,降低双方通信的网络开销;
- Web 原生支持,实现相对简单。
4. TCP 长连接衍生的 IM 协议 (其它参考)
除了 WebSocket 协议,在 IM 领域,还有其他一些常用的基于 TCP 长连接衍生的通信协议,如 XMPP 协议、MQTT 协议以及各种私有协议。
这些基于 TCP 长连接的通信协议,在用户上线连接时,在服务端维护好连接到服务器的用户设备和具体 TCP 连接的映射关系,通过这种方式客户端能够随时找到服务端,服务端也能通过这个映射关系随时找到对应在线的用户的客户端。
而且这个长连接一旦建立,就一直存在,除非网络被中断。这样当有消息需要实时推送给某个用户时,就能简单地通过这个长连接实现 “服务端实时推送” 了。
但是上面提到的这些私有协议都各有优缺点,如:XMPP 协议虽然比较成熟、扩展性也不错,但基于 XML 格式的协议传输上冗余比较多,在流量方面不太友好,而且整体实现上比较复杂,在如今移动网络场景下用得并不多。
而轻量级的 MQTT 基于代理的 “发布 / 订阅” 模式,在省流量和扩展性方面都比较突出,在很多消息推送场景下被广泛使用,但这个协议并不是 IM 领域的专有协议,因此对于很多 IM 下的个性化业务场景仍然需要大量复杂的扩展和开发,比如不支持群组功能、不支持离线消息。
因此,对于开发人力相对充足的大厂,目前很多是基于 TCP(或者 UDP)来实现自己的私有协议,一方面私有协议能够贴合业务需要,做到真正的高效和省流;另一方面私有协议相对安全性更高一些,被破解的可能性小。目前主流的大厂很多都是采用私有协议为主的方式来实现。
➣ 异常和容错处理
建立一个完备的 IM 系统需要考虑的问题:
- 如何保证长连接通道能随时处于良好的连接状态(随时接收对方 write 的消息)?
- 服务端如何实现心跳机制?
- 当连接不可用时,用户此时发送的消息该怎么处理?
- 连接断开时,对方发送消息该怎么实现?
- 当恢复连接时,怎么恢复之前的聊天现场?
- 如何保证发送的消息的实时性?
- 当我收到对方的消息时,对方怎么知道我已经收到了?
- 当重复收到对方的消息时,该怎么处理?
- 当收到的消息时序有错乱,该怎么处理?
- 离线消息该保存多久?
- 图片、短视频、大文件这类的离线消息,多媒体文件该怎么存(有可能量会很大)?
- 当本地的消息积累太多时,怎么能保证本地存储的性能?
- 当应用更新、升级或异常时,怎么能保证本地存储的完整性(不被破坏)?
1. 网络异常和重连处理
SDK 设计了重连机制,当网络异常时,可以使用 SDK 接口传入设置的重连频率自动重连。
2. 发送接收消息乱序问题处理
每个会话和每条消息除了有自己的 ID 外还有时间戳 timestamp
属性,开发者收到 SDK 推送的消息时,需要根据时间戳来进行排序。由于 SDK 没有本地存储机制,因此不具备二次处理消息的能力,需要开发者自己处理这部分逻辑。
3. 对低版本浏览器的兼容处理
- 本地数据存储方面:采用
LocalStorage
,浏览器不支持时降级为MemoryStorage
。对于 IE 浏览器还使用了ElementUserData
的兼容方式。 - 长连接通信方面:优先使用
Websocket
,浏览器不支持时降级为XHR
长轮询。
4. 历史消息和未读消息的处理
首先应该明确,每个会话的每条消息都带有时间戳,并且 SDK 会记录每个会话的最新一条消息的时间戳。
调用 SDK 接口查询历史消息需要提供这些参数:
- conversationType:会话类型
- targetId:会话 ID
- timestamp:时间戳
- count:查询条数
SDK 接口返回时除了携带所有消息,还会有一个附带参数表明是否还有更多历史消息,如果还有更多历史消息,则需要再次调用 SDK 接口获取其余历史消息。
每个会话会有一个未读消息属性,查询会话信息时可以拿到,SDK 内部会自动更新这个属性值,不需要开发者多次发送查询请求进行实时更新。
5. 前端对大量消息展示的性能优化处理
有时候项目中要求我们在不使用分页的情况下渲染一个超长的列表组件,比如一个文件上传列表里面的每个文件上传任务,我们同时添加成千上万个上传任务,然后并行上传几个,操作者同时也能通过列表的上下滚动来查看每个上传任务的状态。这种数量级的界面元素展示可能导致我们的界面会有一定程度的卡顿。
一个解决方案就是可以采用懒加载技术来实现当滚动到任务列表底部时加载其余的一小部分任务列表元素,这样虽然解决了初次渲染时耗费时间过长的问题,不过随着滚动到底部加载的任务条目越来越多,界面的渲染负载也会越来越大。这种情况下采用虚拟化滚动技术来进行优化就显得很有必要了。
虚拟列表是一种根据滚动容器元素的可视区域高度来渲染长列表数据中某一个部分数据的技术,如果要直接使用的话可以考虑这两个热门的虚拟滚动库 react-window 和 react-virtualized。
6. 前端负载均衡的处理
在 SDK 设计时考虑了后端多组服务器的情况,前端首先会通过固定的 Navigation Server (导航服务器) 获取所有可用的服务器配置信息等,然后在 SDK 初始化时同时向这些服务器发送连接请求,首先连接上的会被作为当前默认的服务器节点。
此机制设计的参考依据:
- 首先连接上的服务器在当前用户的网络环境下响应速度最快。
- 无后端介入机制,减少实现复杂度,提高系统可靠性。
四、IM Web SDK 架构和业务流程
参考融云 IM 开源 SDK 设计
➣ Web SDK 架构图
1. Storage 存储
主要分为临时存储和持久化存储:
- 临时数据:SDK 云信过程中产生的临时数据,比如会话列表、各种时间戳、token 等。
- 持久化数据:比如群消息已读状态回执、历史连接过的 appId、sessionId、导航数据等等。下文有详细说明。
持久化存储方面也对低版本浏览器做了兼容:
优先使用 LocalStorage
IE 早期版本采用
Element#UserData
方式兼容,原理就是浏览器将 DOM Element 作为存储容器,不过只是作为备用兼容方案,并不支持持久化。兜底方案降级为
MemoryStorage
,直接使用内存存储。这种方案下用户多次登录都需要重新获取所有数据,包括导航数据、自定义数据等,对用户的友好度降低。
2. Service 业务和服务
- [01] IM Client:作为一个装饰器对象对外提供 SDK 接口,大部分接口功能会实际调用
Data Access Provider
和Bridge
,相当于对内部整体逻辑做了一层装饰器处理。 - [02] Data Access Provider:负责大部分业务逻辑,会直接调用 Bridge 和 Storage。
- [03] Bridge:将 IM Client 和 Msg Client 连接起来,设置监听者,内部 Client 的调用 Handler。
- ├─ Navigation:导航数据处理,获取用于建立服务器连接前的一些配置信息和定制化信息。
- └─ Client:消息客户端,整个消息通信的中心,外部响应消息监听者,内部调用 Channel 通道进行消息通信。
- ├─ MessageHandler:消息接收和处理
- └─ Channel:消息通道逻辑,会注册
连接状态监听者
和消息监听者
。- ├─ Socket:通信载体对象,底层会调用
WebSocket
或Long Polling
传输对象进行数据发送和接收。- ├─ MessageOutputStream:在数据发送前会对明文请求消息数据进行 Header 定制和
Stream
流化编码处理。 - └─ MessageInputStream:在数据接收前,会对
Stream
流化响应数据进行解码生成 SDK 直接使用的明文对象。
- ├─ MessageOutputStream:在数据发送前会对明文请求消息数据进行 Header 定制和
- ├─ Listeners:连接状态监听器和消息监听器
- ├─ ConnectionStatusListener: 连接状态监听者
- └─ ReceiveMessageListener: 消息接收监听者
- ├─ Socket:通信载体对象,底层会调用
3. Network 网络
以 WebSocket 作为主要通信协议,HTTP 长轮询作为兼容方案,上传文件、下载文件、获取导航信息等会用到 HTTP 短连接用于即时数据的获取。
需要注意的是整个通信过程中,Socket 传输的数据都是二进制数据,不会传明文,客户端发送消息时进行二进制编码,服务端接收消息时进行二进制解码。这样设计的目的一个是为了信息传送安全,另一个就是二进制数据有更好的传输性能。
➣ 整体业务流程
基础概念说明:
- appKey:应用的唯一标识 (可能不需要实现)
- userId :用户的唯一标识
- token :用户的通信凭证
1. 获取应用的 appKey / appSecret
如果要作为一个云平台是公开对外公开提供服务的,类似于微信小程序的概念的话。appKey 可以用于区分云平台不同的开发者的应用,以提供每个开发者独立的服务。
早期简单实现可以只考虑一个开发者,也就是个 appKey / appSecret 固定,不用需要动态分发给不同的开发者。
2. 前端通过 App 应用服务器接口获取 token
开发环境下,为了方便开发者调试,可以使用可用的测试 token。SDK 调用者固定传入测试 token,以测试正常流程。
token 作为用户使用 IM 系统时通信的凭证,用于验证用户身份,以及用户的消息接收和发送。
流程图:
(1)客户端向 应用服务器 发送请求,请求携带 userId、appKey 等参数。
(2)应用服务器拿到请求后,使用步骤一拿到的 appSecret,根据签名算法使用 “appSecret + 随机数 (从前端请求拿到) + timestrap(从前端请求拿到)” 生成签名 signature,然后使用 appKey + 随机数 + timestrap + signature
作为参数向 API 服务器 发送请求,获取用户 token。拿到 token 后应用服务器会将其存入数据库,最后 token 作为 http 请求结果并返回给客户端。(这里有个概念就是区分了 API 服务器和应用服务器,如果简化架构的话应用服务器和 API 服务器可以为同一个服务器)。
(3)客户端拿到的 token 后存储 token 数据,之后客户端直接使用 token 跟 IM 消息服务器通信,API 服务器只会用于用户的某次会话中最初的 token 获取。
(4)对于后续客户端获取 token 的请求,App 应用服务器直接从数据库中返回之前从 API 服务器拿到的 token。进而流程简化为:
3. 客户端对 token 的本地化处理
客户端获取到 token 后,可以直接使用 token 跟应用服务器、消息服务器通信。对 token 做本地持久化存储支持后,客户端也可自行实现自动登录和登录过期逻辑。
4. 客户端和 IM 消息服务端进行业务通信
(1)客户端初始化 sdk
1 | RongIMLib.RongIMClient.init(appkey, [options]); |
(2)客户端设置状态监听器和消息监听器
必须设置监听器后,再连接 IM 消息服务器,示例代码如下:
1 | /* 连接状态监听器 */ |
(3)客户端连接服务器
连接消息服务器必须在执行 RongIMLib.RongIMClient.init(appkey);
之后调用,除监听以外所有方法都必须在调用 connect 连接服务器成功之后再调用。
- dev 开发环境下客户端可以直接写死 token 测试
- prod 生产环境下需要通过
应用服务器 (App Server)
获取 token
1 | // 本地调试写死或者通过应用服务器获取,dev/prod 的两种处理方式 |
(4)网络错误后客户端重新连接服务器
当网络不可用或网络断开导致客户端离线时,开发者可以手动通过 SDK 尝试重连。SDK 提供给开发者重连服务器方法 reconnect
,该功能内部采用定时器递归机制实现。
重连时提供两种方式,可以自行选择:
- 重连单次,调用该方法后 SDK 会尝试重连一次,如果重连成功,会触发 onSuccess 回调,如果重连失败会触发 onError 回调。
- 按照给定的重试频率数组进行自动重连,当给定的所有重试时间都用完后而服务器仍未连接上时,会携带
NETWORK_UNAVAILABLE
对应的错误码触发 onError 回调。
1 | var callback = { |
五、SDK 源码文件说明
模块文件内容和职责索引:
1. 内部业务流程处理类定义
[file] internal/navi.ts:
- [class] Navigation:导航信息,存储所有可能用到的服务器和其它配置等
- [caller] Client: 创建和调用消息连接端实例
- [function] connect:开始连接服务器
- [function] requestNavi:获取导航信息
- [function] getNaviSuccess:设置本地持久化数据
- [class] Navigation:导航信息,存储所有可能用到的服务器和其它配置等
[file] internal/connect_client.ts:
- [class] Bridge:连接类,实现 imclient 与 connect_client 的连接
- [instance] Navigation:导航信息实例
- [instance] Client:消息连接端实例
- [function] setListener:设置监听器
- [function] connect:连接服务器
- [function] Client.connect:调用实例连接
- [function] queryMsg:查询消息
- [function] pubMsg:发布消息
- [class] Client: 消息连接客户端类
- [instance] MessageHandler:消息处理实例
- [instance] Channel:消息通道实例
- [attr] token:用户 token
- [attr] userId: 用户 ID
- [attr] heartbeat:心跳间隔
- [attr] chatroomId:聊天室 ID
- [function] connect:初始化消息处理、创建 Channel、设置监听器
- [function] syncTime:同步消息
- [function] invoke:syncTime同步消息时,会调用 invoke,分为在线和离线消息两种情况
- [class] Channel: 消息通道类
- [instance] Socket: Socket通信实例
- [super] token:用户 token
- [listener] _ConnectionStatusListener: 连接状态监听者
- [listener] _ReceiveMessageListener: 消息接收监听者
- [attr] url:服务器地址
- [function] startConnect:获取服务器配置后开始调用 Transportation 实例连接
- [class] MessageHandler:消息处理类
- [super] Client: 消息连接端实例
- [function] handleMessage:消息处理功能
- [01] 处理顶层消息:
- [enum] ConnAckMessage:连接应答消息类型,服务器连接成功后收到(发向客户端)
- [enum] QueryAckMessage:请求查询消息应答(发向客户端,携带查询结果)
- [enum] QueryConMessage:请求查询消息回执(发向服务器,表明已收到查询结果)
- [enum] PingReqMessage:心跳请求消息信令(双向,用于心跳检测和 Socket 验活)
- [enum] PingRespMessage:心跳响应消息信令(发往客户端)
- [enum] DisconnectMessage:断开连接消息(发往客户端)
- [02] 调用 onReceived 处理聊天消息和聊天回执消息:
- [enum] PublishMessage:聊天消息,客户端发布或收到此类消息(双向)
- [enum] PubAckMessage: 发送聊天消息应答。某些消息发送之后要求服务端接收时回发回执消息,同样的收到某些新消息时,服务端可能要求客户端发送回执消息(双向)
- [01] 处理顶层消息:
- [function] onReceived:接收消息处理
- [class] Socket: Socket通信类
- [listener] _events:事件监听者容器
- [instance] PollingTransportation:长轮询传输实例
- [instance] SocketTransportation:Websocket 传输实例
- [function] connect:连接远程服务器
- [function] send:发送消息
- [function] disconnect:端开连接
- [function] fire:触发监听器
- [function] on:注册监听器
- [function] removeEvent:移除监听器
- [class] Bridge:连接类,实现 imclient 与 connect_client 的连接
2. 基础单元类定义
[file] internal/transportation.ts:
- [interface] Transportation:数据传输接口定义
[file] internal/transportation/websocket.ts:
- [class] SocketTransportation:Websocket 数据传输类
- [instance] MessageOutputStream:消息数据输出流实例(发送)
- [instance] MessageInputStream:消息数据输入流实例(接收)
- [class] SocketTransportation:Websocket 数据传输类
[file] internal/transportation/xhrpolling.ts:
- [class] PollingTransportation:XHR 长轮询数据传输类
- [instance] MessageInputStream:消息数据输入流实例(接收)
- [attr] MessageOutputStream:消息数据输出流实例(发送,由上层进行编码处理)
- [class] PollingTransportation:XHR 长轮询数据传输类
[file] internal/internal_callback.ts:
- [class] MessageCallback:基础消息回调基类
- [class] CallbackMapping: 聊天室消息映射处理类
- [class extends MessageCallback] PublishCallback: 发送消息回调类,内部包含定时器超时处理
- [class extends MessageCallback] QueryCallback:拉取新消息回调类,内部包含定时器超时处理
- [class extends MessageCallback] ConnectAck:服务器连接回调处理类,调用
checkSocket
,并触发StatusChanged
事件
[file] internal/rmtp/signalling.ts:
- [class] BaseMessage:基础消息类
- [class extends BaseMessage] ConnectMessage: 连接消息类型
- [class extends BaseMessage] ConnAckMessage: 连接应答消息类型,服务器连接成功后收到(发向客户端)
- [class extends BaseMessage] DisconnectMessage:断开连接消息(发往客户端)
- [class extends BaseMessage] PingReqMessage:心跳请求消息信令(双向,用于心跳检测和 Socket 验活)
- [01] 用于
keepLive
心跳 - [02] 第一次连接服务器时,用于
checkSocket
验证服务器是否已经正常连接
- [01] 用于
- [class extends BaseMessage] PingRespMessage:心跳响应消息信令(发往客户端)
- [class extends BaseMessage] RetryableMessage:用于 MesssageId 封装
- [class extends RetryableMessage] PubAckMessage:发送聊天消息应答。某些消息发送之后要求服务端接收时回发回执消息,同样的收到某些新消息时,服务端可能要求客户端发送回执消息(双向)
- [class extends RetryableMessage] PublishMessage:聊天消息,客户端发布或收到此类消息(双向)
- [class extends RetryableMessage] QueryMessage:请求查询消息类型
- [class extends RetryableMessage] QueryConMessage:请求查询消息回执(发向服务器,表明已收到查询结果)
- [class extends RetryableMessage] QueryAckMessage:请求查询消息应答(发向客户端,携带查询结果)
[file] internal/rmtp/stream.ts:
- [class] MessageOutputStream:消息对象流对象(消息对象写入流,用于发送请求)
- [attr] out:消息对象流,使用
binaryHelper.convertStream
生成
- [attr] out:消息对象流,使用
- [class] MessageInputStream:流转换为消息对象(消息对象写入流,用于接收响应)
- [instance] msg:消息类型
- [enum] ConnectMessage:连接消息
- [enum] ConnAckMessage:连接应答消息
- [enum] PublishMessage:聊天消息,客户端发布或收到此类消息(双向)
- [enum] PubAckMessage:发送聊天消息应答。某些消息发送之后要求服务端接收时回发回执消息,同样的收到某些新消息时,服务端可能要求客户端发送回执消息(双向)
- [enum] QueryMessage:查询聊天记录消息(双向)
- [enum] QueryAckMessage:请求查询消息应答(发向客户端,携带查询结果)
- [enum] QueryConMessage:请求查询消息回执(发向服务器,表明已收到查询结果)
- [enum] PingReqMessage:心跳请求消息信令(双向,用于心跳检测和 Socket 验活)
- [enum] PingRespMessage:心跳响应消息信令(发往客户端)
- [enum] DisconnectMessage:断开连接消息(发往客户端)
- [instance] msg:消息类型
- [class] Header:消息头部
- [class] BinaryHelper:二进制帮助对象
- [class] RongIMStream:流对象
- [class] MessageOutputStream:消息对象流对象(消息对象写入流,用于发送请求)
[file] internal/utils.ts:
- [class] MessageUtil
- [function] messageParser:消息解析器
- [function] detectCMP:XHR 对象创建
- [class] MessageUtil
3. 整体业务流程定义
[file] providers/data_access/server.ts:
- [class extends DataAccessProvider] ServerDataProvider:服务器数据访问提供者
- [function] connect:连接服务器
- [caller] RongIMClient.bridge: 连接桥
- [function] RongIMClient.bridge.setListener:设置监听器
- [function] RongIMClient.bridge.connect:执行连接
- [function] RongIMClient.bridge.pushMsg:发送消息
- [class extends DataAccessProvider] ServerDataProvider:服务器数据访问提供者
[file] imclient.ts:
- [class] RongIMClient:SDK 客户端类
- [function] init:SDK 初始化
- [attr] RTCListener: RTC消息监听器
- [attr] Conversation:会话对象
- [attr] Protobuf:公众服务订阅相关
- [attr] MessageParams:所有消息参数定义
- [attr] MessageType:所有消息内容类型定义,比如文本类型、图片类型
- [attr] LogFactory:存储长连接通信消息码和消息含义映射关系,比如通信超时、聊天室异常等
- [instance] _dataAccessProvider:imclient 相关逻辑实际执行者,imclient 作为一个装饰器包装了它,并且它也有多种实现
- [instance] _storageProvider:本地存储区域,内部可能由 LocalStorage/Memory/UseData实现
- [instance] _memoryStore:内存临时存储区域,用于存储会话列表和其它临时数据
- [function] initApp:执行 init,并调用外部回调函数
- [function] connect:连接
- [function] reconnect:重新连接
- [function] registerMessageType:注册自定义消息类型
- [function] setConnectionStatusListener:设置连接状态变化的监听器
- [function] setOnReceiveMessageListener:设置接收消息的监听器
- [function] logout:清理所有连接相关的变量
- [function] disconnect:断开连接
- [function] startCustomService:开启客服
- [function] stopCustomeService:关闭客服
- [function] switchToHumanMode:切换到人工模式
- [function] evaluateRebotCustomService:评价机器人客服
- [function] evaluateHumanCustomService:评价人工客服
- [function] getCurrentConnectionStatus:获取当前连接状态
- [function] getConnectionChannel:获取当前连接通道
- [function] getStorageProvider:获取本地存储提供者
- [function] setFilterMessages:过滤聊天室消息
- [function] getAgoraDynamicKey:获取 Agora 动态密钥
- [function] getCurrentUserId:获取当前用户 ID
- [function] getDeltaTime:获取服务器时间和本地时间的差值
- [function] getMessage:获取消息
- [function] deleteLocalMessages:删除本地消息
- [function] updateMessage:更新消息
- [function] clearData:清除本地存储数据
- [function] clearMessages:清除消息
- [function] clearMessagesUnreadStatus:清除本地存储的未读消息,目前清空内存中的未读消息
- [function] deleteMessages:删除消息记录
- [function] sendLocalMessage:发送本地消息
- [function] getPullSetting:获取拉取设置
- [function] setOfflineMessageDuration:设置离线消息持续时间
- [function] sendMessage:发送消息
- [function] sendReceiptResponse:发送消息已读回执
- [function] sendTypingStatusMessage:更新“正在输入”状态
- [function] sendTextMessage:发送文本消息
- [function] sendRecallMessage:发送撤回消息
- [function] insertMessage:向本地插入一条消息,不发送到服务器
- [function] setMessageContent:设置消息内容
- [function] setMessageSearchField:设置消息搜索字段
- [function] getHistoryMessages:获取历史消息记录
- [function] getRemoteHistoryMessages:拉取远程某个时间戳之前的消息
- [function] clearHistoryMessages:清除历史消息记录
- [function] clearRemoteHistoryMessages:清除某个时间戳之前的历史消息
- [function] deleteRemoteMessages:删除远程历史消息
- [function] hasRemoteUnreadMessages:是否有未接收的消息,jsonp方法
- [function] getTotalUnreadCount:获取未读消息总数
- [function] getConversationUnreadCount:指定多种会话类型获取未读消息数
- [function] getUnreadCount:获取用户、会话类型的未读消息总数。
- [function] setUnreadCount:指定用户、会话类型的未读消息总数。
- [function] clearUnreadCountByTimestamp:清除某个时间戳之前的未读消息数。
- [function] clearUnreadCount:清除会话未读消息数
- [function] clearTotalUnreadCount:清除未读消息总数
- [function] clearLocalStorage:清除本地存储
- [function] setMessageExtra:设置消息额外信息
- [function] setMessageReceivedStatus:设置消息接收状态
- [function] setMessageStatus:设置消息状态
- [function] setMessageSentStatus:设置消息发送状态
- [function] clearTextMessageDraft:清除文本消息草稿
- [function] getTextMessageDraft:获取指定消息和会话的草稿
- [function] saveTextMessageDraft:保存文本消息草稿
- [function] searchConversationByContent:搜索会话
- [function] searchMessageByContent:搜索消息内容
- [function] clearCache:清除缓存
- [function] clearConversations:清除会话
- [function] getConversation:获取指定会话,此方法需在getConversationList之后执行
- [function] pottingConversation:组装会话列表
- [function] addConversation:添加会话
- [function] getConversationList:获取会话列表
- [function] getRemoteConversationList:拉取远程会话列表
- [function] updateConversation:更新会话
- [function] createConversation:创建会话
- [function] removeConversation:删除会话
- [function] setConversationHidden:设置会话隐藏状态
- [function] setConversationToTop:设置会话置顶状态
- [function] getConversationNotificationStatus:获取指定用户和会话类型免提醒
- [function] setConversationNotificationStatus:设置指定用户和会话类型免提醒
- [function] getNotificationQuietHours:获取免打扰时间
- [function] addMemberToDiscussion:加入讨论组
- [function] createDiscussion:创建讨论组
- [function] getDiscussion:获取讨论组
- [function] quitDiscussion:退出讨论组
- [function] removeMemberFromDiscussion:移除讨论组成员
- [function] setDiscussionInviteStatus:设置讨论组邀请状态
- [function] setDiscussionName:设置讨论组名称
- [function] joinChatRoom:加入聊天室
- [function] setDeviceInfo:设置设备信息
- [function] setChatroomHisMessageTimestamp:设置聊天室历史消息时间戳
- [function] getChatRoomHistoryMessages:获取聊天室历史消息
- [function] getChatRoomInfo:获取聊天室信息
- [function] quitChatRoom:退出聊天室
- [function] setChatroomEntry:设置聊天室入口
- [function] forceSetChatroomEntry:强制设置聊天室入口
- [function] getChatroomEntry:获取聊天室入口
- [function] getAllChatroomEntries:获取所有聊天室入口
- [function] removeChatroomEntry:移除聊天室入口
- [function] forceRemoveChatroomEntry:强制移除聊天室入口
- [function] addToBlacklist:加入黑名单
- [function] getBlacklist:获取黑名单
- [function] getBlacklistStatus:得到指定人员再黑名单中的状态
- [function] removeFromBlacklist:将指定用户移除黑名单
- [function] getFileToken:获取文件上传的token
- [function] getFileUrl:获取文件下载的url
- [function] getUserStatus:获取用户在线状态
- [function] setUserStatus:设置用户在线状态
- [function] setUserStatusListener:设置用户在线状态监听者
- [function] getAppInfo:获取应用信息
- [function] getSDKInfo:获取 SDK 信息
- [class] RongIMClient:SDK 客户端类
六、概念和模块说明
➣ 用户、群组、聊天室相关的逻辑
应用服务器维护用户所有信息,请求注册用户(提供 id、name、portrait),id 用于消息收发,name 用于 push,此接口同时提供了刷新方法。
应用服务器维护群组及成员信息,也就是说,群的创建、群成员的维护都需要在应用服务器完成,后同步到消息服务器,但只同步群组(群 id)及群成员信息(成员 id),两种 id 同样用于消息的收发,消息发送给群组 id,群成员可以获取对应消息。
用户创建和加入聊天室是在端上调用 SDK 相应方法完成,具体参考 Web SDK Demo 代码示例。
通过连接服务器、消息收发等携带相关 id (用户 Id、群组 Id)到各端,端上通过 id 请求应用服务器获取详细信息(用户名、头像、群组名称、群头像、群成员信息以及其他业务需要的职位、等级等业务相关数据)。
用户使用 token 链接消息服务器,成功后返回当前用户的 id,可以获取登录用户的详细信息。
接收到消息时,消息体里有 conversationType、targetId 和 senderUserId,单聊(conversationType = 1)可以通过 userId = senderUserId 获取发送者详细信息;群聊(conversationType = 3)可以通过 groupId = targetId 获取群组信息,可以直接返回群组及成员信息,也可以分开请求获取,具体取决于接口设计。
收到聊天室消息(conversationType = 4),可以通过 senderUserId 得知发送者 id,通过 targetId 得知聊天室 id,然后可以获取聊天室成员列表,进而处理相应的用户信息逻辑。
➣ 会话和消息
会话与消息的关系
Web SDK 不包含消息数据的本地存储机制,开发者需要自己进行消息的存储。
会话实体类和消息实体类是用来存储本地会话和消息的容器类,除了包含会话内容和消息内容外,还包括了保存在本地的各种状态。
用来存储消息的实体类主要有 Conversation(会话) 和 Message(消息) 两个实体类,在客户端读取消息时,获取的对象都和这两个类相关。会话有多种类型,可以是私聊会话,也可以是群组会话等,每一个 Conversation(会话)包含多条 Message(消息),关系如下图所示:
如何标识一个会话
通过 conversationType 和 targetId,可以唯一确定一个会话。ConversationType 枚举值意义和对应的 targetId 意义为:
会话名称 | 会话类型枚举 ConversationType | 对应的 targetId | 状态值 |
---|---|---|---|
单聊 | PRIVATE | 用户的 Id(userId) | 1 |
群组 | GROUP | 群组的 Id(groupId) | 3 |
聊天室 | CHATROOM | 聊天室的 Id(chatroomId) | 4 |
客服 | CUSTOMER_SERVICE | 客服的 Id(customerServiceId) | 5 |
系统会话 | SYSTEM | 系统账户 Id | 6 |
应用公众服务 | APP_PUBLIC_SERVICE | 应用公众服务的 Id (publicServiceId) | 7 |
公众服务 | PUBLIC_SERVICE | 公众服务的 Id (publicServiceId) | 8 |
消息的定义
MessageContent 是消息基类,所有消息类都继承于 MessageContent 类。需要注意的是,MessageContent 类和 Message 类之间的关系:Message 中包含了一个具体的继承自 MessageContent 的消息,就是 Content 属性,可以通过 setContent() 和 getContent() 进行存取。
约定:如果您要定义一个内容类消息(需要显示在聊天会话界面中,且不是通知类消息),请从 MessageContent 类继承,命名为 XxxxxMessage。
内容消息表示一个用户间发送的包含具体内容的消息,需要展现在聊天界面上,如文字消息、语音消息等。
消息的分类
消息分类 | 消息行为状态标识 |
---|---|
内容类消息 | 表示一个用户发送的包含具体内容的消息,需要展现在聊天界面上,如文字消息、语音消息等。 |
通知类消息 | 表示一个通知信息,可能展现在聊天界面上,如提示条通知。 |
状态类消息 | 表示一个状态,用来实现如 “对方正在输入” 的功能。 |
信令类消息 | 在实现 SDK 自身业务功能时使用的,开发者不需要对其做任何处理。 |
(1)内容类消息
优先实现 TextMessage,其它类型 Message 需要再进行规划。
消息类型 | ObjectName | 类名 | 是否计数 | 是否存储 |
---|---|---|---|---|
文字消息 | RC:TxtMsg | TextMessage | 是 | 是 |
语音消息 | RC:VcMsg | VoiceMessage | 是 | 是 |
高质量语音消息 | RC:HQVCMsg | HQVoiceMessage | 是 | 是 |
图片消息 | RC:ImgMsg | ImageMessage | 是 | 是 |
GIF 图片消息 | RC:GIFMsg | GIFMessage | 是 | 是 |
图文消息 | RC:ImgTextMsg | RichContentMessage | 是 | 是 |
文件消息 | RC:FileMsg | FileMessage | 是 | 是 |
位置消息 | RC:LBSMsg | LocationMessage | 是 | 是 |
小视频消息 | RC:SightMsg | SightMessage | 是 | 是 |
公众服务单图文消息 | RC:PSImgTxtMsg | PublicServiceRichContentMessage | 是 | 是 |
公众服务多图文消息 | RC:PSMultiImgTxtMsg | PublicServiceMultiRichContentMessage | 是 | 是 |
(2)通知类消息
好友通知、提示条通知、已读通知消息可以考虑优先实现,其它通知类型再进行规划。
消息类型 | ObjectName | 类名 | 是否计数 | 是否存储 |
---|---|---|---|---|
好友通知消息 | RC:ContactNtf | ContactNotificationMessage | 否 | 是 |
资料通知消息 | RC:ProfileNtf | ProfileNotificationMessage | 否 | 是 |
通用命令通知消息 | RC:CmdNtf | CommandNotificationMessage | 否 | 是 |
提示条通知消息 | RC:InfoNtf | InformationNotificationMessage | 否 | 是 |
群组通知消息 | RC:GrpNtf | GroupNotificationMessage | 否 | 是 |
已读通知消息 | RC:ReadNtf | ReadReceiptMessage | 否 | 否 |
公众服务命令消息 | RC:PSCmd | PublicServiceCommandMessage | 否 | 否 |
命令消息 | RC:CmdMsg | CommandMessage | 否 | 否 |
(3)状态类消息
可以优实现正在输入状态处理,其它状态类型再进行规划。
消息类型 | ObjectName | 类名 | 是否计数 | 是否存储 |
---|---|---|---|---|
对方正在输入状态消息 | RC:TypSts | TypingStatusMessage | 否 | 否 |
群消息已读状态回执 | RC:RRRspMsg | ReadReceiptResponseMessage | 否 | 否 |
(4)信令类消息
内部用于和后端通信,比如服务器连接成功后信令、心跳检测信令、接收消息回执信令等。
待完善
消息体解析和交互流程说明
1. 消息类型
- [class] ConnectMessage:连接消息 (发向客户端) / 消息类型值:1
- [class] ConnAckMessage:连接应答消息类型,服务器连接成功后收到 (发向客户端) / 消息类型值:2
- [class] QueryMessage:请求查询消息 (发向服务器,用于查询) / 消息类型值:5
- [class] QueryAckMessage:请求查询消息应答 (发向客户端,携带查询结果) / 消息类型值:6
- [class] QueryConMessage:请求查询消息回执 (发向服务器,表明已收到查询结果) / 消息类型值:7
- [class] PingReqMessage:心跳请求消息信令 (双向,用于心跳检测和 Socket 验活) / 消息类型值:12
- [class] PingRespMessage:心跳响应消息信令 (发往客户端) / 消息类型值:13
- [class] DisconnectMessage:断开连接消息 (发往客户端) / 消息类型值:14
- [class] PublishMessage:聊天消息,客户端发布或收到此类消息 (双向) / 消息类型值:3
- s_ntf:系统通知消息
- s_msg:聊天消息
- s_stat:用户状态更新消息
- s_cmd:聊天室消息
- pp/pd/ch/pc:群组消息
- [class] PubAckMessage: 发送聊天消息应答。某些消息发送之后要求服务端接收时回发回执消息,同样的收到某些消息后,客户端会往服务端发送回执消息。(双向) 码值:4
2. 消息体
消息头各字段数据意义 (flags)
以下参数由
HEADER 运算规则和枚举
处的解码算法计算得出。
1 | { |
消息体数据格式 (body)
1 | { |
3. 消息头部码值编解码规则
消息头部有很多标志位,通过编解码算法简化标志位的表示,服务器和客户端拿到消息后需要进行解码,发送消息时需要编码。
运算规则:
1 | const Qos = { |
4. 消息交互逻辑描述
历史消息 (会话)
- 历史消息是针对某一个会话的,根据会话中的某条消息时间戳,可以向上或向下查询历史消息。
离线消息 (全局)
- 离线消息就是用户不在线时,服务端存储的消息,需要在用户上线的时候由手动调用 SDK 拉取,SDK 会每隔 180s 向服务端发送消息拉取离线消息。
- SDK 连接服务器成功后,会自动拉取一次离线消息 (
QueryMessage-pullMsg
),客户端断开连接后重连时也会调用。 - 注意离线消息和历史消息不一样的是,历史消息是针对用户的,SDK 调用者在会话中会主动拉取历史消息。
- 而离线消息是 SDK 自发的一种消息机制,一方面防止网络波动和其它情况下的消息丢失,另一方面可以在用户上线时自动获取服务器的离线消息,它不属于用户 (SDK 调用者) 行为。
心跳消息
- SDK 会每隔 30s 向服务端发送心跳包
PingReqMessage
,用于保持连接。服务端需要回传PingRespMessage
用于响应。同时服务器端也是通过这个过程来确定客户端是否在线,因为活跃的客户端一定会每隔 30s 向服务器发送心跳包,断线的用户收到的消息会被离线处理。
- SDK 会每隔 30s 向服务端发送心跳包
用户初次加载界面后
- 需要使用
QueryMessage-qryCon
拉取所有会话,每个会话都有未读消息数量和最后一条未读消息内容。 - 首次拉取离线消息和首次列举会话并不冲突,如果首次拉取的离线消息先到达,会先根据某些离线消息来创建某几个本地会话。而列举会话请求返回后也会直接利用前面创建的会话,属于 SDK 自发的逻辑。
- 之后 SDK 主动拉取离线消息 (消息心跳,需要区别于 ping 心跳) 或是收到消息通知后拉取在线消息时,SDK 调用者需要根据每条消息所属的会话对会话列表做更新。
- 需要使用
聊天界面
- 进入聊天界面:将最新的消息设置为已读 (通过
PublishMessage
的方式发送,同时会收到服务器端的发送回执PubAckMessage
)。 - 输入信息:聚焦时发送
sendTypingStatusMessage
表明正在输入。 - 点击发送文本:向后端发送
PublishMessage-ppMsgP
信息。
- 进入聊天界面:将最新的消息设置为已读 (通过
发送消息整体流程
- 客户端向服务器发送
PublishMessage-ppMsgP
消息。 - 服务端收到后向消息发送端发一条
PubAckMessage
消息表明消息发送成功。 - 服务器开始进行消息转发,如果接收方不在线,则将消息存储为离线消息。如果接收方在线,则服务器先向接收端发送
PublishMessage
(s_ntf
) 用于通知有消息到达。另一种情况时恰好此时接收端正在查询消息列表,那么此消息数据也可能由一条QueryAckMessage
响应带回。 - 消息接收端成功接收新消息通知
PublishMessage-s_ntf
后,再向服务端发送QueryMessage-pullMsg
用于拉取消息,服务器端接收到拉取消息请求,将消息数据通过QueryAckMessage
消息发送给消息接收端,然后消息接收端收到后向服务端发送QueryConMessage
消息表明成功接收。 - 另外如果此时接收端正在在聊天界面则会继续向服务器发送已读回执,回执通过
PublishMessage
格式发出 (参数见下方描述)。 - 服务器端拿到已读回执做数据处理后,向消息接收端发出
PubAckMessage
消息表明已读回执已经成功处理,最后向消息发送端发送PublishMessage
用于表示消息已经被接收端读取。 - 消息发送端拿到最后的
PublishMessage
信息将本地的消息状态更新为对方已读,并向服务器发送PubAckMessage-s_msg
表明已处理。
- 客户端向服务器发送
发送消息整体流程 (补充)
- 融云在处理消息转发的时候,会有一定的动态机制,有些情况下会通过服务端下发通知让接收端拉取消息,有时候会直接向接收端发送
PublishMessag
传输消息数据,暂时未知为何这样处理。
- 融云在处理消息转发的时候,会有一定的动态机制,有些情况下会通过服务端下发通知让接收端拉取消息,有时候会直接向接收端发送
查询消息流程
- 客户端向服务器发送
QueryMessage
消息 (离线消息 / 历史消息)。 - 服务器统计消息数量,向消息查询客户端发送
QueryAckMessage
携带所有消息。 - 某些消息客户端收到后,需要向服务端回发
QueryConMessage
表明所查询的消息已经成功接收。
- 客户端向服务器发送
发送方发消息流程
- 发送
PublishMessage-ppMsgP
(私聊个人消息),然后收到服务器PubAckMessage
表明发送成功。
- 发送
接收方收消息流程
- 收到
PublishMessage-s_ntf
消息通知,接收方发出QueryMessage-pullMsg
请求拉取消息。 - 服务器向客户端发送
QueryAckMessage
用于传输消息数据 - 客户端接收消息后向服务器发送
QueryConMessage
表示接收成功。 - 如果用户在聊天界面,还会发出已读信息
PublishMessag-s_msg
将消息标注为已读。
- 收到
➣ 网络通信
Socket 长连接和 XHR 长轮询的方式,消息体都是以二进制 Buffer 流的形式传输的。在一次传输中前端会将各种类型的消息内容加上个本次通信的消息头部转换成对应的 Buffer 流,然后发送给服务器。
通信方式
状态标识 | 状态值 | 状态中文 |
---|---|---|
XHR_POLLING | 0 | 长轮询 |
WEBSOCKET | 1 | 长连接 |
1. Socket 长连接
作为优先的通信方式
WebSocket 是一种全双工的通信协议,支持服务器推消息给客户端以及客户端主动推送消息到服务端,通常 WebSocket 只需要在开始通信时建立连接,然后通信过程中连接会一直存在。
2. XHR 长轮询
作为兼容的通信方式
HTTP 长轮询是人为编程实现的一种通信方式,它在短轮询的基础上加入服务端的连接保持、连接超时和数据变化监听等功能。
前端发送请求后设置定时器,服务端需要在定时器时间内 hold 住前端请求,在定时器到期前需要返回新消息或是返回空以表明没有新消息。反之定时器超时后后端还没返回,则前端视为长连接通信错误,Socket 连接进入 error 状态。
每次前端拿到新消息并处理后,需要立即发送下一个长连接请求给服务器,循环这个流程。它不同于平常使用的短连接,短连接只在需要的时候发送请求到服务端获取接口数据。
通信状态枚举字段
SDK 内部使用
socket 连接会伴随各种状态变化,比如连接成功、收到消息、连接断开等等。
内部逻辑代码:
1 | socket.on('StatusChanged|message|disconnect', function() { |
三种 Socket 信号:
1)StatusChanged:网络连接状态变化
状态标识 | 状态值 | 状态中文 |
---|---|---|
CONNECTED | 0 | 连接成功 |
CONNECTING | 1 | 连接中 |
DISCONNECTED | 2 | 断开连接 |
KICKED_OFFLINE_BY_OTHER_CLIENT | 6 | 用户账户在其他设备登录,本机会被踢掉线 |
WEBSOCKET_UNAVAILABLE | 7 | websocket 连接失败 |
WEBSOCKET_ERROR | 8 | websocket 报错 |
NETWORK_UNAVAILABLE | 3 | 网络不可用 |
DOMAIN_INCORRECT | 12 | 域名错误 |
APPKEY_IS_FAKE | 20 | appkey 不正确 |
CONNECTION_CLOSED | 4 | 连接关闭 |
ULTRALIMIT | 1101 | 互踢次数过多(count > 5) |
REQUEST_NAVI | 201 | 开始请求导航 |
RESPONSE_NAVI | 202 | 请求导航结束 |
RESPONSE_NAVI_ERROR | 203 | 请求导航失败 |
RESPONSE_NAVI_TIMEOUT | 204 | 请求导航超时 |
2)message:消息通信
- [class] ConnAckMessage:连接应答消息类型,服务器连接成功后收到(发向客户端)
- [class] QueryAckMessage:请求查询消息应答(发向客户端,携带查询结果)
- [class] QueryConMessage:请求查询消息回执(发向服务器,表明已收到查询结果)
- [class] PingReqMessage:心跳请求消息信令(双向,用于心跳检测和 Socket 验活)
- [class] PingRespMessage:心跳响应消息信令(发往客户端)
- [class] DisconnectMessage:断开连接消息(发往客户端)
- [class] PublishMessage:聊天消息,客户端发布或收到此类消息(双向)
- s_ntf:系统通知消息
- s_msg:聊天消息
- s_stat:用户状态更新消息
- s_cmd:聊天室消息
- pp/pd/ch/pc:群组消息
- [class] PubAckMessage: 发送聊天消息应答。某些消息发送之后要求服务端接收时回发回执消息,同样的收到某些
状态标识 | 状态值 | 状态中文 |
---|---|---|
CONNECT | 1 | 请求连接 |
CONNACK | 2 | 请求连接返回 |
PUBLISH | 3 | 发布消息 |
PUBACK | 4 | 发布消息回执 |
QUERY | 5 | 查询消息 |
QUERYACK | 6 | 查询消息返回 |
QUERYCON | 7 | 请求查询消息回执,表明已收到查询结果 |
SUBSCRIBE | 8 | 订阅公众号 |
SUBACK | 9 | 订阅公众号回执 |
UNSUBSCRIBE | 10 | 取消订阅公众号 |
UNSUBACK | 11 | 取消订阅公众号回执 |
PINGREQ | 12 | 心跳请求消息 |
PINGRESP | 13 | 心跳响应消息 |
DISCONNECT | 14 | 断开连接 |
3)disconnect:连接断开
通信过程中产生的各种错误码定义
状态标识 | 状态值 | 状态中文 |
---|---|---|
TIMEOUT | -1 | 超时 |
UNKNOWN | -2 | 未知 |
PARAMETER_ERROR | -3 | 参数错误 |
RECALL_MESSAGE | 25101 | 消息撤回 |
SEND_FREQUENCY_TOO_FAST | 20604 | 发送频率过快 |
RC_MSG_UNAUTHORIZED | 20406 | 未经授权 |
RC_DISCUSSION_GROUP_ID_INVALID | 20407 | 群组 Id 无效 |
FORBIDDEN_IN_GROUP | 22408 | 群组被禁言 |
NOT_IN_DISCUSSION | 21406 | 不在讨论组 |
NOT_IN_GROUP | 22406 | 不在群组 |
NOT_IN_CHATROOM | 23406 | 不在聊天室 |
FORBIDDEN_IN_CHATROOM | 23408 | 聊天室被禁言 |
RC_CHATROOM_USER_KICKED | 23409 | 聊天室中成员被踢出 |
RC_CHATROOM_NOT_EXIST | 23410 | 聊天室不存在 |
RC_CHATROOM_IS_FULL | 23411 | 聊天室人数已满 |
RC_CHATROOM_PATAMETER_INVALID | 23412 | 聊天室参数无效 |
CHATROOM_GET_HISTORYMSG_ERROR | 23413 | 聊天室异常 |
CHATROOM_NOT_OPEN_HISTORYMSG_STORE | 23414 | 聊天室未开启历史消息存储 |
CHATROOM_KV_EXCEED | 23423 | 聊天室 KV 设置超限 |
CHATROOM_KV_OVERWRITE_INVALID | 23424 | 聊天室 KV 设置失败 (kv 已存在, 需覆盖设置) |
CHATROOM_KV_STORE_NOT_OPEN | 23426 | 聊天室 KV 存储未开通 |
CHATROOM_KEY_NOT_EXIST | 23427 | 聊天室 Key 不存在 |
SENSITIVE_SHIELD | 21501 | 敏感词屏蔽 |
SENSITIVE_REPLACE | 21502 | 敏感词替换 |
JOIN_IN_DISCUSSION | 21407 | 加入讨论组失败 |
CREATE_DISCUSSION | 21408 | 创建讨论组失败 |
INVITE_DICUSSION | 21409 | 设置讨论组邀请状态失败 |
GET_USERINFO_ERROR | 23407 | 获取用户信息失败 |
REJECTED_BY_BLACKLIST | 405 | 在黑名单中 |
RC_NET_CHANNEL_INVALID | 30001 | 通信过程中,当前 Socket 不存在 |
RC_NET_UNAVAILABLE | 30002 | Socket 连接不可用 |
RC_MSG_RESP_TIMEOUT | 30003 | 通信超时 |
RC_HTTP_SEND_FAIL | 30004 | 导航操作时,HTTP 请求失败 |
RC_HTTP_REQ_TIMEOUT | 30005 | HTTP 请求失败 |
RC_HTTP_RECV_FAIL | 30006 | HTTP 接收失败 |
RC_NAVI_RESOURCE_ERROR | 30007 | 导航操作的 HTTP 请求,返回不是 200 |
RC_NODE_NOT_FOUND | 30008 | 导航数据解析后,其中不存在有效数据 |
RC_DOMAIN_NOT_RESOLVE | 30009 | 导航数据解析后,其中不存在有效 IP 地址 |
RC_SOCKET_NOT_CREATED | 30010 | 创建 Socket 失败 |
RC_SOCKET_DISCONNECTED | 30011 | Socket 被断开 |
RC_PING_SEND_FAIL | 30012 | PING 失败 |
RC_PONG_RECV_FAIL | 30013 | PONG 超时 |
RC_MSG_SEND_FAIL | 30014 | 消息发送失败 |
RC_CONN_ACK_TIMEOUT | 31000 | 做 connect 连接时,收到的 ACK 超时 |
RC_CONN_PROTO_VERSION_ERROR | 31001 | 参数错误 |
RC_CONN_ID_REJECT | 31002 | 参数错误,App Id 错误 |
RC_CONN_SERVER_UNAVAILABLE | 31003 | 服务器不可用 |
RC_CONN_USER_OR_PASSWD_ERROR | 31004 | token 错误 |
RC_CONN_NOT_AUTHRORIZED | 31005 | appId 与 token 不匹配 |
RC_CONN_REDIRECTED | 31006 | 重定向,地址错误 |
RC_CONN_PACKAGE_NAME_INVALID | 31007 | NAME 与后台注册信息不一致 |
RC_CONN_APP_BLOCKED_OR_DELETED | 31008 | 应用被删除或不存在 |
RC_CONN_USER_BLOCKED | 31009 | 用户被封禁 |
RC_DISCONN_KICK | 31010 | 异常断开,由服务器返回,比如用户互踢 |
RC_DISCONN_EXCEPTION | 31011 | 异常断开,服务器内部异常 |
RC_QUERY_ACK_NO_DATA | 32001 | 协议层内部错误,query,上传下载过程中数据错误 |
RC_MSG_DATA_INCOMPLETE | 32002 | 协议层内部错误 |
BIZ_ERROR_CLIENT_NOT_INIT | 33001 | 客户端未初始化 |
BIZ_ERROR_DATABASE_ERROR | 33002 | 数据库初始化失败 |
BIZ_ERROR_INVALID_PARAMETER | 33003 | 参数无效 |
BIZ_ERROR_NO_CHANNEL | 33004 | 网络通道无效 |
BIZ_ERROR_RECONNECT_SUCCESS | 33005 | 重新连接成功 |
BIZ_ERROR_CONNECTING | 33006 | 连接中,再调用 connect 被拒绝 |
MSG_INSERT_ERROR | 33008 | 消息插入失败 |
MSG_DEL_ERROR | 33009 | 消息删除失败 |
CONVER_REMOVE_ERROR | 34001 | 删除会话失败 |
CONVER_GETLIST_ERROR | 34002 | 获取历史消息失败 |
CONVER_SETOP_ERROR | 34003 | 会话置顶异常 |
CONVER_TOTAL_UNREAD_ERROR | 34004 | 获取会话未读消息总数失败 |
CONVER_TYPE_UNREAD_ERROR | 34005 | 获取指定会话类型未读消息数异常 |
CONVER_ID_TYPE_UNREAD_ERROR | 34006 | 获取指定用户 ID & 会话类型未读消息数异常 |
CONVER_CLEAR_ERROR | 34007 | 清除会话失败 |
CLEAR_HIS_ERROR | 34010 | 清除历史消息失败 |
CLEAR_HIS_TYPE_ERROR | 34008 | 清除指定会话类型历史消息失败 |
CLEAR_HIS_TIME_ERROR | 34011 | 清除指定时间段历史消息失败 |
CONVER_GET_ERROR | 34009 | 获取会话失败 |
GROUP_SYNC_ERROR | 35001 | 群组异常 |
GROUP_MATCH_ERROR | 35002 | 匹配群信息异常 |
CHATROOM_ID_ISNULL | 36001 | 聊天室 ID 为空 |
CHARTOOM_JOIN_ERROR | 36002 | 聊天室加入失败 |
CHATROOM_HISMESSAGE_ERROR | 36003 | 获取聊天室历史消息失败 |
CHATROOM_KV_NOT_FOUND | 36004 | 聊天室 kv 没有找到 |
BLACK_ADD_ERROR | 37001 | 添加黑名单失败 |
BLACK_GETSTATUS_ERROR | 37002 | 获得指定人员在黑名单中的状态异常 |
BLACK_REMOVE_ERROR | 37003 | 删除黑名单失败 |
DRAF_GET_ERROR | 38001 | 获取草稿失败 |
DRAF_SAVE_ERROR | 38002 | 保存草稿失败 |
DRAF_REMOVE_ERROR | 38003 | 删除草稿失败 |
SUBSCRIBE_ERROR | 39001 | 关注公众号失败 |
COOKIE_ENABLE | 51001 | cookie 被禁用 |
GET_MESSAGE_BY_ID_ERROR | 61001 | 根据消息 ID 获取消息失败 |
HAVNODEVICEID | 24001 | 用户没有登陆 |
DEVICEIDISHAVE | 24002 | 用户已经存在 |
SUCCESS | 0 | 成功 |
FEILD | 24009 | 没有对应的用户或 token |
NULLCHANNELNAME | 24011 | 通道名称为空 |
INTERNALERRROR | 24015 | 服务器内部错误 |
CLOSE_BEFORE_OPEN | 51001 | 在服务开启之前调用关闭操作 |
ALREADY_IN_USE | 51002 | 已经被使用 |
INVALID_CHANNEL_NAME | 51003 | 通道名称无效 |
DELETE_MESSAGE_ID_IS_NULL | 61001 | 删除消息数组长度为 0 |
REMOTE_ENGINE_UN_SUPPORTED | 16 | 对方不支持当前引擎 |
REMOTE_NETWORK_ERROR | 17 | 对方网络错误 |
➣ 本地数据存储
SDK 主要使用两种数据存储方式:
- 一种用于存取 SDK 初始化后在本次会话中需要的即时数据;
- 另一种就是持久化本地数据,不过如果 SDK 运行平台不支持持久化存储的话,会降级为 memory 存储;
临时存储区:memoryStore
一些必要的临时数据:
1 | { |
本地存储区:storageProvider
1. 存储内容
存储一些必要的持久化数据:比如群消息已读状态回执、历史连接过的 appId、sessionId、导航数据等等。
示例:
全部持久化数据:
1 | { |
持久化的导航数据:
1 | { |
2. 存储方式
LocalStorage 存储容器 (优先):现代浏览器支持的本地数据化持久方式,能够方便的对浏览器同一个域 (IP / 域名) 下的本地数据进行操作。对比 Cookie 其提供的存储空间更大,可以达到数 MB 的大小,而且不会自动过期。
UserData 存储容器 (兼容):主要用于 IE 浏览器的兼容,它将某个 dom 节点作为存储容器,支持类似 localStorage 数据存取方式,但是跟 localStorage 不同的是它不是持久化存储。
Memory 存储容器 (兜底):作为一种兜底的存储方式,主要用于内存中直接存储数据,不具有持久化特性,页面刷新后所有数据都会丢失。
七、SDK 接口详细设计
➣ SDK 引入
1 | var RongIMLib = require('../../static/js/RongIMLib-2.5.6.js') // RongIMLib 相对路径 |
➣ 初始化
1 | var appkey = 'kj29chm026yyn' |
➣ 连接和监听
1. 状态监听器
1 | RongIMClient.setConnectionStatusListener({ |
状态 | 说明 | 枚举值 |
---|---|---|
RongIMLib.ConnectionStatus.CONNECTED | 连接成功 | 0 |
RongIMLib.ConnectionStatus.CONNECTING | 连接中 | 1 |
RongIMLib.ConnectionStatus.DISCONNECTED | 链接已断开 | 2 |
RongIMLib.ConnectionStatus.NETWORK_UNAVAILABLE | 网络错误 | 3 |
RongIMLib.ConnectionStatus.CONNECTION_CLOSED | 链接已关闭 | 4 |
RongIMLib.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT | 其他设备登录 (被踢) | 6 |
RongIMLib.ConnectionStatus.WEBSOCKET_UNAVAILABLE WebSocket | 不可用 | 7 |
RongIMLib.ConnectionStatus.DOMAIN_INCORRECT | 域名错误 (需通过开发者后台检查安全域名设置) | 12 |
RongIMLib.ConnectionStatus.REQUEST_NAVI | 正在请求导航 | 201 |
RongIMLib.ConnectionStatus.RESPONSE_NAVI | 请求导航成功 | 202 |
RongIMLib.ConnectionStatus.RESPONSE_NAVI_ERROR | 请求导航失败 | 203 |
RongIMLib.ConnectionStatus.RESPONSE_NAVI_TIMEOUT | 请求导航超时 | 204 |
2. 消息监听器
收到消息时, 将触发消息监听器
1 | RongIMClient.setOnReceiveMessageListener({ |
消息类型 | ObjectName | 类名 | 是否计数 | 是否存储 |
---|---|---|---|---|
文字消息 | RC:TxtMsg | TextMessage | 是 | 是 |
语音消息 | RC:VcMsg | VoiceMessage | 是 | 是 |
高质量语音消息 | RC:HQVCMsg | HQVoiceMessage | 是 | 是 |
图片消息 | RC:ImgMsg | ImageMessage | 是 | 是 |
GIF 图片消息 | RC:GIFMsg | GIFMessage | 是 | 是 |
图文消息 | RC:ImgTextMsg | RichContentMessage | 是 | 是 |
文件消息 | RC:FileMsg | FileMessage | 是 | 是 |
位置消息 | RC:LBSMsg | LocationMessage | 是 | 是 |
小视频消息 | RC:SightMsg | SightMessage | 是 | 是 |
公众服务单图文消息 | RC:PSImgTxtMsg | PublicServiceRichContentMessage | 是 | 是 |
公众服务多图文消息 | RC:PSMultiImgTxtMsg | PublicServiceMultiRichContentMessage | 是 | 是 |
消息状态 | ObjectName | 值 |
---|---|---|
已读 | READ | 0x1 |
已听 | LISTENED | 0x2 |
已下载 | DOWNLOADED | 0x2 |
未读 | UNREAD | 0 |
消息查询类型 | ObjectName | 值 |
---|---|---|
私聊 | qryPMsg | 1 |
讨论组 | qryDMsg | 2 |
群组 | qryGMsg | 3 |
聊天室 | qryCMsg | 4 |
查询离线消息 | pullMsg | 5 |
系统 | qrySMsg | 6 |
用户信息 | userInfo | 14 |
列举会话 | qryCon | 26 |
消息查询类型(专用于PublishMessage) | 值 |
---|---|
系统通知消息 | s_ntf |
聊天消息 | s_msg |
用户状态更新消息 | s_stat |
聊天室消息 | s_cmd |
群组消息 | pp |
群组消息 | pd |
群组消息 | ch |
群组消息 | pc |
3. 连接服务
注意:
- 连接方法必须在执行 初始化 之后调用
- 连接方法必须在设置 状态监听器、 消息监听器 之后调用
- 除初始化、监听以外, 所有方法都必须在 connect 成功之后 再调用
- 默认一个用户只支持一个页面连接, 开通 “多设备消息同步” 即可支持多页面连接
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
token | String | 是 | 用户的唯一标识 | 2.0.0 |
1 | var token = "mKmyKqTSf7aNDinwAFMnz7NXKI3dV3X0+Cd1BOxmtO2pmvsjW2HViWrePIfq0GuTu9jELQqsckv4AhfjCAKgQ=="; |
3. 断开链接
1 | RongIMClient.getInstance().disconnect(); |
4. 重新连接
注意:
- 不传 config 参数, 则为直接重连, 此时 reconnect 方法必须在网络正常的情况下调用
- 传入 config 参数, SDK 内部自动做网络嗅探处理, 当检测到网络正常时, 进行重连
config 参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
auto | Boolean | 否 | 是否自动重连, 默认 false | 2.3.3 |
url | String | 否 | 用于网络嗅探的地址, auto 为 true 时, 此参数必填 | 2.3.3 |
rate | Array | 否 | 网络嗅探频率, 单位为毫秒, auto 为 true 时, 此参数必填 | 2.3.3 |
示例代码:
1 | var callback = { |
5. 获取用户 id
1 | RongIMClient.getInstance().getCurrentUserId(); |
6. 获取连接状态
1 | RongIMClient.getInstance().getCurrentConnectionStatus(); |
➣ 会话
1. 会话数据结构
会话列表由多个会话组成, 单个会话的数据结构如下:
字段名 | 类型 | 说明 |
---|---|---|
conversationType | Number | 会话类型 |
targetId | String | 目标 |
latestMessageId | String | 会话中最后一条消息 Id |
objectName | String | 会话中最后一条消息的消息标识, SDK 内置消息以 “RC:” 开头 |
unreadMessageCount | Number | 当前会话的未读消息数 |
latestMessage | Object | 会话中最后一条消息, 消息结构详见消息数据结构 |
sentStatus | Number | 会话中最后一条消息发送状态 |
sentTime | Number | 会话中最后一条消息服务端的发送时间 |
会话类型说明:
会话类型 | 说明 | 枚举值 |
---|---|---|
RongIMLib.ConversationType.PRIVATE | 单聊 | 1 |
RongIMLib.ConversationType.DISCUSSION | 讨论组 | 2 |
RongIMLib.ConversationType.GROUP | 群聊 | 3 |
RongIMLib.ConversationType.CHATROOM | 聊天室 | 4 |
RongIMLib.ConversationType.CUSTOMER_SERVICE | 客服 | 5 |
RongIMLib.ConversationType.SYSTEM | 系统 | 6 |
RongIMLib.ConversationType.APP_PUBLIC_SERVICE | 公众账号 (默认关注) | 7 |
RongIMLib.ConversationType.PUBLIC_SERVICE | 公众账号 (手动关注) | 7 |
2. 获取会话列表
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
callback | Object | 是 | 回调对象 | 2.2.0 |
callback.onSuccess | Function | 是 | 成功回调 | 2.2.0 |
callback.onError | Function | 是 | 失败回调 | 2.2.0 |
conversationTypes | Array | 是 | 获取的会话类型, 获取所有会话类型传 null | 2.3.3 |
count | Number | 否 | 获取会话数量 | 2.3.3 |
示例代码:
1 | var conversationTypes = [RongIMLib.ConversationType.PRIVATE]; |
3. 获取会话详情
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | Number | 是 | 会话类型 | 2.2.0 |
targetId | String | 是 | 目标 id | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
示例代码:
1 | var conversationType = RongIMLib.ConversationType.PRIVATE; |
4. 删除会话
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | Number | 是 | 会话类型 | 2.2.0 |
targetId | String | 是 | 目标 id | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
1 | var conversationType = RongIMLib.ConversationType.PRIVATE; |
5. 清除会话列表
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
callback | Object | 是 | 回调对象 | 2.3.2 |
conversationTypes | Array | 否 | 清除的会话类型, 不填则清除所有会话 | 2.3.2 |
1 | var conversationTypes = [RongIMLib.ConversationType.PRIVATE, RongIMLib.ConversationType.GROUP]; |
➣ 消息
1. 消息发送
注意事项:
- 发送消息必须在成功连接服务器 connect 成功之后进行
- 每秒最多发送 5 条消息
- 群定向消息不存储在历史消息中
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | Number | 是 | 会话类型 | 2.2.0 |
targetId | String | 是 | 目标 id | 2.2.0 |
msg | Object | 是 | 消息 | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
isMentioned | Boolean | 否 | 是否为 @ 消息 | 2.2.0 |
pushContent | String | 否 | Push 显示内容 | 2.2.0 |
pushData | String | 否 | Push 通知时附加信息 | 2.2.0 |
methodType | String | 否 | 已废弃 | 2.2.0 |
config | Object | 否 | 其他设置项 | 2.5.3 |
config 说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
userIds | Array | 否 | 接收定向消息的用户 id | 2.2.0 |
isStatus | Boolean | 否 | 是否发送状态消息 | 2.5.6 |
发送消息示例:
1 | var isMentioned = true; // @ 消息 |
1)文本消息
1 | var msg = new RongIMLib.TextMessage({content: 'hello RongCloud!', extra: '附加信息'}); |
2)发送撤回消息
1 | // recallMessage 为需要撤回的消息对象 |
3)发送已读通知消息
1 | /* 以下 3 个属性在会话的最后一条消息中可以获得 */ |
2. 未读消息
注意事项:
- 会话未读数指某一个会话中未读消息的数量
- 未读消息数也可通过 conversation.unreadMessageCount 获取
- 会话消息未读数存储在 WebStorage 中, 若浏览器不支持或禁用 WebStorage,未读消息数将不会保存,浏览器页面刷新未读消息数将不会存在
1)清除指定会话未读数:
参数说明
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | Number | 是 | 会话类型 | 2.2.0 |
targetId | String | 是 | 目标 id | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
代码示例
1 | var conversationType = RongIMLib.ConversationType.PRIVATE; |
2)获取所有会话未读数
代码示例:
1 | RongIMClient.getInstance().getTotalUnreadCount({ |
3)获取指定会话未读数
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | Number | 是 | 会话类型 | 2.2.0 |
targetId | String | 是 | 目标 id | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
代码示例:
1 | var conversationType = RongIMLib.ConversationType.PRIVATE; |
4)获取指定会话类型总未读数
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationTypes | Array | 是 | 指定会话类型 | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
代码示例:
1 | var conversationTypes = [RongIMLib.ConversationType.PRIVATE, RongIMLib.ConversationType.DISCUSSION]; |
3. 历史消息
每个会话都存在历史消息,通常我们使用 SDK 连接服务器时,SDK 只会向用户推送最新的消息,历史消息需要开发者自己调用接口按照规则获取。
1)拉取历史消息
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | ConversationType | 是 | 指定会话类型 | 2.2.0 |
targetId | string | 是 | 目标ID | 2.2.0 |
dateTime | Date | 是 | 时间戳 | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
代码示例:
1 | var params = { |
2)清除历史消息
参数说明:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
conversationType | ConversationType | 是 | 指定会话类型 | 2.2.0 |
targetId | string | 是 | 目标ID | 2.2.0 |
dateTime | Date | 是 | 时间戳 | 2.2.0 |
callback | Object | 是 | 回调对象 | 2.2.0 |
代码示例:
1 | /** |
➣ 用户信息相关操作
说明:对于 interface over http
标识的接口,SDK 会向应用服务器发送请求,无任何标识的接口,会向消息服务器发送请求。
1. 用户登录
interface over http
开发者可以调用此方法登录一个账户,需要输入邮箱 / 手机号和密码进行登录验证。
SDK 会向应用服务器发送 HTTP 请求验证用户账户和密码,如果验证通过则会返回用户基本身份信息。
参数 - params:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
account | String | 是 | 用户邮箱或手机号 | 2.5.6 |
password | String | 是 | 密码 | 2.5.6 |
响应数据 - user:
参数 | 类型 | 存在 | 说明 | 最低版本 |
---|---|---|---|---|
account | String | 是 | 用户邮箱或手机号 | 2.5.6 |
token | String | 是 | 用户通信凭证 | 2.5.6 |
userId | String | 是 | 用户 ID | 2.5.6 |
示例代码:
1 | RongIMClient.getInstance().login(params, { |
2. 用户信息获取 - 通过 Socket 消息体
发消息时可以携带用户信息,以供接收方读取:
1)获取当前用户 (也就是发送者) 的用户信息
2)发消息时携带当前用户信息
3)展示消息时, 通过消息体内的用户信息进行展示
以文本消息为例:
1 | var msg = new RongIMLib.TextMessage({ |
3. 用户信息获取 - 通过 HTTP 请求
interface over http
开发者可以调用此方法查询 userId 对应的用户信息,SDK 会向应用服务器发送 HTTP 请求获取用户信息:
示例代码:
1 | RongIMClient.getInstance().getUserInfo(userId, { |
4. 用户修改资料
interface over http
开发者可以调用此方法修改账户信息,目前可修改性别、昵称、邮箱、手机号、密码。
SDK 会向应用服务器发送 HTTP 执行资料更改操作。
参数 - params:
参数 | 类型 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|
userId | String | 是 | 用户 Id | 2.5.6 |
account | String | 可选 | 账户 | 2.5.6 |
password | String | 可选 | 密码 | 2.5.6 |
sex | String | 可选 | 性别 | 2.5.6 |
name | String | 可选 | 昵称 | 2.5.6 |
响应数据 - user:
参数 | 类型 | 存在 | 说明 | 最低版本 |
---|---|---|---|---|
userId | String | 是 | 用户 Id | 2.5.6 |
account | String | 是 | 账户 | 2.5.6 |
password | String | 是 | 密码 | 2.5.6 |
sex | String | 是 | 性别 | 2.5.6 |
name | String | 是 | 昵称 | 2.5.6 |
示例代码:
1 | RongIMClient.getInstance().login(params, { |
5. 退出登录
interface over http
退出登录会清除 Session 和其它数据处理操作。
1 | RongIMClient.getInstance().logout(); |
6. 注销账户
interface over http
注销账户会删除账户数据。
1 | RongIMClient.getInstance().revoked(); |