跳到主要内容

帧头

基础规则

基础帧头固定为 56 字节。Header Length 字段用于未来扩展,但当前版本发送端必须使用 header_length = 56

所有多字节数值字段统一使用 Little Endian 编码。浮点数不出现在基础帧头中,Raw Binary Payload 的浮点规则见Payload 与 Schema

NRCP Frame 总长度由 Header Length 与 Payload Length 决定:

Lf=Lh+LpL_f = L_h + L_p

其中:

  • LfL_f 表示 NRCP Frame 总字节长度;
  • LhL_h 表示 Header Length;
  • LpL_p 表示 Payload Length;

接收端必须先读取基础 56 字节 Header,再根据 Header Length 与 Payload Length 判断完整 Frame 边界。若 Header Length 小于 56、Frame 长度不匹配或保留字段非 0,接收端必须拒绝该 Frame

帧头字段布局

字节索引命名字段含义
0-3Magic魔数
4Version版本号
5Header length帧头字节长度
6Payload codec载荷部分序列化方式
7Reserved 0必须为 0
8-9Message Type消息类型
10-11Flags标志位
12-13Channel ID运行时通道 ID
14-15Reserved 1必须为 0
16-23Message ID消息 ID
24-31Related ID关联的消息 ID
32-39Sequence数据流消息序号
40-47Source mono timestamp发送方单调时间戳
48-51Flow Epoch数据流上下文版本
52-53Payload length载荷字节长度
54-55Reserved 2必须为 0

字段类型

字节索引字段类型编码
0-3Magic固定字节序列0x4e 0x52 0x43 0x50,即 ASCII NRCP
4Versionuint8当前为 1
5Header lengthuint8当前为 56
6Payload codecuint8见 Payload Codec 注册表
7Reserved 0uint8必须为 0
8-9Message Typeuint16Little Endian
10-11Flagsuint16Little Endian
12-13Channel IDuint16Little Endian
14-15Reserved 1uint16必须为 0
16-23Message IDuint64Little Endian
24-31Related IDuint64Little Endian
32-39Sequenceuint64Little Endian
40-47Source mono timestampuint64Little Endian,单位为微秒
48-51Flow Epochuint32Little Endian
52-53Payload lengthuint16Little Endian
54-55Reserved 2uint16必须为 0

Payload Codec 注册表

名称说明
0raw_binary原始二进制
1protobufProtobuf
2cborCBOR
3jsonJSON 文本,UTF-8 编码
4flatbuffers FlatBuffers
5sbeSBE

字段详解

Magic

Magic 固定为 ASCII 字节序列 NRCP,即 0x4e 0x52 0x43 0x50

QUIC 无 TCP 的粘包问题,因此此处的 Magic 作用并非防粘包与重同步,而是:

  • Dump/日志/抓包快速识别 NRCP 帧;
  • 防止错误的 Buffer 被误解析;
  • 单测/回放工具更易定位;
  • 与普通业务载荷、内部测试载荷、错误 Stream 内容等做快速区分;

Version

当前基础帧头 Version 为 1

QUIC ALPN 负责协议大版本协商,此处的 Version 负责:

  • 帧数据脱离 QUIC 连接后仍然能够被独立解析;
  • Dump 文件不一定保存了 ALPN;
  • 测试样本、Fuzz 样本具备自描述能力;
  • SDK 日志可直接展示帧版本;
  • 后期同一 ALPN 下可区分 Header Minor Revision;
  • 防止错误版本的解析器误解析帧;

Header length

当前版本发送端必须使用 header_length = 56。接收端收到小于 56 的 Header Length 必须拒绝该 Frame

之所以选择引入该字段是为了后续协议演进的便利,它有助于:

  • 后续增加扩展头;
  • 老解析器允许跳过未知扩展;
  • 新解析器允许解析扩展字段;
  • 不破坏基础帧布局;
  • 支持 Feature Negotiation 后的扩展字段;

若去除该字段,未来协议中加字段时只能:

  • 修改 ALPN;
  • 修改整个帧格式;
  • 依赖 Flags;

Payload codec

载荷部分的序列化方式,目前定义了以下枚举值:

  • 0:原始二进制(Raw Binary);
  • 1:Protobuf;
  • 2:CBOR;
  • 3:JSON 文本(JSON);
  • 4:FlatBuffers;
  • 5:SBE

载荷部分之所以存在多种序列化方式,是因为 NRCP 包含控制、状态、告警、日志等不同类别,每种类别适合的序列化方式不同

Reserved 0/1/2

为了内存布局与 CPU 访问优化而添加,同时也作为后期扩展的显式保留字段

Message Type

该字段用于定义载荷的通用协议语义,具体规则见Message Type

Message Type 不表示具体业务能力,具体业务含义由 Operation Name 或 Flow Name 定义

Flags

该字段用于表达跨 Message Type 的通用修饰语义,形式为掩码,具体规则见Flags

未知 Flags 必须按消息语义章节的规则处理

Channel ID

该字段是 NRCP 动态数据流机制的核心字段之一,主要作用是查询所属数据流的上下文(比如载荷类型、QoS 配置等)

非 Flow 消息应使用 channel_id = 0

Message ID 是单方向唯一消息 ID,两者结合可用于:

  • Req/Res 关联;
  • ACK 关联;
  • 日志追踪;
  • 幂等处理;
  • 应用层消息去重;
  • 跨 Stream 关联;

当 Related ID 为 0 时,代表无关联

Sequence

QUIC Stream 在传输层保证了字节流的顺序性,但 Sequence 字段用于:

  • QUIC Datagram 乱序;
  • QUIC Datagram 丢包统计;
  • 应用层 QoS 实现;
  • Flow Epoch 切换期间旧包识别;
  • 跨 Stream 或 Stream 重建后的 Flow 连续性;

0 代表不启用该字段

Source mono timestamp

该字段主要作用就是时间同步与消息的时效性判断

因为控制终端作为 Client、机械狗本体作为 Server 时,两端时间戳默认为非同步,因此无法直接使用系统时钟判断消息是否过期

更多规则见心跳与时钟同步

Flow Epoch

该字段为动态数据流机制的第二个核心字段,用于标识数据流版本,主要场景有:

  • 订阅重建;
  • QoS 更新;
  • Flow 暂停后恢复;
  • Channel ID 复用;
  • 旧消息隔离;
  • 服务器授权数据流上下文版本校验;

Payload length

Payload Length 表示基础 Header 与扩展 Header 之后的 Payload 字节长度。当前字段类型为 uint16,因此单个 Frame 的 Payload 最大长度为 65535 字节

接收端必须校验实际 Payload 长度与 Payload Length 一致。长度不一致时,Datagram 承载的 Frame 必须被丢弃,Stream 承载的 Frame 必须触发协议错误并进入实现定义的错误处理流程