两种发送策略
send(message,{delivery:'reliable'|'latest'}) 返回明确结果。省略 options 默认 reliable;旧 boolean true/false 临时分别映射可靠/最新值。传错策略、空对象或未知字段明确 invalid_delivery,不静默选默认通道。
成功是 {accepted:true,status:'sent'|'queued',messageId?,bytes?};可靠项附messageId/字节数,ID仅在本sender内有效。失败是 {accepted:false,status:'rejected',reason}。完整reason:invalid_delivery、invalid_message、not_open、message_too_large、backpressure、expired、closed、send_failed、cancelled。
sent 仅表示 RTCDataChannel.send 接受,不代表对端收到或执行。queued 是本地接受待发。关键操作应有应用 ID、ACK 和重复处理;没拿到ACK时不能凭“reliable”判断已成功。循环结构/BigInt/无可序列化顶层/抛错toJSON明确拒绝;消息按JSON编码后的 UTF-8 字节计量。
| 限制 | reliable | latest |
|---|---|---|
| 单消息 | 64 KiB,且不超过协商的有限正 SCTP 上限 | 同左 |
| 本地待发 | 最多64条、总256 KiB,保持FIFO | 仅最新1条 |
| 原生缓冲 | 下一条加入后不超过256 KiB | 小包加入后不超过 512 B;单个更大消息只在缓冲空时发送 |
| 待发age | 最老项满5秒使未发前缀失败 | 旧待发值超过150ms丢弃 |
| 队列满 | 明确拒绝新消息,绝不覆盖已接受项 | 更新替换旧待发值 |
两个lane独立,可靠队头不会阻塞SDK的latest槽;底层共享网络仍会受实际带宽约束。小包预算允许浏览器尚未将少量已交付字节的计数归零时继续发送,避免每包等待清零。512 B 是本地有效负载预算,不是时延保证;较大消息仍可能占用网络,实际吞吐应单独测量。5秒仅限本地未发队列,不是已经进入原生缓冲的交付TTL。clearStateQueue只清latest,不撤销可靠业务。
可靠项接受后失败逐条通知观察回调:
{delivery:'reliable',messageId,reason:'expired'|'closed'|'send_failed'|'cancelled',
bytes,ageMs,generation,errorName?}
最新值的原生异常通知 {delivery:'latest',reason:'send_failed',generation,errorName};替换/age过期是正常有损策略。errorName为有限枚举,无原始异常message或消息正文。
onSendError 只观察。SDK把当前连接的可靠非cancelled失败统一交 onDisconnect;不要在两个回调重复关闭/结算。同步 rejected 由调用方处理,先核对当前连接/代际,因为即时原生异常可能已经结束该会话。主动 leave/detach 或替换旧channel会报告 cancelled,但不制造第二次断线。关闭时清timer/listener,旧sender不能结束新连接。
适合reliable:回合/投票、选择/准备、开局/结束确认、checksum。适合latest:可覆盖的位置/光标/快照。一次性按键不能直接当latest丢弃;游戏若要用latest承载输入,需带原始输入历史、序号及确认水位并单独测试丢包恢复。回滚、预测、反作弊与终局权威都属于游戏协议。
有序事件示例的序号作用域是整条 transport 代际:同一连接再战继续使用同一实例,不从 1 重置。若游戏另建按局实例,必须在 event 与 ACK 外层同时绑定并校验局编号;只校验 event 内的 roundId 无法阻止旧 ACK 确认新一局的同序号事件。此示例只负责有序事件确认,回合权限、规则和恢复由游戏实现。