# 前言

作者一直认为一个优秀的程序员不仅 仅是具有设计架构的能力,更是了解整个行业背景的现状和趋势。为了更好的设计和架构一个大型系统,我们需要调研行业的背景,从一个具象行业抽象出两个核心:领域模型和抽象流程。下面这章会介绍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即时通讯是不同用户之间交流的双通通道,如下是收发消息的简单模型。

image-20210214203248788

客户端A向客户端B发消息,内部流程应该如下:

  1. 客户端A和客户端B都先和服务端建立 WebSocket的长连接。
  2. 客户端A通过Websocket向服务端发送了文本消息,同时服务端向客户端A发送一个ACK回包,说明收到此消息。
  3. 服务分析此消息的来源和去向,消息类型和内容。通过 Websocket 投递给客户端B
  4. 客户端B收到消息的回调。完成一次客户端A向客户端B发消息的流程

IM系统一次完整用户建立连接的流程图:

  1. 输入用户基本信息,如果有固定秘钥或者 Token 则初始化信息,建立连接。
  2. 没有 Token 则通过 https 根据用户信息往服务端申请 Token
  3. 返回 Token 及用户信息,通过 Websocket 和服务端建立长连接
  4. 建立长连接之后,可以和服务端进行 收发消息 的通信。

# 基本功能

# 单聊

1V1 聊天,提供包括文字、图片、语音、地理位置、文件、自定义消息等多种能力,除此之外还提供消息推送功能

# 群聊

多人聊天服务,内置公开群、私有群、聊天室、互动直播聊天室和在线成员广播大群五种群组形态,能够适应各种群组需求的场景。

# 云信IM服务实践

文章上文介绍了IM的基本概念、基本流程和基本功能。下面以云信IM服务为例,我们来实战的看下如何使用IM服务。

# 建立云信账号

我们创建云信账号,并且创建掘金小册应用,建立应用会提供Appkey 和 AppSecret 用于后续服务认证。同时建立两个测试账号 Bob 和 Alice。操作视频如下:

# 如何获取建连Token

具体建立连接的API接口和传参可以参考云信文档。

[dev.yunxin.163.com/docs/produc…](https://dev.yunxin.163.com/docs/product/IM即时通讯/服务端API文档/网易云通信ID?kw=refresh Token.action&pg=1&pid=0&#网易云通信ID更新)

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系统,如何进行系统分层。下一章介绍的系统分层方式也是常见的前端设计架构方案。

阅读全文