前言
作者一直认为一个优秀的程序员不仅
仅是具有设计架构的能力,更是了解整个行业背景的现状和趋势。为了更好的设计和架构一个大型系统,我们需要调研行业的背景,从一个具象行业抽象出两个核心:领域模型和抽象流程。下面这章会介绍IM系统的 会话 和 消息 两大核心概念, 以及IM系统的基本抽象流程。
会话和消息两大核心概念
笔者认为两个核心的概念是 会话 和 消息 的概念
- 会话session(conversation): 它是指AB通讯之间维持的一种关系,它是消息存储的载体。
- 消息message: 可以根据业务分为两大块消息,会话内消息和系统通知消息。会话内消息又可以分为基本消息和自定义消息。
会话
即时通讯 SDK 的核心概念「会话」,即 Conversation。我们将单聊和群聊(包括聊天室)的消息发送和接收都依托于 Conversation 这个统一的概念进行操作。如下是定义会话数据结构
会话数据格式
会话属性 | 备注 |
---|---|
id | 会话ID |
scene | 场景 |
to | 聊天对象,账号或者群ID |
updateTime | 会话更新时间 |
unread | 未读数 |
lastMsg | 此会话的最后一条消息 |
custom | 扩展Json字符串 |
消息
IM SDK内的消息可以分为两类:会话内消息和系统消息。
会话内消息
会话内消息只能出现并展示在聊天界面里,一般是应用内的一个用户发给另一个用户(或群组/聊天室)的消息,例如文本消息、图片消息都属于会话内消息。:
会话内消息类型 | 备注 |
---|---|
文本消息 | 消息内容为普通文本 |
图片消息 | 消息内容为图片URL地址、尺寸、图片大小等信息 |
语音消息 | 消息内容为语音URL地址、时长、大小、格式等信息 |
视频消息 | 消息内容为视频文件的URL地址、时长、大小、格式等信息 |
文件消息 | 消息内容为文件的URL地址、大小、格式等信息,格式不限 |
地理位置消息 | 消息内容为地理位置标题、经度、纬度信息 |
通知消息 | 自定义消息可以用于消息接入扩展。 例如卡片消息,红包消息等。 |
自定义消息 | 通知消息属于会话内 的一种消息,用于会话内通知和提示场景。例如:群名称更新、某某某退出了群聊等。 |
消息数据格式
属性 | 类型 | 描述 |
---|---|---|
conversationId | String | |
id | String | 服务器用于区分消息用的ID |
idClient | String | SDK生成的消息id, 在发送消息之后会返回给开发者, 开发者可以在发送消息的结果回调里面根据这个ID来判断相应消息的发送状态, 到底是发送成功了还是发送失败了, 然后根据此状态来更新页面的UI。如果发送失败, 那么可以重新发送此消息 |
scene | String | 群聊还是单聊场景 |
type | String | 消息类型:文本,图片,语音,还是自定义消息 |
from | String | 消息发送方 |
to | String | 消息接收方 |
time | Number | 消息时间戳 |
flow | String | 消息的流向 'in' 表示此消息是收到的消息,'out' 表示此消息是发出的消息 |
status | String | 消息发送状态 'sending' : 发送中, 'success' : 发送成功,'fail' : 发送失败 |
content | String | 文本消息的文本内容, 请参考发送文本消息 |
file | Object | 文件消息的文件对象, 具体字段请参考 |
resend | Boolean | 是否是重发的消息 |
custom | String | 扩展字段, 用途扩展自定消息 |
系统消息
系统通知一般在会话维度,通常用于验证关系。例如:某某某请求加你为好友,群名称更新、某某某退出了群聊等
系统消息数据格式
属性 | 类型 | 说明 |
---|---|---|
msgId | String | 消息Id |
time | Number | 时间戳 |
type | String | 系统通知类型 |
from | String | 系统通知的来源, 账号或者群ID |
to | String | 系统通知的目标, 账号或者群ID |
scene | String | 自定义系系统通知的场景, 参考[消息场景] |
content | String | 文本消息内容 |
custom | String | 自定义消息内容,JSONString 格式 |
基本流程
IM即时通讯是不同用户之间交流的双通通道,如下是收发消息的简单模型。
客户端A向客户端B发消息,内部流程应该如下:
- 客户端A和客户端B都先和服务端建立 WebSocket的长连接。
- 客户端A通过Websocket向服务端发送了文本消息,同时服务端向客户端A发送一个ACK回包,说明收到此消息。
- 服务分析此消息的来源和去向,消息类型和内容。通过 Websocket 投递给客户端B
- 客户端B收到消息的回调。完成一次客户端A向客户端B发消息的流程
IM系统一次完整用户建立连接的流程图:
- 输入用户基本信息,如果有固定秘钥或者
Token
则初始化信息,建立连接。 - 没有
Token
则通过https
根据用户信息往服务端申请Token
- 返回
Token
及用户信息,通过Websocket
和服务端建立长连接 - 建立长连接之后,可以和服务端进行 收发消息 的通信。
基本功能
单聊
1V1 聊天,提供包括文字、图片、语音、地理位置、文件、自定义消息等多种能力,除此之外还提供消息推送功能
群聊
多人聊天服务,内置公开群、私有群、聊天室、互动直播聊天室和在线成员广播大群五种群组形态,能够适应各种群组需求的场景。
云信IM服务实践
文章上文介绍了IM的基本概念、基本流程和基本功能。下面以云信IM服务为例,我们来实战的看下如何使用IM服务。
建立云信账号
我们创建云信账号,并且创建掘金小册应用,建立应用会提供Appkey 和 AppSecret 用于后续服务认证。同时建立两个测试账号 Bob 和 Alice。操作视频如下:
如何获取建连Token
具体建立连接的API接口和传参可以参考云信文档。
import { APP_CONFIG } from './constant';
import { _ } from 'tbms-util';
import sha1 from 'sha1';
const getAccountToken = async (accid: string) => {
const time =Math.round(+new Date() / 1000);
const hash = _.md5(accid);
const response:any = await fetch('https://api.netease.im/nimserver/user/refreshToken.action', {
method: 'POST',
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"AppKey": APP_CONFIG.appkey,
"Nonce": hash,
"CurTime": '' + time,
"CheckSum": sha1(APP_CONFIG.appSecret + hash + time)
},
body: `accid=${accid}`
});
return response.json();
}
接口解释:分装了一个获取账号Token的 Async Await 的 Fetch 方法, 根据参数要求获取连接 Token。
结语
本章节介绍了IM的两个核心概念:消息 和 会话,以及IM系统的基本抽象流程,作为基础知识概念,对于后续的架构设计和系统开发有很大的帮助。
下一章会加如何设计架构IM系统,如何进行系统分层。下一章介绍的系统分层方式也是常见的前端设计架构方案。