一,从简单的单聊、群聊、收发图文消息开始
阅读准备
在阅读本章之前,如果你还不太了解即时通讯服务的总体架构,建议先阅读即时通讯服务总览。 另外,如果你还没有下载对应开发环境(语言)的 SDK,请参考相应语言的 SDK 配置指南完成 SDK 安装与初始化:
本章导读
在很多产品里面,都存在让用户实时沟通的需求,例如:
- 员工与客户之间的实时交流,如房地产行业经纪人与客户的沟通,商业产品客服与客户的沟通,等等。
- 企业内部沟通协作,如内部的工作流系统、文档/知识库系统,增加实时互动的方式可能就会让工作效率得到极大提升。
- 直播互动,不论是文体行业的大型电视节目中的观众互动、重大赛事直播,娱乐行业的游戏现场直播、网红直播,还是教育行业的在线课程直播、KOL 知识分享,在支持超大规模用户积极参与的同时,也需要做好内容审核管理。
- 应用内社交,游戏公会嗨聊,等等。社交产品要能长时间吸引住用户,除了实时性之外,还需要更多的创新玩法,对于标准化通讯服务会存在更多的功能扩展需求。
根据功能需求的层次性和技术实现的难易程度不同,我们分为多篇文档来一步步地讲解如何利用即时通讯服务实现不同业务场景需求:
- 本篇文档,我们会从实现简单的单聊/群聊开始,演示创建和加入「对话」、发送和接收富媒体「消息」的流程,同时让大家了解历史消息云端保存与拉取的机制,希望可以满足在成熟产品中快速集成一个简单的聊天页面的需求。
- 离线消息文档会介绍一些特殊消息的处理,例如 @ 成员提醒、撤回和修改、消息送达和被阅读的回执通知等,离线状态下的推送通知和消息同步机制,多设备登录的支持方案,以及如何扩展自定义消息类型,希望可以满足一个社交类产品的多方面需求。
- 权限与聊天室文档会介绍一下系统的安全机制,包括第三方的操作签名,同时也会介绍直播聊天室和临时对话的用法,希望可以帮助开发者提升产品的安全性和易用性,并满足特殊场景的需求。
- Hook 与系统对话文档会介绍即时通讯服务端 Hook 机制,系统对话的用法,以及给出一个基于这两个功能打造一个属于自己的聊天机器人的方案,希望可以满足业务层多种多样的扩展需求。
希望开发者最终顺利完成产品开发的同时,也对即时通讯服务的体系结构有一个清晰的了解,以便于产品的长期维护和定制化扩展。
一对一单聊
在开始讨论聊天之前,我们需要介绍一下在即时通讯 SDK 中的 IMClient 对象:
IMClient对应实体的是一个用户,它代表着一个用户以客户端的身份登录到了即时通讯的系统。
具体可以参考即时通讯服务总览中《clientId、用户和登录》一节的说明。
创建 IMClient
假设我们产品中有一个叫「Tom」的用户,首先我们在 SDK 中创建出一个与之对应的 IMClient 实例(创建实例前请确保已经成功初始化了 SDK):
- Unity
- Android
- iOS
- JavaScript
LCIMClient tom = new LCIMClient("Tom");
// clientId 为 Tom
LCIMClient tom = LCIMClient.getInstance("Tom");
// 定义一个常驻内存的属性变量
@property (nonatomic) LCIMClient *tom;
// 初始化
NSError *error;
tom = [[LCIMClient alloc] initWithClientId:@"Tom" error:&error];
if (error) {
NSLog(@"init failed with error: %@", error);
} else {
NSLog(@"init succeeded");
}
// Tom 用自己的名字作为 clientId 来登录即时通讯服务
realtime
.createIMClient("Tom")
.then(function (tom) {
// 成功登录
})
.catch(console.error);
注意这里一个 IMClient 实例就代表一个终端用户,我们需要把它全局保存起 来,因为后续该用户在即时通讯上的所有操作都需要直接或者间接使用这个实例。
登录即时通讯服务器
创建好了「Tom」这个用户对应的 IMClient 实例之后,我们接下来需要让该实例「登录」即时通讯服务器。
只有登录成功之后客户端才能开始与其他用户聊天,也才能接收到云端下发的各种事件通知。
这里需要说明一点,有些 SDK(比如 C# SDK)在创建 IMClient 实例的同时会自动进行登录,另一些 SDK(比如 iOS 和 Android SDK)则需要调用开发者手动执行 open 方法进行登录:
- Unity
- Android
- iOS
- JavaScript
await tom.Open();
// Tom 创建了一个 client,用自己的名字作为 clientId 登录
LCIMClient tom = LCIMClient.getInstance("Tom");
// Tom 登录
tom.open(new LCIMClientCallback() {
@Override
public void done(LCIMClient client, LCIMException e) {
if (e == null) {
// 成功打开连接
}
}
});
// 定义一个常驻内存的属性变量
@property (nonatomic) LCIMClient *tom;
// 初始化,然后登录
NSError *error;
tom = [[LCIMClient alloc] initWithClientId:@"Tom" error:&error];
if (error) {
NSLog(@"init failed with error: %@", error);
} else {
[tom openWithCallback:^(BOOL succeeded, NSError * _Nullable error) {
if (succeeded) {
// open succeeded
}
}];
}
// Tom 用自己的名字作为 clientId 登录,并且获取 IMClient 对象实例
realtime
.createIMClient("Tom")
.then(function (tom) {
// 成功登录
})
.catch(console.error);
使用 _User 登录
除了应用层指定 clientId 登录之外,我们也支持直接使用 _User 对象来创建 IMClient 并登录。这种方式能直接利用云端内置的用户鉴权系统而省掉登录签名操作,更方便地将存储和即时通讯这两个模块结合起来使用。示例代码如下:
- Unity
- Android
- iOS
- JavaScript
var user = await LCUser.Login("USER_NAME", "PASSWORD");
var client = new LCIMClient(user);
// 以 LCUser 的用户名和密码登录到存储服务
LCUser.logIn("Tom", "cat!@#123").subscribe(new Observer<LCUser>() {
public void onSubscribe(Disposable disposable) {}
public void onNext(LCUser user) {
// 登录成功,与服务器连接
LCIMClient client = LCIMClient.getInstance(user);
client.open(new LCIMClientCallback() {
@Override
public void done(final LCIMClient avimClient, LCIMException e) {
// 执行其他逻辑
}
});
}
public void onError(Throwable throwable) {
// 登录失败(可能是密码错误)
}
public void onComplete() {}
});
// 定义一个常驻内存的属性变量
@property (nonatomic) LCIMClient *client;
// 登录 User,然后使用登录成功的 User 初始化 Client 并登录
[LCUser logInWithUsernameInBackground:USER_NAME password:PASSWORD block:^(LCUser * _Nullable user, NSError * _Nullable error) {
if (user) {
NSError *err;
client = [[LCIMClient alloc] initWithUser:user error:&err];
if (err) {
NSLog(@"init failed with error: %@", err);
} else {
[client openWithCallback:^(BOOL succeeded, NSError * _Nullable error) {
if (succeeded) {
// open succeeded
}
}];
}
}
}];
var AV = require("leancloud-storage");
// 以 AVUser 的用户 名和密码登录即时通讯服务
AV.User.logIn("username", "password")
.then(function (user) {
return realtime.createIMClient(user);
})
.catch(console.error.bind(console));