# wecloud-im 即时通讯云对接文档


[TOC]

## 使用场景

即时通讯服务现在已经被广泛使用**在应用内社交、工作协同、客服系统、超大型赛事和电视直播、以及游戏状态同步**等多种业务场景之中。

即时通讯是主要**解决产品内聊天**即时通信（Instant Messaging）、实时数据同步等需求，其设计上的主要目标是：

- **支持为现有应用快速加入多种通讯能力**

  我们很多客户的产品都已经达到一个比较稳定的形态，即时通讯只是其中一个锦上添花的功能，所以如何和现有系统无缝集成，是我们设计上一个重要的出发点。即时通讯服务可以在应用账户系统独立的情况下，快速接入并稳定安全地运行。

  我们支持了多种典型通讯场景，提供了UI 库和脚手架来帮助开发者快速接入。并且考虑到业务运行环境，我们提供了支持的 SDK。

   

- **强大的自定义机制满足业务各种扩展需求**

   

  我们默认支持了**文本、图片、音视频、地理位置**等多种类型的消息收发，同时也允许产品开发者来扩展自己的消息类型和 UI 样式。并且在基本功能之外，我们也支持更多高阶需求，例如消息撤回与修改、@ 成员提醒、暂态消息、「已读」回执、离线推送、敏感内容过滤等等与消息收发相关的功能在这里都可以得到满足。

   

- **安全和权限控制**

  我们始终把系统安全性放在首要位置，客户端与云端使用 WebSocket 全双工通讯，全程 TLS 加密传输。在用户登录和操作权限的控制上，我们专门设计了第三方操作签名的机制，让应用层在快速接入的同时，也可以实时、完整地控制用户在即时通讯系统内的所有活动。在群聊和开放聊天室等业务场景里，我们也提供了成员角色管理和黑名单的机制，以满足产品运营管理的多样需求。

   

- **最大限度降低客户的生产运维成本** 

  我们提供专业的技术支持服务，富有经验的资深工程师 7 × 24 小时对接，以帮助开发者快速、有效地完成产品集成，缩短开发周期。此外在产品运营阶段，让开发者彻底摆脱后端系统日常的运维细节和突发的软硬件故障处理，也不用关心用户量和流量的变化。帮助客户在享受高品质技术服务的同时，也可以最大限度降低生产运维成本，并且以更快的速度推进产品迭代，是我们始终追求的目标。

## 功能和特性

即时通讯服务提供的主要功能有：

- **基本聊天功能**，包括：

  - 支持多种聊天场景。除了普通的单聊、群聊

  - 用户之间可以发送多种多样的消息，如文本、图片、语音、音视频、地理位置等，以及更多的应用层自定义消息。

  - 聊天消息自动保存在云端,可选支持永久消息存储，支持各种复杂的查找和翻页方式。
  
    

- **特殊的消息收发需求**。除了普通的消息收发之外，我们还支持：

  - 带有提醒功能的 **@ 消息**（如微信里面的 @ 某人）
  - 消息的 **撤回和修改**
  - 消息送达和对方已读的 **回执通知**
  - 群聊里面为了避免过于干扰，允许用户开启 **消息免打扰** 开关
  - 在消息接收方离线时，自动转为 **系统推送通知,ios和安卓**（Push Notification）
  
    
  
- **安全控制**

  任何终端用户要开启即时通讯服务，只需要提供一个唯一标识自己的 `clientId` 即可,不需要传入任何用户数据,以保证信息不被泄露，这种与产品自有账户系统解耦合的方式，带来了集成的便利，也可以促使通讯服务商专注做好底层的「信使」角色。 同时我们也提供 **第三方鉴权** 的机制，通过在聊天流程中加入开发者服务器签名授权这一环节，来确保通讯操作的安全。 而且，即时通讯 SDK 与云端是 WebSocket 全双工通讯，且全程使用 TLS 安全加密。

- **强大的业务扩展能力**

  对于很多典型的需求，我们提供了默认的实现，而为了支持业务的多样性和特殊性，我们也提供了丰富的扩展机制：

  - 为了和产品自有用户系统进行对接，我们提供了第三方操作鉴权的扩展接口，确保在用户登录、创建/加入/退出对话群组、以及拉取聊天记录时，所有操作都得到了授权。

  - 同时我们还支持开发者对消息传递的过程进行 **hook 处理**，在消息到达云端但是还没有投递之前和投递之后，分别完成自定义的处理逻辑，例如过滤掉竞品的品牌，以及自定义离线推送消息，等等。

  - 我们也支持通过简单的 **web hook** 来完成云端和应用后端的消息同步。

  - 在提供移动端的 SDK 之外，我们还提供了 REST API，以帮助产品在可信环境下更好地实现业务处理。


**我们相信灵活性和扩展性也是云服务的核心竞争力。**




## 核心概念说明

### clientId、用户和登录

即时通讯服务中的每一个终端称为一个「Client」。Client 拥有一个在应用内唯一标识自己的 ID（`clientId`）。这个 ID 由应用自己定义，必须是 **由任意英文字母、数字、半角下划线与半角短横线组成，可以纯数字，不超过 64个字符的字符串组成**。在大部分场合，Client 都可以对应到应用中的某个「用户」，但是并不是只有真的用户才能作为「Client」，你完全可以把一个探测器当成一个「Client」，把它收集到的数据通过即时通讯服务广播给更多「人」。

要使用即时通讯服务，每一个终端设备需要首先建立与即时通讯云服务端的 WebSocket 长连接，并使用唯一的 `clientId` 来加入即时通讯服务，我们把这一过程称为「登录」。请注意这里的登录仅仅指客户端登录即时通讯服务，与应用层面的用户账户注册登录是不一样的。

### appKey与appSecret

appKey为应用在蔚可云的唯一标识, appSecret为安全密钥, 均由蔚可云控制台或联系客服下发,给应用接入方安全验证密钥,生产环境appSecret不建议保存在接入方前端客户端, appKey与appSecret总是成对出现和使用

### 生成sign签名

sign 由接入方后端生成, 进行加签的参数:  MD5{timestamp + clientId + appKey + appSecret} 
timestamp:毫秒时间戳
clientId:由后端生成

**在生产环境中，你需要自行部署服务器签发 sign**

### 对话（Conversation）

用户登录之后，与其他人(client)进行消息沟通，即为开启了一个「对话（Conversation）」。在即时通讯服务中，「对话」包含了沟通的用户群体（成员），也是所有消息依托的媒介：消息都是由某一个 Client 发往一个「对话」。终端用户在开始聊天之前，需要先创建或者加入一个对话，然后再邀请其他人进来（可选），之后所有参与者在这个对话内进行交流。

### 已读/已接收

**已接收**为客户端接收到该消息,已经保存到本地缓存中,用户还未打开会话查看该条消息的情况. 服务器将不会再次下发已接收状态的离线消息,未接收的消息会在拉取离线消息时返回

**已读**为客户端已经查看该消息



## 业务场景的需求


在解释对话类型之前，我们先列举一下即时通讯可能的使用场景。

-  单聊/私聊


就是两个 Client 之间的对话，公开与否（能否让其他人看到这个对话存在）由应用层自己控制。通常的业务场景里它是私密的，并且加入新的成员之后，会切换成新的群聊（当然，也可以依然不离开当前对话，这一点还是由应用层来决定）, 可选两个client之间只能存在一个会话。

- 群聊


就是两个（含）以上 Client 之间的对话，通常可以添加和删除成员，并且会赋予群聊一个名字，例如「家人群」、「朋友群」、「部门同事群」等等。随着成员的减少，群聊也可能只有两个甚至一个成员（成员的多少并不是区分群聊和单聊的关键）。群聊能否公开（譬如支持名字搜索），由应用自己决定。



## 客户端连接流程

![wc_im_client连接流程-Page-1](https://tva1.sinaimg.cn/large/008i3skNly1gvv3zjcuajj30zt0u0div.jpg)

1. appKey, appSecret为蔚可云下发给客户方安全保护密钥,不建议保存在前端客户端;
2. 第三方应用服务端需提供获取sign的接口, sign 由MD5{timestamp + clientId + appKey + appSecret},其中clientId由后端生成;
3. 前端拿到sign后,调用验证sign接口进行获取token;
4. websocket连接初始化需要带上token即可连接成功;
5. sign 需要在你的应用服务端生成;



### 第三方应用后端生成sign接口示例

本文展示如何在服务端部署一个 sign 生成器。

 **java**示例代码 ,供客户应用后端参考

```java
import org.springframework.util.DigestUtils;
private void getSign(String timestamp, String clientId, String appKey, String appSecret) {
  
  String data = timestamp + clientId + appKey + appSecret;
	String sign = DigestUtils.md5DigestAsHex(data.getBytes());
}

public static void main(String[] args)   {
  String clientId = "client_123123";
  String appKey = "elLwpel1gWCHDqZy";
  String appSecret = "68809bb5a9077a83631aeb0b17b5965d6b2302faf2ab3737";
  String timestamp = String.valueOf(new Date().getTime());
  getSign(timestamp, clientId, appKey, appSecret);
}
```

第三方应用后端**必须**要响应给前端的参数:

```json
{
	"timestamp": "1628838135066",
	"clientId": "client_3334444",
	"appKey": "D13ug9jsWbJbeVx1",
	"sign": "c15a886fe4114dba2c8f078369e6bec9"
}
```



