即时通讯常见问题
活跃用户的标准是什么?
在一个自然日内主动或被动使用过即时通讯服务的用户会计入当日活跃用户。 一般而言,活跃用户主要包括在一个自然日内客户端和服务器建立过一次长连接的用户。 除此以外,还包括在一个自然日内有其他用户发来消息、被其他用户加入或踢出会话的用户。 同一用户在多个设备登录,算一个活跃用户,但同时登录的设备超过合理的数量,会被计为不同的用户。 如果开发者希望控制成本,可以在业务需求允许的前提下,在适当的时机建立和关闭长连接。 比如,使用即时通讯实现一次性的客服沟通的应用,可以在终端用户初次发起对话时再建立长连接,而不是在用户打开应用时立即建立长连接。 当然,这也取决于应用具体的需求,由于接收消息也需要维持长连接,如果希望终端用户随时能接受到消息,就应该在打开应用时建立长连接,而不是等到用户初次发消息时再建。
即时通讯云端错误码说明
即时通讯的错误码会以 SDK 异常或 WebSocket 关闭状态码的形式返回给客户端。当出现异常情况时,SDK 会输出状态码到日志里,以下是对部分状态码的简单说明:
代码 | 消息 | 说明 |
---|---|---|
0 | (无) | WebSocket 正常关闭,可能发生在服务器重启,或本地网络异常的情况。SDK 会自动重连,无需人工干预。 |
1006 | (无) | WebSocket 连接非正常关闭,通常见于路由器配置对长连接限制的情况。SDK 会自动重连,无需人工干预。 |
4100 | APP_NOT_AVAILABLE | 应用不存在或应用禁用了即时通讯服务。 |
4101 | DUPLICATED_LOGIN | 同一个设备重复登录推送服务。该错误码与即时通讯服务无关。 |
4102 | SIGNATURE_FAILED | 登录签名验证失败。 |
4103 | INVALID_LOGIN | Client Id 格式错误,超过 64 个字符。 |
4105 | SESSION_REQUIRED | Session 没有打开就发送消息,或执行其他操作。常见的错误场景是调用 open session 后直接发送消息,正确的用法是在 Session 打开的回调里执行。 |
4106 | BLACKLISTED | 用户被列入黑名单,禁止使用即时通讯服务。 |
4107 | READ_TIMEOUT | 读超时,云端长时间没有收到客户端的数据,切断连接。SDK 包装了心跳包的机制,出现此错误通常是网络问题。SDK 会自动重连,无需人工干预。 |
4108 | LOGIN_TIMEOUT | 登录超时,连接后长时间没有完成 session open。通常是登录被拒绝等原因,出现此问题可能是使用方式有误,可以 创建工单,由我们技术顾问来给出建议。 |
4109 | FRAME_TOO_LONG | 包过长。消息大小超过 5 KB,请缩短消息或者拆分消息。 |
4110 | INVALID_ORIGIN | 设置安全域名后,当前登录的域名与安全域名不符合。 |
4111 | SESSION_CONFLICT | 设置单设备登录后,客户端被其他设备挤下线。 |
4112 | SESSION_TOKEN_EXPIRED | Session 过期,请重新登录。 |
4113 | APP_QUOTA_EXCEEDED | 应用容量超限,当天登录用户数已经超过应用设定的最大值。 |
4114 | UNPARSEABLE_RAW_MESSAGE | 客户端发送的序列化数据服务器端无法解析。 |
4115 | KICKED_BY_APP | 客户端被 REST API 管理接口强制下线。 |
4116 | MESSAGE_SENT_QUOTA_EXCEEDED | 应用单位时间内发送消息量超过限制,消息被拒绝。 |
4117 | UNBIND_INSTALLATION_FAILED | 通过即时通讯 SDK 退出登录时,解除退出登录用户与当前使用设备的绑定关系失败。 |
4200 | INTERNAL_ERROR | 服务器内部错误,如果反复出现请收集相关线索并 创建工单,我们会尽快解决。 |
4201 | SEND_MESSAGE_TIMEOUT | 通过 API 发送消息超时。 |
4300 | CONVERSATION_INTERNAL_ERROR | 对话操作引起服务器内部错误。如果反复出现请收集相关线索并 创建工单,我们会尽快解决。 |
4301 | CONVERSATION_API_FAILED | 上游 API 调用异常,请查看报错信息了解错误详情。 |
4302 | CONVERSATION_SIGNATURE_FAILED | 对话相关操作签名错误。 |
4303 | CONVERSATION_NOT_FOUND | 发送消息、邀请加入对话等操作对应的对话不存在或因为 API 并发数超限等原因无法获取到对话信息。 |
4304 | CONVERSATION_FULL | 对话成员已满,不能再添加。 |
4305 | CONVERSATION_REJECTED_BY_APP | 对话操作被应用的云引擎 Hook 拒绝。 |
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 | 对话操作超出了 API 请求限制,需要提升应用级别。 |
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 | 发送的消息被应用的云引擎 Hook 拒绝。 |
4403 | MESSAGE_OWNERSHIP_REQUIRED | 没有操作目标消息的权限。 |
4404 | MESSAGE_NOT_FOUND | 找不到被操作的消息。 |
4405 | MESSAGE_UPDATE_REJECTED_BY_APP | 修改或撤回消息操作被 Hook 拒绝。 |
4406 | MESSAGE_EDIT_DISABLED | 未开启「允许通过 SDK 编辑消息」功能。 |
4407 | MESSAGE_RECALL_DISABLED | 未开启「允许通过 SDK 撤回消息」功能。 |
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 将聊天记录同步到自己的服务器上。
聊天消息没有收到,该如何排查
当出现聊天消息没有收到的情况,你可以按照以下思路排查:
- 调用消息记录 API 查看消息是否到达了云端
- 如果只有一个消息接收者,可以检查消息记录中对应条目的
ack-at
字段判断消息是否到达了客户端 - 在 控制台 > 即时通讯 > 用户 页面的文本框里输入对应的 Client ID,查看是否在线,以及是否有离线消息。
- 在 控制台 > 即时通讯 > 对话 页面的文本框里输入消息所属对话 ID,在
消息与日志
一栏里选日志,根据消息发送时间查看消息发送日志,看服务器是否有收到消息请求,消息是否有转发记录,转发消息时目标用户是否在线等信息。
为什么我收不到离线消息推送
首先请参考 聊天消息没有收到 一节内容查看聊天消息是否有正常送达服务器。
其次请利用控制台即时消息页的用户状态查询页面来确认消息接收者是否真的处于离线状态,是否有未读消息产生,是否在 _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
表中有关联的设备记录 - 检查是否有设置推送内容
- 使用控制台推送在线发送工具实际发推送给目标设备查看推送是否出错,比如 iOS 证书不匹配,设备 Token 过期,设备 Token 和推送环境不匹配等
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 展示一个会话里的所有消息,不会存在遗漏消息的情况。