SuIM-SDK是跨端IM客户端的内核,本地存 SQLite(Web 换 IndexedDB),对外靠 JSON 字符串 + 回调接口 跨语言。

打开app后,会先InitSDK,这一阶段只校验并保存客户端环境配置 + 注册连接回调.置,不连网。

然后就挂监听SetListeners。

「挂监听」= App 把自己的回调对象交给 SDK,之后服务端推送 / 本地同步 / 连接状态变化都会主动调你,而不是你轮询。

监听器 管什么

OnConnListener

连中 / 成功 / 失败、踢下线、token 过期/无效

OnConversationListener

同步起止进度、新会话、会话变更、总未读、输入中

OnAdvancedMsgListener

新消息、离线消息、撤回、已读回执、删除

OnFriendshipListener

好友申请/同意/拒绝、好友增删改、黑名单

OnGroupListener

加退群、成员变化、群资料、群申请

OnUserListener

自己资料更新、在线状态

OnCustomBusinessListener

自定义业务通知

OnMessageKvInfoListener

消息 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

状态 作用

syncedMaxSeqs

每个会话本地认为「已对齐到」的最大 seq

reinstalled

本地无会话且未标记 Installed 时为 true,走重装同步

isSyncing

短时间防止「连上/唤醒」重复开战

Login 的 LoadSeq:从本地库把各会话(含通知)已有最大 seq 读进 syncedMaxSeqs


它被动响应的四类事件

事件 做什么

WS 连上

通知同步开始 → GetNewestSeq → 按缺口拉 → 通知同步结束

推送到来

连续则直接下发;有缺口先 Pull 再下发

App 唤醒

让 Conversation 再同步一批元数据 + 再 GetNewestSeq/拉缺口

手动同步指定会话

查这些会话的 maxSeq,再拉缺口


拉消息的策略(三个数字)

参数 含义

connectPullNums

1

刚连上:每个会话只要最新尖,够会话列表预览

defaultPullNums

10

推送缺口 / 唤醒:多补一点,仍非全历史

SplitPullMsgNum

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 能感知到的)

  1. 连上后:OnSyncServerStart → … → OnSyncServerFinish
  2. 追上的聊天消息:经 Conversation → OnRecvNewMessage / 会话列表变更等
  3. 通知类:经 Conversation 分发给好友/群/用户模块 → 再触发那些监听
  4. 会话列表能看到较新的 latestMsg;完整历史进聊天再补

可记成五步

  1. Login 时把本地 seq 水位装进内存
  2. 连上后问服务器各会话最大 seq
  3. 有落后就按区间 Pull(上线少拉、推送/唤醒多拉一点)
  4. 推进 syncedMaxSeqs,把消息/通知交给 Conversation
  5. 历史空洞留给打开对话框、往上翻时再拉

这就是 MsgSyncer 这块 SDK 的全部主责:消息 seq 的对齐与按需拉取调度。

Logo

一站式 AI 云服务平台

更多推荐