在一个自然日内主动或被动使用过即时通讯服务的用户会计入当日活跃用户。 一般而言,活跃用户主要包括在一个自然日内客户端和服务器建立过一次长连接的用户。 除此以外,还包括在一个自然日内有其他用户发来消息、被其他用户加入或踢出会话的用户。 同一用户在多个设备登录,算一个活跃用户,但同时登录的设备超过合理的数量,会被计为不同的用户。 如果开发者希望控制成本,可以在业务需求允许的前提下,在适当的时机建立和关闭长连接。 比如,使用即时通讯实现一次性的客服沟通的应用,可以在终端用户初次发起对话时再建立长连接,而不是在用户打开应用时立即建立长连接。 当然,这也取决于应用具体的需求,由于接收消息也需要维持长连接,如果希望终端用户随时能接受到消息,就应该在打开应用时建立长连接,而不是等到用户初次发消息时再建。
即时通讯的错误码会以 SDK 异常或 WebSocket 关闭状态码的形式返回给客户端。当出现异常情况时,SDK 会输出状态码到日志里,以下是对部分状态码的简单说明:
0
(无)
1006
4100
APP_NOT_AVAILABLE
4101
DUPLICATED_LOGIN
4102
SIGNATURE_FAILED
4103
INVALID_LOGIN
4105
SESSION_REQUIRED
4106
BLACKLISTED
4107
READ_TIMEOUT
4108
LOGIN_TIMEOUT
4109
FRAME_TOO_LONG
4110
INVALID_ORIGIN
4111
SESSION_CONFLICT
4112
SESSION_TOKEN_EXPIRED
4113
APP_QUOTA_EXCEEDED
4114
UNPARSEABLE_RAW_MESSAGE
4115
KICKED_BY_APP
4116
MESSAGE_SENT_QUOTA_EXCEEDED
4117
UNBIND_INSTALLATION_FAILED
4200
INTERNAL_ERROR
4201
SEND_MESSAGE_TIMEOUT
4300
CONVERSATION_INTERNAL_ERROR
4301
CONVERSATION_API_FAILED
4302
CONVERSATION_SIGNATURE_FAILED
4303
CONVERSATION_NOT_FOUND
4304
CONVERSATION_FULL
4305
CONVERSATION_REJECTED_BY_APP
4306
CONVERSATION_UPDATE_FAILED
4307
CONVERSATION_READ_ONLY
4308
CONVERSATION_NOT_ALLOWED
4309
CONVERSATION_UPDATE_REJECTED
4310
CONVERSATION_QUERY_FAILED
4311
CONVERSATION_LOG_FAILED
4312
CONVERSATION_LOG_REJECTED
4313
SYSTEM_CONVERSATION_REQUIRED
4314
NORMAL_CONVERSATION_REQUIRED
4315
CONVERSATION_TEMPORARY_BLACKLISTED
4316
TRANSIENT_CONVERSATION_REQUIRED
4317
CONVERSATION_MEMBERSHIP_REQUIRED
4318
CONVERSATION_API_QUOTA_EXCEEDED
4320
CONVERSATION_OPERATION_UNAUTHORIZED
4321
UNKNOWN_CONVERSATION_ROLE
4322
CONVERSATION_MEMBER_IN_ROLE_FULL
4323
TEMPORARY_CONVERSATION_EXPIRED
4324
CONVERSATION_NEED_OWNER
4325
CONVERSATION_MEMBER_INFO_FEATURE_DISABLED
4401
INVALID_MESSAGING_TARGET
4402
MESSAGE_REJECTED_BY_APP
4403
MESSAGE_OWNERSHIP_REQUIRED
4404
MESSAGE_NOT_FOUND
4405
MESSAGE_UPDATE_REJECTED_BY_APP
4406
MESSAGE_EDIT_DISABLED
4407
MESSAGE_RECALL_DISABLED
4408
MESSAGE_MODIFIED_BY_CENSORSHIP
4543
BLACKLIST_FULL
4544
BLACKLIST_FEATURE_DISABLED
4546
BLACKLIST_SIGNATURE_FAILED
4548
BLOCKED_BY_CONV
4561
SILIENCED_MEMBER_LIST_FULL
4563
SILIENCED
对于普通对话的新消息,LeanCloud 即时通讯服务有选项支持将消息以 Push Notification 的方式通知当前不在线的成员,但是有时候,这种推送会非常频繁对用户造成干扰。LeanCloud 提供选项,支持让单个用户关闭特定对话的离线消息推送。具体可以参考 消息免打扰 文档。
LeanCloud 即时通讯服务是完全独立的即时通讯业务抽象,专注在即时通讯本身,所以即时通讯的业务逻辑中,并不含有好友关系,以及对应的聊天用户数据信息(如头像、名称等)。即时通讯与其他业务逻辑完全隔离,不耦合,唯一关联的就是 clientId。这样做的好处是显而易见的,比如你可以很容易让匿名用户直接通信,你也可以自定义一些好友逻辑,总之可以做成因为任意逻辑而匹配产生的聊天行为。
当然,如果你想维护一套好友关系,完全可以使用你自己的逻辑,只要存储着每个用户在即时通讯中的 clientId 即可。我们推荐使用 LeanCloud 的存储,即 LeanStorage,这样可以结合 LeanCloud 中的 User 相关对象来简单地实现账户系统,以及与之相关的存储,详情可以阅读对应的 SDK 开发指南。
一个对话的消息记录会在云端保留 6 个月,也就是说一个对话可以查询到半年之内的历史消息记录。开发者可以付费来延长这一期限,请联系 leancloud-support@xd.com。你也随时可以通过 REST API 将聊天记录同步到自己的服务器上。
当出现聊天消息没有收到的情况,你可以按照以下思路排查:
ack-at
消息与日志
首先请参考 聊天消息没有收到 一节内容查看聊天消息是否有正常送达服务器。
其次请利用控制台即时消息页的用户状态查询页面来确认消息接收者是否真的处于离线状态,是否有未读消息产生,是否在 _Installation 表内有关联的设备,如下图所示。如果接收者处于在线状态能正常接收消息则不会有未读消息计数,也不会触发推送,请先让接收者离线后再测试离线消息推送。如果用户在 _Installation 表内没有关联的设备则也无法触发推送,对于 iOS 设备请确认接收者设备是否有正常从 APNs 申请到 Device Token,是否有正常存储设备记录在 _Installation 表中,对于 Android 设备请确认是否开启了混合推送,是否正常存储了设备记录在 _Installation 表中。
_Installation
接着请参考离线推送通知一节内容确认您应用是否有配置默认的推送内容,或是否有通过云引擎 Hook 、消息附件方式为期望产生离线推送的消息动态设置了离线消息推送内容。没有设置离线消息推送内容也无法触发离线消息推送。
之后请在 控制台 > 推送 > 在线发送 页面尝试给接收者用户 Client ID 在 _Installation 表关联的设备单独发推送,查看推送是否能收到。可以通过推送记录查看是否有错误产生。如看到 Invalid Token 计数非 0 表示目标 iOS 设备的 Device Token 过期或 Device Token 和推送使用的证书不匹配或目标 Device Token 和推送使用的环境不匹配。请尝试切换推送证书,确认目标 Device Token 是 Production 环境还是 Development 环境后再重新推送,不匹配的证书或不匹配的推送环境均会导致推送失败。如何切换离线推送通知的证书请参考 离线推送通知
Invalid Token
检查方法总结如下:
可能会,取决于证书的时间。当错误的系统时间和当前时间的误差,大于证书的有效时间,就会导致 SSL 握手失败,进而让即时通讯服务整体不可用。
不需要重复创建。我们推荐的方式是开发者可以用自定义属性来实现对私聊和群聊的标识,并且在进行私聊之前,需要查询当前两个参与对话的 ClientId 是否之前已经存在一个私聊的对话了。另外,SDK 已经提供了创建唯一对话的接口,请查看 创建对话。
可以。目前聊天记录从属关系是属于对话的,也就是说,只要对话 Id 不变,不论人员如何变动,只要这个对话产生的聊天记录,当前成员都可以获取。
LeanCloud 云引擎提供了托管 Python 和 Node.js 运行的方式,开发者可以用这两种语言按照签名的算法实现签名,完全可以支持开发者的自定义权限控制。
LeanCloud 有消息优先级的概念,当某个用户连接因为消息过多出现阻塞写入缓慢的情况下,用户可以考虑指定消息优先级,低优先级消息在堵塞时我们会丢弃,高优先级消息则永久排队等待下发。默认情况下消息都是高优先级。 此功能仅针对聊天室消息有效。使用指南参考:消息等级。
SDK 层面不区分单聊和群聊。可以使用会话的成员数量做区分。「会话成员数量为 2」即是单聊,大于 2 即可看作群聊。
在即时通信服务中,SDK 没有提供删除会话的方法。理论上使用存储 SDK 或 REST API 能够做到删除会话记录,也就是删除 _Conversation 表数据。但是如果用户删除了某条 conversation 记录,这个会话中的其他成员也会受到影响,所以不建议直接删除会话。
在即时通信中,可以使用 用户主动退出对话 或者 将他人踢出对话 来实现类似删除会话的需求。
单独发送的消息只能一个一个的撤回。如果是使用订阅消息方式发送的消息可以一次撤回所有人的消息。
订阅消息发送方式接口参考文档:给所有订阅者发消息。
从 Objective-C SDK v6.0.0、Android SDK v4.4.0、JavaScript SDK v3.5.0 开始,我们支持了新的修改与撤回消息功能。 修改或撤回消息后,即使已经收到并已缓存在客户端的消息也会被修改或撤回。 对于老版本的 SDK,仅能修改或撤回服务器端的消息记录,并不能修改或撤回客户端已缓存的消息记录。
我们提供了 客户端上下线 Hook,开发者可以利用这两个 Hook 函数,结合云缓存来完成一组客户端实时状态查询的 endpoint。
_clientOnline 客户端上线,客户端登录成功后调用。
_clientOffline 客户端下线,客户端登出成功或意外下线后调用。
具体实现步骤是通过 Hook 拿到 clientId 的在线状态,将这些状态存储到 LeanCache 中。客户端定期查询云函数来获得用户的在线状态。具体可以参考文档:即时通讯中的在线状态查询。
使用 REST API 发送即时通信消息也是收费的。计费标准就是 API 调用费用标准(每万次 1.0 元)。 此项计费在控制台 > 财务 > 消费明细中对应扣费服务项目是:「数据存储(API 请求)」。
在 Android 环境下,我们是通过一个后台服务来保持客户端与即时通讯云端的长链接的,但是从 Android 8.0 之后系统收紧了权限,会很快中止进入后台的应用的所有后台网络活动(也就是切断所有网络连接),这样就会导致即时通讯 SDK 依赖的网络连接中断。
我们的 SDK 会在网络恢复的时候尝试自动重建连接,但是这需要一定的时间,应用层可以通过 AVIMClientEventHandler 接口来监听网络状态变化,具体可参考文档:客户端事件与网络状态响应。
对于 Android 应用来说,网络的变化是非常常见的,开发者要注意监听这些状态变化,不能假定网络是一直可用的。
另外需要注意在纯 Java 环境下使用即时通讯的功能,需要先手动建立连接(startConnection),详见文档:Java 平台初始化代码。
目前公有云不支持单个会话里单个成员的未读数超过 100。 通常来说,客户端的 UI 界面也不需要精确展示超过 100 的未读数,一般的处理方式是显示 99+。
99+
然后消息查询接口是可以根据消息 ID 以及消息时间戳的组合条件查询所有历史消息的,所以它能支持 UI 展示一个会话里的所有消息,不会存在遗漏消息的情况。
即时通讯常见问题
活跃用户的标准是什么?
在一个自然日内主动或被动使用过即时通讯服务的用户会计入当日活跃用户。 一般而言,活跃用户主要包括在一个自然日内客户端和服务器建立过一次长连接的用户。 除此以外,还包括在一个自然日内有其他用户发来消息、被其他用户加入或踢出会话的用户。 同一用户在多个设备登录,算一个活跃用户,但同时登录的设备超过合理的数量,会被计为不同的用户。 如果开发者希望控制成本,可以在业务需求允许的前提下,在适当的时机建立和关闭长连接。 比如,使用即时通讯实现一次性的客服沟通的应用,可以在终端用户初次发起对话时再建立长连接,而不是在用户打开应用时立即建立长连接。 当然,这也取决于应用具体的需求,由于接收消息也需要维持长连接,如果希望终端用户随时能接受到消息,就应该在打开应用时建立长连接,而不是等到用户初次发消息时再建。
即时通讯云端错误码说明
即时通讯的错误码会以 SDK 异常或 WebSocket 关闭状态码的形式返回给客户端。当出现异常情况时,SDK 会输出状态码到日志里,以下是对部分状态码的简单说明:
0(无)1006(无)4100APP_NOT_AVAILABLE4101DUPLICATED_LOGIN4102SIGNATURE_FAILED4103INVALID_LOGIN4105SESSION_REQUIRED4106BLACKLISTED4107READ_TIMEOUT4108LOGIN_TIMEOUT4109FRAME_TOO_LONG4110INVALID_ORIGIN4111SESSION_CONFLICT4112SESSION_TOKEN_EXPIRED4113APP_QUOTA_EXCEEDED4114UNPARSEABLE_RAW_MESSAGE4115KICKED_BY_APP4116MESSAGE_SENT_QUOTA_EXCEEDED4117UNBIND_INSTALLATION_FAILED4200INTERNAL_ERROR4201SEND_MESSAGE_TIMEOUT4300CONVERSATION_INTERNAL_ERROR4301CONVERSATION_API_FAILED4302CONVERSATION_SIGNATURE_FAILED4303CONVERSATION_NOT_FOUND4304CONVERSATION_FULL4305CONVERSATION_REJECTED_BY_APP4306CONVERSATION_UPDATE_FAILED4307CONVERSATION_READ_ONLY4308CONVERSATION_NOT_ALLOWED4309CONVERSATION_UPDATE_REJECTED4310CONVERSATION_QUERY_FAILED4311CONVERSATION_LOG_FAILED4312CONVERSATION_LOG_REJECTED4313SYSTEM_CONVERSATION_REQUIRED4314NORMAL_CONVERSATION_REQUIRED4315CONVERSATION_TEMPORARY_BLACKLISTED4316TRANSIENT_CONVERSATION_REQUIRED4317CONVERSATION_MEMBERSHIP_REQUIRED4318CONVERSATION_API_QUOTA_EXCEEDED4320CONVERSATION_OPERATION_UNAUTHORIZED4321UNKNOWN_CONVERSATION_ROLE4322CONVERSATION_MEMBER_IN_ROLE_FULL4323TEMPORARY_CONVERSATION_EXPIRED4324CONVERSATION_NEED_OWNER4325CONVERSATION_MEMBER_INFO_FEATURE_DISABLED4401INVALID_MESSAGING_TARGET4402MESSAGE_REJECTED_BY_APP4403MESSAGE_OWNERSHIP_REQUIRED4404MESSAGE_NOT_FOUND4405MESSAGE_UPDATE_REJECTED_BY_APP4406MESSAGE_EDIT_DISABLED4407MESSAGE_RECALL_DISABLED4408MESSAGE_MODIFIED_BY_CENSORSHIP4543BLACKLIST_FULL4544BLACKLIST_FEATURE_DISABLED4546BLACKLIST_SIGNATURE_FAILED4548BLOCKED_BY_CONV4561SILIENCED_MEMBER_LIST_FULL4563SILIENCED要让单个群组消息进入「免打扰模式」,该如何做
对于普通对话的新消息,LeanCloud 即时通讯服务有选项支持将消息以 Push Notification 的方式通知当前不在线的成员,但是有时候,这种推送会非常频繁对用户造成干扰。LeanCloud 提供选项,支持让单个用户关闭特定对话的离线消息推送。具体可以参考 消息免打扰 文档。
聊天好友关系如何实现
LeanCloud 即时通讯服务是完全独立的即时通讯业务抽象,专注在即时通讯本身,所以即时通讯的业务逻辑中,并不含有好友关系,以及对应的聊天用户数据信息(如头像、名称等)。即时通讯与其他业务逻辑完全隔离,不耦合,唯一关联的就是 clientId。这样做的好处是显而易见的,比如你可以很容易让匿名用户直接通信,你也可以自定义一些好友逻辑,总之可以做成因为任意逻辑而匹配产生的聊天行为。
当然,如果你想维护一套好友关系,完全可以使用你自己的逻辑,只要存储着每个用户在即时通讯中的 clientId 即可。我们推荐使用 LeanCloud 的存储,即 LeanStorage,这样可以结合 LeanCloud 中的 User 相关对象来简单地实现账户系统,以及与之相关的存储,详情可以阅读对应的 SDK 开发指南。
聊天记录的保存时间和条数
一个对话的消息记录会在云端保留 6 个月,也就是说一个对话可以查询到半年之内的历史消息记录。开发者可以付费来延长这一期限,请联系 leancloud-support@xd.com。你也随时可以通过 REST API 将聊天记录同步到自己的服务器上。
聊天消息没有收到,该如何排查
当出现聊天消息没有收到的情况,你可以按照以下思路排查:
ack-at字段判断消息是否到达了客户端消息与日志一栏里选日志,根据消息发送时间查看消息发送日志,看服务器是否有收到消息请求,消息是否有转发记录,转发消息时目标用户是否在线等信息。为什么我收不到离线消息推送
首先请参考 聊天消息没有收到 一节内容查看聊天消息是否有正常送达服务器。
其次请利用控制台即时消息页的用户状态查询页面来确认消息接收者是否真的处于离线状态,是否有未读消息产生,是否在
_Installation表内有关联的设备,如下图所示。如果接收者处于在线状态能正常接收消息则不会有未读消息计数,也不会触发推送,请先让接收者离线后再测试离线消息推送。如果用户在_Installation表内没有关联的设备则也无法触发推送,对于 iOS 设备请确认接收者设备是否有正常从 APNs 申请到 Device Token,是否有正常存储设备记录在_Installation表中,对于 Android 设备请确认是否开启了混合推送,是否正常存储了设备记录在_Installation表中。接着请参考离线推送通知一节内容确认您应用是否有配置默认的推送内容,或是否有通过云引擎 Hook 、消息附件方式为期望产生离线推送的消息动态设置了离线消息推送内容。没有设置离线消息推送内容也无法触发离线消息推送。
之后请在 控制台 > 推送 > 在线发送 页面尝试给接收者用户 Client ID 在
_Installation表关联的设备单独发推送,查看推送是否能收到。可以通过推送记录查看是否有错误产生。如看到Invalid Token计数非 0 表示目标 iOS 设备的 Device Token 过期或 Device Token 和推送使用的证书不匹配或目标 Device Token 和推送使用的环境不匹配。请尝试切换推送证书,确认目标 Device Token 是 Production 环境还是 Development 环境后再重新推送,不匹配的证书或不匹配的推送环境均会导致推送失败。如何切换离线推送通知的证书请参考 离线推送通知检查方法总结如下:
_Installation表中有关联的设备记录Android 设备系统时间不准,会影响即时通讯服务吗?
可能会,取决于证书的时间。当错误的系统时间和当前时间的误差,大于证书的有效时间,就会导致 SSL 握手失败,进而让即时通讯服务整体不可用。
我只想实现两个用户的私聊,是不是每次都得重复创建对话?
不需要重复创建。我们推荐的方式是开发者可以用自定义属性来实现对私聊和群聊的标识,并且在进行私聊之前,需要查询当前两个参与对话的 ClientId 是否之前已经存在一个私聊的对话了。另外,SDK 已经提供了创建唯一对话的接口,请查看 创建对话。
某个成员退出对话之后,再加入,在他离开的这段期间内的产生的聊天记录,他还能获取么?
可以。目前聊天记录从属关系是属于对话的,也就是说,只要对话 Id 不变,不论人员如何变动,只要这个对话产生的聊天记录,当前成员都可以获取。
我自己没有云端,如何实现签名的功能?
LeanCloud 云引擎提供了托管 Python 和 Node.js 运行的方式,开发者可以用这两种语言按照签名的算法实现签名,完全可以支持开发者的自定义权限控制。
即时通信服务中,有些消息类型及时性要求特别高,有些消息及时性要求不高。一个房间内的消息有没有优先级?
LeanCloud 有消息优先级的概念,当某个用户连接因为消息过多出现阻塞写入缓慢的情况下,用户可以考虑指定消息优先级,低优先级消息在堵塞时我们会丢弃,高优先级消息则永久排队等待下发。默认情况下消息都是高优先级。 此功能仅针对聊天室消息有效。使用指南参考:消息等级。
对话查询如何区分单聊还是群聊?
SDK 层面不区分单聊和群聊。可以使用会话的成员数量做区分。「会话成员数量为 2」即是单聊,大于 2 即可看作群聊。
怎么删除或者退出一个会话聊天?
在即时通信服务中,SDK 没有提供删除会话的方法。理论上使用存储 SDK 或 REST API 能够做到删除会话记录,也就是删除 _Conversation 表数据。但是如果用户删除了某条 conversation 记录,这个会话中的其他成员也会受到影响,所以不建议直接删除会话。
在即时通信中,可以使用 用户主动退出对话 或者 将他人踢出对话 来实现类似删除会话的需求。
使用系统会话给用户发的消息支持撤回吗?
单独发送的消息只能一个一个的撤回。如果是使用订阅消息方式发送的消息可以一次撤回所有人的消息。
订阅消息发送方式接口参考文档:给所有订阅者发消息。
撤回消息或修改消息无效
从 Objective-C SDK v6.0.0、Android SDK v4.4.0、JavaScript SDK v3.5.0 开始,我们支持了新的修改与撤回消息功能。 修改或撤回消息后,即使已经收到并已缓存在客户端的消息也会被修改或撤回。 对于老版本的 SDK,仅能修改或撤回服务器端的消息记录,并不能修改或撤回客户端已缓存的消息记录。
即时通信如何获取在线用户列表以及用户的在线时长?
我们提供了 客户端上下线 Hook,开发者可以利用这两个 Hook 函数,结合云缓存来完成一组客户端实时状态查询的 endpoint。
_clientOnline 客户端上线,客户端登录成功后调用。
_clientOffline 客户端下线,客户端登出成功或意外下线后调用。
具体实现步骤是通过 Hook 拿到 clientId 的在线状态,将这些状态存储到 LeanCache 中。客户端定期查询云函数来获得用户的在线状态。具体可以参考文档:即时通讯中的在线状态查询。
使用 REST API 发送实时通信消息收费吗?
使用 REST API 发送即时通信消息也是收费的。计费标准就是 API 调用费用标准(每万次 1.0 元)。 此项计费在控制台 > 财务 > 消费明细中对应扣费服务项目是:「数据存储(API 请求)」。
Android 设备时常报错连接断开:java.lang.IllegalStateException: Connection Lost
在 Android 环境下,我们是通过一个后台服务来保持客户端与即时通讯云端的长链接的,但是从 Android 8.0 之后系统收紧了权限,会很快中止进入后台的应用的所有后台网络活动(也就是切断所有网络连接),这样就会导致即时通讯 SDK 依赖的网络连接中断。
我们的 SDK 会在网络恢复的时候尝试自动重建连接,但是这需要一定的时间,应用层可以通过 AVIMClientEventHandler 接口来监听网络状态变化,具体可参考文档:客户端事件与网络状态响应。
对于 Android 应用来说,网络的变化是非常常见的,开发者要注意监听这些状态变化,不能假定网络是一直可用的。
另外需要注意在纯 Java 环境下使用即时通讯的功能,需要先手动建立连接(startConnection),详见文档:Java 平台初始化代码。
怎么才能取到超过 100 条未读的真实未读条数?
目前公有云不支持单个会话里单个成员的未读数超过 100。 通常来说,客户端的 UI 界面也不需要精确展示超过 100 的未读数,一般的处理方式是显示
99+。然后消息查询接口是可以根据消息 ID 以及消息时间戳的组合条件查询所有历史消息的,所以它能支持 UI 展示一个会话里的所有消息,不会存在遗漏消息的情况。