SuIM-SDK
SuIM-SDK是跨端IM客户端的内核,本地存 SQLite(Web 换 IndexedDB),对外靠 JSON 字符串 + 回调接口 跨语言。
打开app后,会先InitSDK,这一阶段只校验并保存客户端环境配置 + 注册连接回调.置,不连网。
然后就挂监听SetListeners。
「挂监听」= App 把自己的回调对象交给 SDK,之后服务端推送 / 本地同步 / 连接状态变化都会主动调你,而不是你轮询。
| 监听器 | 管什么 |
|---|---|
|
|
连中 / 成功 / 失败、踢下线、token 过期/无效 |
|
|
同步起止进度、新会话、会话变更、总未读、输入中 |
|
|
新消息、离线消息、撤回、已读回执、删除 |
|
|
好友申请/同意/拒绝、好友增删改、黑名单 |
|
|
加退群、成员变化、群资料、群申请 |
|
|
自己资料更新、在线状态 |
|
|
自定义业务通知 |
|
|
消息 KV 扩展变更 |
连接监听例外:OnConnListener 不在这里,而是 InitSDK(listener, …) 传入。
User模块
Login登录
App 调用 Login(callback, operationID, userID, token)
│
├─ 若当前已是 Logged → 直接 OnError(LoginRepeat),结束
├─ 登录状态设为 Logging
│
├─ 把 userID 和 token 写入 GlobalConfig
│ 之后 REST 请求头里的 token、WS 连接 URL 里的 sendID/token
│ 都从 GlobalConfig 读取
│
├─ initialize
│ │
│ ├─ 按 userID 打开本地数据库
│ │ 路径:{DataDir}/OpenIM_{大版本}_{userID}.db
│ │ · 文件已存在 → 直接打开,历史数据保留
│ │ · 文件不存在 → 创建该文件
│ │ · 用 AutoMigrate / 版本迁移保证好友、群、会话等表结构存在
│ │ (补表/补字段,不因此清空已有行数据)
│ │ · 打开失败 → Login OnError,结束
│ │
│ ├─ 处理 local_sending_messages
│ │ 遍历发送中记录:
│ │ · 对应消息若仍是 Sending → 改为 SendFailed
│ │ · 若该消息是会话 LatestMsg → LatestMsg 状态也改为 Failed
│ │ · 删除这条 sending 记录
│ │ (单条失败只打日志,不中断 Login)
│ │
│ ├─ 把 db 和 loginUserID 注入各模块
│ │ user / file / relation / group /
│ │ msgSyncer / conversation / third(另设平台、日志路径等)
│ │
│ └─ LoadSeq
│ · 读取本地全部会话 ID
│ · 若会话 ID 列表长度为 0:
│ 再读 LocalAppSDKVersion
│ 若记录不存在,或 Installed == false
│ → 置 MsgSyncer.reinstalled = true
│ (否则 reinstalled 保持 false)
│ · 若会话 ID 列表非空:不在这里改 reinstalled
│ · 分批并发读取每个会话已同步的最大普通消息 seq
│ → 写入内存 syncedMaxSeqs[conversationID]
│ · 再读取全部通知会话 seq,合并进 syncedMaxSeqs
│ · 失败 → Login OnError,结束
│
├─ run:启动后台 goroutine
│ · LongConnMgr:readPump / writePump / heartbeat
│ · MsgSyncer.DoListener
│ · Conversation 事件循环
│ · logoutListener
│
├─ 登录状态设为 Logged
├─ return nil → Base.OnSuccess
│ (此处尚未完成 WS 建连,也未完成数据同步)
│
└─(异步,由 readPump 驱动)建立 WebSocket
│
├─ OnConnecting;状态设为 Connecting
├─ Dial URL:
│ {WsAddr}?sendID={UserID}&token={Token}
│ &platformID=…&operationID=…&isBackground=…
│ &sdkVersion=…&compression=gzip
│
├─ Dial 成功
│ · 发送首包在线订阅信息
│ · OnConnectSuccess;状态 Connected
│ · 重置重连退避
│ · 向 MsgSyncer 投递「连接成功」
│ · MsgSyncer.doConnected:
│ · reinstalled == true
│ → 同步标志 AppDataSyncStart
│ → OnSyncServerStart(true)
│ → 同步群/好友/会话/未读等(重装路径)
│ · reinstalled == false
│ → 同步标志 MsgSyncBegin
│ → OnSyncServerStart(false)
│ → 同步未读等,并异步同步资料/黑名单/群/好友/会话
│ · WS GetNewestSeq,与 syncedMaxSeqs 对比后拉缺口消息
│ · 重装路径在对应同步流程末尾会
│ SetAppSDKVersion(Installed=true),并把 reinstalled 置 false
│ · 结束标志 → OnSyncServerFinish(reinstalled 当时对应的 true/false)
│
├─ Dial 失败且无业务错误 body(网络错误)
│ · OnConnectFailed
│ · 指数退避后再次 Dial
│
└─ Dial 失败且响应 body 含 errCode
· Token 过期 → OnUserTokenExpired,投递登出,停止重连
· Token 无效等 → OnUserTokenInvalid,投递登出,停止重连
· 被踢 → OnKickedOffline,投递登出,停止重连
· 其它错误码 → 可继续重连
MsgSyncer
保证消息按会话 seq 对齐:连上/唤醒/推送时发现落后或断层,就向服务器按区间拉取,再交给 Conversation 落库和回调 UI。不管好友、群资料那些元数据。
type MsgSyncer struct {
loginUserID string
longConnMgr *LongConnMgr
PushMsgAndMaxSeqCh chan Cmd2Value // 输入:连上/推送/唤醒…
conversationEventQueue chan Cmd2Value // 输出:给 Conversation
syncedMaxSeqs map[string]int64 // 每个会话本地已追上的最大 seq
db DataBase
reinstalled bool // LoadSeq 里按规则置位
isSyncing bool // 防连上/唤醒并发撞车
}
长连接 LongConnMgr
│ 连接成功 / 推送 / 唤醒…
▼
MsgSyncer ← 算缺口、拉消息、推进水位
│ 新消息 / 通知 / 同步开始结束标志
▼
Conversation ← 写库、刷会话列表、回调 App
| 状态 | 作用 |
|---|---|
|
|
每个会话本地认为「已对齐到」的最大 seq |
|
|
本地无会话且未标记 Installed 时为 true,走重装同步 |
|
|
短时间防止「连上/唤醒」重复开战 |
Login 的 LoadSeq:从本地库把各会话(含通知)已有最大 seq 读进 syncedMaxSeqs。
它被动响应的四类事件
| 事件 | 做什么 |
|---|---|
|
WS 连上 |
通知同步开始 → GetNewestSeq → 按缺口拉 → 通知同步结束 |
|
推送到来 |
连续则直接下发;有缺口先 Pull 再下发 |
|
App 唤醒 |
让 Conversation 再同步一批元数据 + 再 GetNewestSeq/拉缺口 |
|
手动同步指定会话 |
查这些会话的 maxSeq,再拉缺口 |
拉消息的策略(三个数字)
| 参数 | 值 | 含义 |
|---|---|---|
|
|
1 |
刚连上:每个会话只要最新尖,够会话列表预览 |
|
|
10 |
推送缺口 / 唤醒:多补一点,仍非全历史 |
|
|
100 |
多会话一起同步时分批,避免单次请求过大 |
设计意图:
- 上线:对齐最新预览 + 把水位推到服务器 max
- 进聊天往上翻:本地没有的再按需拉历史(Conversation 的 history/gap 逻辑)
- 连上时即使区间很大、只拉
Num条,也会把syncedMaxSeqs标到区间 End(中间空洞留给翻历史补)
普通 vs 重装
普通(本地已有会话数据)
- 算
[本地+1, 服务器max]或新会话[0, max] - Pull → 普通新消息通道 / 通知通道
重装(无会话且 Installed 为假)
- 通知会话:不拉消息体,只把 seq 写入本地并推进水位
- 聊天会话:按重装通道触发(偏会话列表可用)
- 结束后写
Installed = true,清掉reinstalled
推送时的连续性规则
Seq == 0:立刻下发,不改水位- 连续(最后一条 seq == 本地水位 + 本次条数):直接下发,水位前进
- 不连续且推得更新:记缺口,Pull 补齐后再下发
它明确不做什么
- 不打开/管理 SQLite 业务表的日常 CRUD(Login 开库;落库在 Conversation)
- 不做好友/群/会话元数据的 Version Syncer
- 不直接回调 App UI(只往 Conversation 的队列丢命令)
- 不负责翻历史时的分页 UI(那是进会话后的另一套拉取)
对外表现出的结果(App 能感知到的)
- 连上后:
OnSyncServerStart→ … →OnSyncServerFinish - 追上的聊天消息:经 Conversation →
OnRecvNewMessage/ 会话列表变更等 - 通知类:经 Conversation 分发给好友/群/用户模块 → 再触发那些监听
- 会话列表能看到较新的 latestMsg;完整历史进聊天再补
可记成五步
- Login 时把本地 seq 水位装进内存
- 连上后问服务器各会话最大 seq
- 有落后就按区间 Pull(上线少拉、推送/唤醒多拉一点)
- 推进
syncedMaxSeqs,把消息/通知交给 Conversation - 历史空洞留给打开对话框、往上翻时再拉
这就是 MsgSyncer 这块 SDK 的全部主责:消息 seq 的对齐与按需拉取调度。
更多推荐




所有评论(0)