docs: 写入 K-00 SDK 行为约定附录
This commit is contained in:
+33
-2
@@ -958,7 +958,7 @@ close()
|
|||||||
1. WebSocket 到 `路径/mqtt`,子协议 `mqtt`。裸 TCP 只在选项里显式打开时使用。浏览器页面是 HTTPS 时,服务器也必须用 TLS:浏览器不允许 HTTPS 页面连 `ws://` 或请求 `http://`。
|
1. WebSocket 到 `路径/mqtt`,子协议 `mqtt`。裸 TCP 只在选项里显式打开时使用。浏览器页面是 HTTPS 时,服务器也必须用 TLS:浏览器不允许 HTTPS 页面连 `ws://` 或请求 `http://`。
|
||||||
2. 每次连接都带 Clean Start,包括重连。Go 的 autopaho 只在第一次连接使用 `CleanStartOnInitialConnection`,必须用 `ConnectPacketBuilder` 在每一次连接把 Clean Start 设为 true,会话过期间隔设为 0。
|
2. 每次连接都带 Clean Start,包括重连。Go 的 autopaho 只在第一次连接使用 `CleanStartOnInitialConnection`,必须用 `ConnectPacketBuilder` 在每一次连接把 Clean Start 设为 true,会话过期间隔设为 0。
|
||||||
3. 订阅 down,发 `hello`,等到成功响应。连接超时默认 30 秒(autopaho 默认 10 秒,要改):服务器重启后大量端同时重连,密码校验要排队。
|
3. 订阅 down,发 `hello`,等到成功响应。连接超时默认 30 秒(autopaho 默认 10 秒,要改):服务器重启后大量端同时重连,密码校验要排队。
|
||||||
4. 重连退避:1 秒起,加倍,上限 30 秒,加减 30% 抖动。稳定在线 60 秒后把退避恢复到 1 秒。
|
4. 重连退避:见本节附录「SDK 行为约定」。
|
||||||
5. 会话令牌:用密码连上后,把握手响应里的 `session_token` 通过 `onSession` 交给应用,之后自动重连都用这个令牌;应用下次启动可以直接用保存的令牌 `connect`。SDK 自己不落盘保存令牌和密码。
|
5. 会话令牌:用密码连上后,把握手响应里的 `session_token` 通过 `onSession` 交给应用,之后自动重连都用这个令牌;应用下次启动可以直接用保存的令牌 `connect`。SDK 自己不落盘保存令牌和密码。
|
||||||
6. 停止重连的情况:收到 `fatal`;被 `0x8E` 接管;CONNACK 为 `0x86` 用户名或密码错误、`0x87` 未授权、`0x8A` 已封禁(3.1.1 为返回码 4、5)。这时事件 `auth_failed` 带原因:用令牌连接被拒为 `session_invalid`(在别处登录过、令牌过期、密码被改或重置、已退出登录、编号被停用或删除),用密码被拒为 `bad_credentials`。其他失败,包括网络错误、`0x88` 服务器不可用、`0x89` 服务器忙、连接被直接关闭,都继续重连。
|
6. 停止重连的情况:收到 `fatal`;被 `0x8E` 接管;CONNACK 为 `0x86` 用户名或密码错误、`0x87` 未授权、`0x8A` 已封禁(3.1.1 为返回码 4、5)。这时事件 `auth_failed` 带原因:用令牌连接被拒为 `session_invalid`(在别处登录过、令牌过期、密码被改或重置、已退出登录、编号被停用或删除),用密码被拒为 `bad_credentials`。其他失败,包括网络错误、`0x88` 服务器不可用、`0x89` 服务器忙、连接被直接关闭,都继续重连。
|
||||||
|
|
||||||
@@ -966,7 +966,7 @@ close()
|
|||||||
|
|
||||||
- 发送队列在内存,默认最多 1000 条,包括已发出但没收到 `resp` 的。连不上时 `send` 入队;重连后按原消息号、原请求内容再交。
|
- 发送队列在内存,默认最多 1000 条,包括已发出但没收到 `resp` 的。连不上时 `send` 入队;重连后按原消息号、原请求内容再交。
|
||||||
- `sendAt` 在调用 `send` 时就换算成 `send_at_ms`,重交时不重算,否则请求指纹变了会被当成冲突。
|
- `sendAt` 在调用 `send` 时就换算成 `send_at_ms`,重交时不重算,否则请求指纹变了会被当成冲突。
|
||||||
- 同时在途(已发出、没收到 `resp`)的请求不超过 100 个。发送收到 `rate_limited` 时按退避自动重交,不算失败;其他请求收到 `rate_limited` 直接返回给应用。
|
- 同时在途(已发出、没收到 `resp`)的请求不超过 100 个。发送收到 `rate_limited` 时按附录「SDK 行为约定」自动重交,不算失败;其他请求收到 `rate_limited` 直接返回给应用。
|
||||||
- 进程退出则队列丢失。SDK 停止重连(被踢、认证失败、`logout`、`close`)时,队列里的发送全部以对应错误结束。
|
- 进程退出则队列丢失。SDK 停止重连(被踢、认证失败、`logout`、`close`)时,队列里的发送全部以对应错误结束。
|
||||||
- 其他调用在未握手时返回未连接。
|
- 其他调用在未握手时返回未连接。
|
||||||
|
|
||||||
@@ -1019,6 +1019,37 @@ close()
|
|||||||
14. 仅 JS:浏览器页面和服务器不同域名时,注册和 WebSocket 连接都成功。
|
14. 仅 JS:浏览器页面和服务器不同域名时,注册和 WebSocket 连接都成功。
|
||||||
15. 会话令牌:密码登录后收到令牌;断开后用令牌重连成功;另一处用密码登录后,原来那处用旧令牌重连被拒(`session_invalid`)且不再重连;`logout` 后令牌失效。
|
15. 会话令牌:密码登录后收到令牌;断开后用令牌重连成功;另一处用密码登录后,原来那处用旧令牌重连被拒(`session_invalid`)且不再重连;`logout` 后令牌失效。
|
||||||
|
|
||||||
|
### 附录:SDK 行为约定
|
||||||
|
|
||||||
|
四种语言同一套对外语义。实现以本附录为准,不以任何一套现有 SDK 为参照。Issue K-00 (#57) 及第二轮审查「补充」条目已并入下表。
|
||||||
|
|
||||||
|
| 项 | 约定 |
|
||||||
|
|---|---|
|
||||||
|
| 首次连接超时 | 默认 30 秒内没完成握手:停止重连,返回 `not_connected`;Client 可再次调用 connect |
|
||||||
|
| 认证失败错误码 | 直接用原因:`bad_credentials`、`session_invalid`、`disabled`、`deleted`、`password_reset`、`taken_over`、`rate_limited`;事件 `auth_failed` / `kicked` 带同一原因 |
|
||||||
|
| 顶号原因名 | MQTT DISCONNECT `0x8E` 一律报 `taken_over`,不报 `"0x8E"` |
|
||||||
|
| 服务器关闭 | DISCONNECT `0x8B` 按可重试处理,继续重连 |
|
||||||
|
| 本地队列满 | 统一 `queue_full`,不用服务端的 `quota_exceeded` |
|
||||||
|
| 请求返回值 | 只返回 resp 的 `data`,不返回整个信封 |
|
||||||
|
| sendAt 与 delay 同时给 | 本地返回 `bad_request`(PRD F11) |
|
||||||
|
| 停止重连后再 send | 立即以停止原因失败,不入队 |
|
||||||
|
| logout | 服务端请求失败(含离线)时把错误返回给应用;本地照常停止重连、清空令牌、断开传输;队列里的发送按「停止重连后再 send」的停止原因结束,并发 `offline` 事件 |
|
||||||
|
| 时长参数 | 毫秒时长和时间戳一律用 64 位整数(Java 用 `long` / `Long`) |
|
||||||
|
| max_receive_bytes 小于 1024 | 本地返回 `bad_request` |
|
||||||
|
| URL 映射 | `http`→`ws`、`https`→`wss`;`ws`/`wss` 原样;`mqtt`/`mqtts` 只在显式开启裸 TCP 选项时允许;路径为空或 `/` 时用 `/mqtt`,否则按原路径 |
|
||||||
|
| 取消与超时 | 尚未发出的条目取消即出队;已发出的返回「结果未知」错误(错误码 `result_unknown`),应用应以同一消息号重试 |
|
||||||
|
| rate_limited 重交退避 | 每条发送单独计数:第 n 次等待 `min(1 秒 × 2^(n-1), 30 秒) × 随机(0.7, 1.3)`,成功后清零。每次重交(包括断线后的重交)都重新生成 `rid` 并重新序列化帧;消息 `id`、正文和 `send_at_ms` 保持不变 |
|
||||||
|
| 重连退避 | 只维护一个连续失败计数 n(n ≥ 1)。第 n 次等待 `min(1 秒 × 2^(n-1), 30 秒) × 随机(0.7, 1.3)`。只有应用调用 connect 后的第一次连接可以不等待;已建立的连接断开后,第一次重连也按 n=1 等约 1 秒。连接尝试失败时 n 加 1;连上后不足 60 秒又断开,也按一次失败计(n 加 1);稳定在线满 60 秒后断开,n 重置为 1。不能再另把 base 翻倍。连接超时覆盖等 CONNACK 的时间 |
|
||||||
|
| 心跳 | 默认 30 秒 |
|
||||||
|
| Receive Maximum | CONNECT 不带该属性 |
|
||||||
|
| 默认请求超时 | 非发送请求默认 60 秒 |
|
||||||
|
|
||||||
|
重连退避标称间隔(去掉抖动):n=1…6 为 1、2、4、8、16、30 秒。rate_limited 重交用同一套标称间隔,但按**该条发送**自己的计数,与连接失败计数无关。
|
||||||
|
|
||||||
|
四套 SDK 为上表每一项使用同名用例(Go `TestK00*`,JS/Python `test_k00_*`,Java `testK00*`):
|
||||||
|
|
||||||
|
`FirstConnectTimeout`、`AuthErrorCodes`、`TakenOverReason`、`Disconnect8BRetryable`、`QueueFull`、`RequestReturnsData`、`SendAtAndDelayConflict`、`SendAfterStopped`、`LogoutReturnsError`、`DurationInt64`、`MaxReceiveBytesMin`、`URLMapping`、`CancelUnsent`、`RateLimitedBackoff`、`ReconnectBackoff`、`KeepaliveDefault`、`NoReceiveMaximum`。
|
||||||
|
|
||||||
## 10. 无 SDK 的设备
|
## 10. 无 SDK 的设备
|
||||||
|
|
||||||
使用 MQTT 5 客户端(例如 ESP-IDF 的 mqtt,走 WebSocket 或 TCP)。ClientID 和用户名是端编号,密码是登录密码。订阅 `nix/c/{编号}/down`,向 `nix/c/{编号}/up` 发第 6 节的 JSON。收到 `msg` 后处理完发 `ack`。用「from + id」去重,已确认过的再到达时再回一次 `ack`。在 `hello` 里按自己的接收缓冲声明 `max_receive_bytes`(不小于 1024)。裸设备要么实现 `receipt_ack`,要么发送时带 `receipt:false`,否则未确认的回执会占满推送窗口。
|
使用 MQTT 5 客户端(例如 ESP-IDF 的 mqtt,走 WebSocket 或 TCP)。ClientID 和用户名是端编号,密码是登录密码。订阅 `nix/c/{编号}/down`,向 `nix/c/{编号}/up` 发第 6 节的 JSON。收到 `msg` 后处理完发 `ack`。用「from + id」去重,已确认过的再到达时再回一次 `ack`。在 `hello` 里按自己的接收缓冲声明 `max_receive_bytes`(不小于 1024)。裸设备要么实现 `receipt_ack`,要么发送时带 `receipt:false`,否则未确认的回执会占满推送窗口。
|
||||||
|
|||||||
@@ -1153,6 +1153,15 @@
|
|||||||
- 备选:ack 发后不等 `resp`;未采用,以免丢「ack 结果当 revoked」语义。
|
- 备选:ack 发后不等 `resp`;未采用,以免丢「ack 结果当 revoked」语义。
|
||||||
- 影响:行为更接近文档;单测仍绿。
|
- 影响:行为更接近文档;单测仍绿。
|
||||||
|
|
||||||
|
### 复审修复 K-00
|
||||||
|
|
||||||
|
- 日期:2026-09-30
|
||||||
|
- 原条款:PRD F19「四种 SDK 行为一致」;DEVELOPMENT 第 9 节重连退避原文过简;issue #57 及第二轮审查修订。
|
||||||
|
- 实际做法:在 DEVELOPMENT 第 9 节增加附录「SDK 行为约定」,四套 README 各加摘要。重连退避按 `min(1s×2^(n-1), 30s)×随机(0.7,1.3)`、只维护一个计数 n;不以现有 JS 实现为参照。rate_limited 与断线重交每次新 rid;logout 失败返回应用;时长用 64 位整数;CONNECT 不带 Receive Maximum;0x8B 可重试;顶号原因 `taken_over`。
|
||||||
|
- 原因:四套对外语义不一致;第二轮审查核实 JS 退避双重翻倍,照搬会把错误带进四套。
|
||||||
|
- 备选方案:统一采用旧 S1.3 状态机;否决。
|
||||||
|
- 影响:K-01 至 K-04 按本附录实现;本条只改文档。
|
||||||
|
|
||||||
## SDK 二 S2
|
## SDK 二 S2
|
||||||
|
|
||||||
### S2-PY/JAVA 1–3 2026-09-30
|
### S2-PY/JAVA 1–3 2026-09-30
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ res, err := nixmsg.Register(ctx, "ws://127.0.0.1:7443/mqtt", "reg-code",
|
|||||||
nixmsg.RegisterOptions{ID: "device-1", LoginPassword: "secret", Name: "门口"})
|
nixmsg.RegisterOptions{ID: "device-1", LoginPassword: "secret", Name: "门口"})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 行为约定
|
||||||
|
|
||||||
|
四种 SDK 同一套对外语义,详见仓库 `docs/DEVELOPMENT.md` 第 9 节附录「SDK 行为约定」。要点:
|
||||||
|
|
||||||
|
- 首次连接默认 30 秒内未握手则停止重连,返回 `not_connected`;Client 可再次 `Connect`。
|
||||||
|
- 顶号原因一律 `taken_over`;DISCONNECT `0x8B` 可重试;CONNECT 不带 Receive Maximum;心跳默认 30 秒。
|
||||||
|
- 重连退避只维护一个计数 n:第 n 次等待 `min(1s×2^(n-1), 30s)×随机(0.7,1.3)`。仅 `Connect` 后第一次可不等待;断线后首次重连也约 1 秒;在线不足 60 秒断开则 n+1,稳定 60 秒后断开 n=1。
|
||||||
|
- `rate_limited` 与断线重交:每次重新生成 `rid` 并重新序列化;消息 `id`、正文、`send_at_ms` 不变。
|
||||||
|
- `Logout` 请求失败返回给应用;本地仍停止重连、清空令牌、结束队列并发 `offline`。
|
||||||
|
- 时长与时间戳用 64 位整数。`sendAt` 与 `delay` 同时给时本地 `bad_request`。停止重连后再 `Send` 立即失败。
|
||||||
|
- 取消尚未发出的条目即出队;已发出的返回 `result_unknown`,应用应以同一消息号重试。
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
见 `LICENSE`(专有)。
|
见 `LICENSE`(专有)。
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ c.close();
|
|||||||
|
|
||||||
命令行示例类:`asia.asio.nixmsg.examples.MinimalExample`。
|
命令行示例类:`asia.asio.nixmsg.examples.MinimalExample`。
|
||||||
|
|
||||||
|
## 行为约定
|
||||||
|
|
||||||
|
四种 SDK 同一套对外语义,详见仓库 `docs/DEVELOPMENT.md` 第 9 节附录「SDK 行为约定」。要点:
|
||||||
|
|
||||||
|
- 首次连接默认 30 秒内未握手则停止重连,以 `not_connected` 失败;Client 可再次 `connect`。
|
||||||
|
- 顶号原因一律 `taken_over`;DISCONNECT `0x8B` 可重试;CONNECT 不带 Receive Maximum;心跳默认 30 秒。
|
||||||
|
- 重连退避只维护一个计数 n:第 n 次等待 `min(1s×2^(n-1), 30s)×随机(0.7,1.3)`。仅 `connect` 后第一次可不等待;断线后首次重连也约 1 秒;在线不足 60 秒断开则 n+1,稳定 60 秒后断开 n=1。
|
||||||
|
- `rate_limited` 与断线重交:每次重新生成 `rid` 并重新序列化;消息 `id`、正文、`send_at_ms` 不变。
|
||||||
|
- `logout` 请求失败时 future 以异常完成;本地仍停止重连、清空令牌、结束队列并发 `offline`。
|
||||||
|
- 时长与时间戳用 `long` / `Long`(含 `updateSelf` 的 `defaultDelayMs`)。`sendAt` 是本机时间(epoch 毫秒或 `java.util.Date`,入队时加偏差);`sendAtMs` 是服务器时间。两者与 `delayMs` 同时给时本地 `bad_request`。停止重连后再 `send` 立即失败。
|
||||||
|
- 取消尚未发出的条目即出队;已发出的返回 `result_unknown`,应用应以同一消息号重试。
|
||||||
|
|
||||||
## 打包(不发布)
|
## 打包(不发布)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ await c.close();
|
|||||||
|
|
||||||
浏览器页面与服务器不同源时,注册接口已回 `Access-Control-Allow-Origin: *`,WebSocket `/mqtt` 不校验 Origin,可直接连接。
|
浏览器页面与服务器不同源时,注册接口已回 `Access-Control-Allow-Origin: *`,WebSocket `/mqtt` 不校验 Origin,可直接连接。
|
||||||
|
|
||||||
|
## 行为约定
|
||||||
|
|
||||||
|
四种 SDK 同一套对外语义,详见仓库 `docs/DEVELOPMENT.md` 第 9 节附录「SDK 行为约定」。要点:
|
||||||
|
|
||||||
|
- 首次连接默认 30 秒内未握手则停止重连,`connect()` 返回 `not_connected`;Client 可再次 `connect`。
|
||||||
|
- 顶号原因一律 `taken_over`;DISCONNECT `0x8B` 可重试;CONNECT 不带 Receive Maximum;心跳默认 30 秒。
|
||||||
|
- 重连退避只维护一个计数 n:第 n 次等待 `min(1s×2^(n-1), 30s)×随机(0.7,1.3)`。仅 `connect` 后第一次可不等待;断线后首次重连也约 1 秒;在线不足 60 秒断开则 n+1,稳定 60 秒后断开 n=1。不要用「attempt 与 base 同时翻倍」。
|
||||||
|
- `rate_limited` 与断线重交:每次重新生成 `rid` 并重新序列化;消息 `id`、正文、`send_at_ms` 不变。
|
||||||
|
- `logout` 请求失败返回给应用;本地仍停止重连、清空令牌、结束队列并发 `offline`。
|
||||||
|
- 时长与时间戳用 64 位整数。`sendAt` 与 `delay` 同时给时本地 `bad_request`。停止重连后再 `send` 立即失败。
|
||||||
|
- 取消尚未发出的条目即出队;已发出的返回 `result_unknown`,应用应以同一消息号重试。
|
||||||
|
|
||||||
## 打包试运行
|
## 打包试运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -38,6 +38,18 @@ c.close()
|
|||||||
|
|
||||||
更完整的命令行示例见 `examples/minimal.py`。
|
更完整的命令行示例见 `examples/minimal.py`。
|
||||||
|
|
||||||
|
## 行为约定
|
||||||
|
|
||||||
|
四种 SDK 同一套对外语义,详见仓库 `docs/DEVELOPMENT.md` 第 9 节附录「SDK 行为约定」。要点:
|
||||||
|
|
||||||
|
- 首次连接默认 30 秒内未握手则停止重连,抛出 `not_connected`;Client 可再次 `connect`。
|
||||||
|
- 顶号原因一律 `taken_over`;DISCONNECT `0x8B` 可重试;CONNECT 不带 Receive Maximum;心跳默认 30 秒。
|
||||||
|
- 重连退避只维护一个计数 n:第 n 次等待 `min(1s×2^(n-1), 30s)×随机(0.7,1.3)`。仅 `connect` 后第一次可不等待;断线后首次重连也约 1 秒;在线不足 60 秒断开则 n+1,稳定 60 秒后断开 n=1。
|
||||||
|
- `rate_limited` 与断线重交:每次重新生成 `rid` 并重新序列化;消息 `id`、正文、`send_at_ms` 不变。
|
||||||
|
- `logout` 请求失败抛给应用;本地仍停止重连、清空令牌、结束队列并发 `offline`。
|
||||||
|
- 时长与时间戳用 64 位整数。`send_at` 是本机时间(会加时钟偏差写成 `send_at_ms`);`send_at_ms` 是服务器时间。两者与 `delay_ms` 同时给时本地 `bad_request`。停止重连后再 `send` 立即失败。
|
||||||
|
- 取消尚未发出的条目即出队;已发出的返回 `result_unknown`,应用应以同一消息号重试。
|
||||||
|
|
||||||
## 打包(不发布)
|
## 打包(不发布)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in New Issue
Block a user