多设备投递游标(deviceId)

rev25 为“同一账号多设备”补上了按设备独立的投递游标:每个设备(安装)有稳定的 deviceId,服务端按设备分别记录 deliveredSeq 与自己未 ack 的消息;A 设备确认消息不会推进 B 设备的游标,B 重连仍能补收自己没确认的消息。本页说明 deviceId 的来源、握手与 hello、游标推进规则、兼容与限制。

一句话结论

客户端 SDK 自动生成并持久化 deviceId,建连时放在 WS query;业务不需要传,也没有新增公开 API(IMLib 仍是 102 个语义 op)。

服务端按 {app}:{user}:{device}:{conv} 记录每设备已确认的最大 seq;离线补收/hello.syncState 都按该设备游标计算。

不带 deviceId 的旧客户端继续使用用户级 legacy 游标,行为不变;同一账号新旧客户端可以并存。

数据链路​

  1. 1. 客户端生成并持久化 deviceId

    各端 SDK 首次连接时生成随机、与硬件无关的安装标识(≤128 字符),写入本地存储;之后断线重连、重启、重新 init 都复用同一值。

    存储不可用(隐私模式/只读盘/沙箱)时退化为“本次会话内存值”,不阻断连接;清缓存/卸载/换设备会得到新的 deviceId(视为新设备)。

  2. 2. 建连时放入 WS query

    WS URL 形如 wss://host/ws?token=…&appkey=…&deviceId=…(deviceId 由 SDK 追加,服务端兼容 device_id 别名)。

    服务端校验:缺省/空白 → 走 legacy;超过 128 字符 → 拒绝握手(HTTP 400 / 1001),不建立 WS 连接。

  3. 3. hello 回显 + 设备版 syncState

    服务端在 hello 中回显 deviceId(提供时),syncState.conversations[].deliveredSeq 取该设备的游标。

    该字段是 additive 运行期字段:旧客户端忽略即可;SDK 收到不一致的回显只记告警(可能服务端尚未升级)。

  4. 4. 离线补收按设备游标

    服务端在 hello 后补发 seq > 该设备 deliveredSeq 的消息(不含自己发送的、不满足可见性的、已被历史清理的),最后发 sync_done。

    补收受 offlineMaxPerConversation(每会话 500 条)约束;超出部分客户端应改用历史分页拉取。

  5. 5. ack / read / read_receipt 推进设备游标
    ack:把该消息所属会话的游标推进到 max(当前游标, 消息 seq);乱序 ack 会“跳过”中间未确认消息(客户端应按 seq 顺序 ack,见限制)。read:清用户级未读,并把当前设备的游标推进到该会话最新 seq(重连后不再补收已读消息)。read_receipt:回执成功后同样推进当前设备游标;其他设备不受影响。

多设备行为矩阵​

场景A 设备B 设备说明
同一消息投递收到(在线实时)收到(在线实时)扇出到该用户全部在线会话;发送者其他端 direction=SEND
A 对消息 ack游标推进游标不变,重连仍补收该消息设备隔离的核心
A 已读会话(read)游标推进到最新、未读清零游标不推进;未读计数仍被清零(用户级)未读仍用户级,见限制
B 长时间离线后重连—补收自己未 ack 的消息按 B 设备游标
新设备首次连接—游标 0 → 收到全部未 ack 消息受每会话 500 条上限
同一设备重连复用持久化 deviceId游标保留不会重复补收已 ack 消息
旧客户端(无 deviceId)legacy 用户级游标与设备游标互不影响行为与 rev24 前一致
清除本地数据/换设备生成新 deviceId新设备 → 全量未 ack 补收可能导致一次性大量补收

hello 示例​

JSON
{
  "type": "hello",
  "data": {
    "userId": "alice",
    "sessionId": "s-…",
    "serverTime": 1761800000000,
    "deviceId": "web-5f0c…",
    "syncState": {
      "conversations": [
        { "conversationType": 1, "targetId": "bob", "deliveredSeq": 42 },
        { "conversationType": 10, "targetId": "g_1001", "deliveredSeq": 7 }
      ]
    }
  }
}

限制(如实记录)​

限制说明
未读/已读仍是用户级read 会清用户级未读(其他端看到清零),设备游标只决定“补发到哪里”;端级未读/端级已读状态未实现
deviceId 为客户端自报服务端不校验设备凭证;伪造 deviceId 只会影响该“设备”的游标,不能越权读取消息
乱序 ack 会跳跃ack 把游标推进到该消息 seq 与当前游标的较大值;若先 ack 较大 seq,中间未 ack 消息不会再次补收。客户端应按 seq 顺序 ack(SDK 已按到达顺序处理)
补收有上限每会话最多补发 500 条;超出请用 getHistoryMessages 分页拉取
自定义 deviceId 的风险同一 deviceId 被多个端复用会共享游标(互相“代表”确认);不要手工固定 deviceId
清本地数据 = 新设备会收到全部未 ack 消息(可能很多);首次启动建议先拉会话列表/历史再进入聊天页

相关链接​