跳转至

DraARLv1 协议规范

本文随 DraARL Server 源码发布,服务端版本以仓库根目录 VERSION 为准。报文线格式仍以固定字符串 DraA 标识; 文末协议修订号描述功能演进,不等同于服务端发布版本。

协议概述

DraARLv1 (Digital Radio Advanced Application Protocol v1) 是 DraARL 平台的设备通信协议,用于设备与服务器之间的实时通信。

报文结构

整体布局

+--------+--------+--------+--------+--------+--------+--------+--------+
| 0-3    | 4-5    | 6-37   | 38-47  | 48     | 49     | 50     | 51-53  |
| Ver    | Length |Username|DevPass | Type   |DevModel| SSID   | DMRID  |
| 4B     | 2B     | 32B    | 10B    | 1B     | 1B     | 1B     | 3B     |
+--------+--------+--------+--------+--------+--------+--------+--------+
| 54-85  | 86-89  | 90+                                              |
|CallSign| Rsv    | DATA                                             |
| 32B    | 4B     | 变长                                             |
+--------+--------+--------+-------------------------------------------------+

固定头部:90 字节
最小报文:90 字节
最大报文:800 字节(UDP 服务端限制,超出将被静默丢弃)

字节序

所有多字节字段使用 大端序 (Big-Endian)


字段详解

偏移 长度 字段名 类型 说明
0 4B Version string 协议版本标识,固定为 "DraA" (0x44726141)
4 2B Length uint16 BE 报文总长度(包含头部和数据)
6 32B Username string 用户名,UTF-8 编码,不足部分用 \0 填充
38 10B DevicePassword string 设备准入密码,ASCII 字母数字,不足部分用 \0 填充
48 1B Type byte 数据包类型,见下方“数据包类型”章节
49 1B DevModel byte 设备型号,见下方“设备型号”章节
50 1B SSID byte 设备子号 (0-255)
51 3B DMRID uint24 BE DMR ID
54 32B CallSign string 业余电台呼号,服务器填充,设备发送时留空
86 4B Reserved uint32 BE 按客户端能力和方向解释,见下述 Reserved 语义
90 变长 DATA []byte 负载数据

Reserved 字段语义

Reserved 只对幽灵 Session 启用,标准 UDP 实体设备继续填 0:

场景 值
UDP 幽灵上行心跳、文本、语音 认证成功响应中的 session_tag
UDP 幽灵认证成功响应 服务端签发的 session_tag
幽灵设备下行 Type 4/5 消息真实 source_group_id
标准实体设备 0

session_tag 只用于在热路径定位已认证 Session。服务端还会联合校验 Username、SSID、DevModel、UDP 端点、账号和 Session 状态;tag 不是凭据,地址变化后必须重新认证。群组 ID 必须可表示为非零 32 位无符号整数。

UDP 幽灵 Session 威胁模型

当前 session_tag 由密码学随机源生成,值非零且在当前在线 Session 集合中唯一。服务端同时要求来源 IP:Port、Username、SSID、DevModel、Session ID、账号和注册表状态全部匹配,并对 IP 与 IP:Port 做包速率限制。因此随机猜中 tag 仍不能从另一个端点接管 Session;Session 删除或重连后,旧 tag 会从运行时索引移除。

该机制用于运行时绑定,不提供传输加密、消息认证或完整重放防护:

  • tag 会出现在认证响应和后续包中,不应视为秘密或 bearer token。
  • Type 1 UDP 认证中的 JWT 以及后续媒体在原始 UDP 链路上均为明文;能观察链路的攻击者可以读取 bearer token、tag 和通信内容。
  • 活跃 Session 内没有媒体包序列号或滑动重放窗口。来自同一已绑定端点、身份和 tag 均匹配的重复心跳、文本或语音包会按普通包处理。
  • 32 位 tag 只保证在线集合内不冲突,Session 结束后理论上可能再次分配相同值。端点和完整身份校验降低旧包误接收概率,但不能提供密码学上的跨 Session 重放隔离。
  • UDP 来源地址可以被伪造;端点绑定能阻止伪造端接收响应,但不能代替链路层或传输层认证。

直接跨不可信网络部署 UDP 幽灵客户端时,应使用 WireGuard、IPsec 等受控加密隧道,并缩短 JWT 有效期、限制入口网络、监控 ghost_packets 拒绝计数。WebSocket 幽灵客户端应只通过 HTTPS/WSS 接入。

未来需要安全漫游或不依赖外层隧道时,应版本化引入带认证的传输方案,例如 DTLS/QUIC,或通过受保护握手派生每 Session 密钥并为媒体包增加单调序列号、认证标签和滑动重放窗口。安全重绑定必须使用旧 Session 密钥或重新认证完成挑战应答,不能只凭 tag 或 client_instance_id 更换端点。该方案需要新的协议版本;不得重新解释当前 90 字节 DraARLv1 实体设备字段。


数据包类型

值 常量名 说明 DATA 内容
1 TypeJWTAuth JWT 认证 JWT Token 字符串(见下方“JWT 认证包格式”章节)
2 TypeHeartbeat 心跳包 可选携带 GPS 位置信息,并可在 DATA 末尾追加 MAC
3 TypeConfig 设备配置 TLV 格式配置数据(见下方“Config 包协议”章节)
4 TypeTextMessage 文本消息 UTF-8 编码文本(见下方“文本消息格式”章节)
5 TypeOpus16K Opus 16K 语音 Opus 16kHz 编码语音帧

文本消息格式 (TypeTextMessage)

当 Type = 4 (TypeTextMessage) 时,DATA 区域携带 UTF-8 编码的文本消息。

DATA 格式

+--------+--------+
| 0      | 1+     |
| Flags  | Text   |
| 1B     | 变长   |
+--------+--------+
偏移 长度 字段名 说明
0 1B Flags 标志位(见下表)
1 变长 Text UTF-8 编码文本内容

Flags 标志位

Bit 名称 说明
0 Urgent 紧急消息(1=紧急)
1 System 系统消息(1=系统)
2-7 Reserved 保留,填0

消息路由规则

  1. 群组消息:发送到设备当前所属群组的所有在线设备
  2. 系统消息:由服务器生成,广播到所有在线设备
  3. 紧急消息:优先显示,可触发设备端告警

示例

普通文本消息:

DATA = [0x00, 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD]
        │     └──────────────────────────────────────┘
        │              "你好" (UTF-8)
        └─ Flags = 0 (普通消息)

紧急系统消息:

DATA = [0x03, 系统消息UTF-8字节...]
        │     └── 紧急系统消息内容
        └─ Flags = 0x03 (Urgent + System)

设备型号

值 常量名 说明 认证方式
0 DevModelUnknown 未知设备 -
1 DevModelESP32Radio ESP32 链路盒子(1W 射频版) 设备密码
2 DevModelESP32NoRadio ESP32 链路盒子(无射频版) 设备密码
100 DevModelWeChatMini 微信小程序 -
101 DevModelAndroid Android 客户端 JWT Token
102 DevModelIOS iOS 客户端 JWT Token
103 DevModelWindows Windows 客户端 JWT Token
104 DevModelMacOS macOS 客户端 (预留) JWT Token
105 DevModelBrowser 浏览器客户端 JWT Token
106 DevModelLegacyBridge 互联设备(历史型号,仅兼容显示) 设备密码
107 DevModelLegacyESP32 ESP32 链路台/手咪(历史型号,仅兼容显示) 设备密码
236 DevModelNSBridge 南山对讲软件桥接器 设备密码
237 DevModelTTBridge 涛涛对讲软件桥接器 设备密码
238 DevModelHTBridge 本视对讲(HT)软件桥接器 设备密码
239 DevModelNRL2Bridge NRL2 系统软件桥接器 设备密码

DevModel 分段规则

范围 含义 说明
0-99 互联产品段 本轮已明确启用:1=ESP32 链路盒子(1W 射频版),2=ESP32 链路盒子(无射频版)
100-105 幽灵/保留段 与 SSID 保留段一致,供 JWT 客户端使用
106-107 历史兼容段 仅保留读取/显示,不再作为正式新型号
108-150 当前无效保留段 不再兼容 110-113
151-255 扩展/待定段 其中 236-239 为当前正式互联网桥软件型号

兼容策略:历史 106/107 设备记录继续可读可显,但不再作为新建/编辑推荐型号。

SSID 分配规则

SSID 范围 用途 认证方式 说明
1-99 普通设备 设备密码 用户自定义,适用于嵌入式设备
100 预留 - 原微信小程序
101-104 UDP 幽灵设备 JWT Token App/PC 客户端,SSID = DevModel
105 Web 幽灵设备 JWT Token WebSocket 客户端
106-254 普通设备扩展 设备密码 用户自定义,适用于嵌入式设备
255 系统历史保留 - 系统保留,用户不可分配

普通设备可用范围: 1-99 和 106-254 (共 248 个),使用设备密码认证 幽灵/保留段范围: 100-105,其中 100 为预留;101-105 使用 JWT Token 认证,且 SSID 固定等于 DevModel


Config 包协议

TypeConfig (Type=3) 仅用于 UDP 普通设备(SSID 1-99, 106-254)的配置同步,不适用于幽灵设备(App/PC 客户端)。

包类型

DATA[0] 方向 说明
0x01 服务端 → 设备 查询配置请求
0x02 双向 配置下发/上报(支持动态下发部分配置项)
0x03 服务端 → 设备 时间同步

TLV 数据格式

当 DATA[0] = 0x02 时,DATA 区域格式:

DATA[0] = 0x02 (配置标识)
DATA[1] = N (配置项数量)
DATA[2...] = TLV 列表

TLV 格式:
┌──────────┬──────────┬─────────────────┐
│ Type (1B)│ Len (1B) │ Value (N bytes) │
└──────────┴──────────┴─────────────────┘

当 DATA[0] = 0x03 时,DATA 区域格式:

DATA[0]    = 0x03 (时间同步标识)
DATA[1]    = 保留字节 (当前实现固定为 0x00)
DATA[2:10] = Unix 时间戳 (8字节,big-endian int64,毫秒)

配置项 Type 定义

Type 配置键 长度 格式 说明
0x01 rx_freq 8 big-endian uint64 接收频率 (Hz)
0x02 tx_freq 8 big-endian uint64 发射频率 (Hz)
0x03 rx_ctcss 4 big-endian float32 接收亚音 (Hz, 0=关闭)
0x04 tx_ctcss 4 big-endian float32 发射亚音 (Hz, 0=关闭)
0x05 sql_level 1 uint8 静噪等级 (0-8)
0x06 power_level 1 uint8 功率等级 (1=低, 3=高;历史值 2 兼容映射为高)
0x07 tx_bandwidth 1 uint8 发射带宽 (1=窄带, 2=宽带)
0x08 rx_tone_mode 1 uint8 接收亚音类型 (0=OFF, 1=CTCSS, 2=CDCSS_N, 3=CDCSS_I)
0x09 rx_tone_value 8 ASCII 接收亚音值(如 88.5、023)
0x0A tx_tone_mode 1 uint8 发射亚音类型 (0=OFF, 1=CTCSS, 2=CDCSS_N, 3=CDCSS_I)
0x0B tx_tone_value 8 ASCII 发射亚音值(如 88.5、023)
0x0C rf_guard_enabled 1 uint8 射频保护开关 (0=关, 1=开)
0x0D rf_guard_single_tx_limit_s 2 big-endian uint16 单次发射上限 (秒, 1-1800)
0x0E rf_guard_window_s 2 big-endian uint16 统计窗口 (秒, 5-3600)
0x0F rf_guard_max_tx_in_window_s 2 big-endian uint16 窗口内累计发射上限 (秒, 1-window_s)
0x10 timestamp 8 big-endian int64 Unix 时间戳 (毫秒)
0x11 adc_gain_db 1 uint8 ADC 增益 (0-24 dB,按最近的 3 dB 档位应用)
0x12 adc_volume 1 uint8 ADC 音量 (0-100)
0x13 dac_volume 1 uint8 DAC 音量 (0-100)
0x14 sql_active_high 1 uint8 SQL 触发极性 (0=低电平触发, 1=高电平触发,仅 DevModel 2)
0x15 ptt_active_high 1 uint8 PTT 激活极性 (0=低电平发送, 1=高电平发送,仅 DevModel 2)

adc_gain_db / adc_volume / dac_volume 是 ESP32 通用音频配置,适用于有射频版 (DevModel=1) 和无射频版 (DevModel=2),不依赖 SA818 模块。

频率配置约定

  • tx_freq / rx_freq 保持手动输入,单位为 Hz。
  • 当前频率设置卡片按 devmodel + cardid 选择实现,本轮默认卡片为 sa818-radio-v1。
  • 亚音采用双层模型:
  • OFF:值固定为 0
  • CTCSS:标准 38 个模拟亚音值
  • CDCSS_N / CDCSS_I:SA818 数字亚音码表,正/反向极性分开
  • 服务端保留 rx_ctcss / tx_ctcss 作为镜像字段:
  • 模拟亚音时回填实际 Hz 值
  • 数字亚音或解析失败时旧字段回退为 0(无亚音)
  • 数字亚音完整下发使用 rx_tone_* / tx_tone_*,设备侧可按 SA818 实际参数格式组合为 023N / 023I 等表达。

示例

下发单个配置项(接收频率 439.500MHz):

DATA = [0x02, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x32, 0x3C, 0xE0]
         │     │     │     │     └─────────────────────────────────────────────┘
         │     │     │     │                       439500000 Hz (0x1A323CE0)
         │     │     │     └─ 长度 8
         │     │     └─ Type=rx_freq
         │     └─ 配置项数量 1
         └─ 配置下发标识

设备上报全部配置:

DATA = [0x02, 0x0F, TLV1, TLV2, TLV3, TLV4, TLV5, TLV6, TLV7, TLV8, TLV9, TLV10, TLV11, TLV12, TLV13, TLV14, TLV15]
              └─ 当前典型全量上报为 15 个配置项

完整数字亚音下发(示例,按 Type 升序):

DATA = [
  0x02, 0x0F,
  0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x32, 0x3C, 0xE0,                    // rx_freq = 439500000
  0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x19, 0xB8, 0x2A, 0xE0,                    // tx_freq = 431500000
  0x03, 0x04, 0x00, 0x00, 0x00, 0x00,                                              // rx_ctcss = 0
  0x04, 0x04, 0x00, 0x00, 0x00, 0x00,                                              // tx_ctcss = 0
  0x05, 0x01, 0x05,                                                                  // sql_level = 5
  0x06, 0x01, 0x03,                                                                  // power_level = 3 (高)
  0x07, 0x01, 0x02,                                                                  // tx_bandwidth = 2 (宽)
  0x08, 0x01, 0x02,                                                                  // rx_tone_mode = CDCSS_N
  0x09, 0x08, 0x30, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,                      // rx_tone_value = "023"
  0x0A, 0x01, 0x03,                                                                  // tx_tone_mode = CDCSS_I
  0x0B, 0x08, 0x34, 0x33, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,                      // tx_tone_value = "431"
  0x0C, 0x01, 0x01,                                                                  // rf_guard_enabled = 1
  0x0D, 0x02, 0x00, 0x1E,                                                            // rf_guard_single_tx_limit_s = 30
  0x0E, 0x02, 0x01, 0x2C,                                                            // rf_guard_window_s = 300
  0x0F, 0x02, 0x00, 0x3C                                                             // rf_guard_max_tx_in_window_s = 60
]

说明:服务端编码时遍历 map,TLV 实际顺序可变;设备应按 Type 解析,不应依赖顺序。

同步流程

设备上线同步:

设备上线 (认证成功)
    │
    ├─ 数据库有配置记录
    │   └─ 服务端发送 Config(0x02 + TLV) 下发配置
    │
    └─ 数据库无配置记录
        └─ 服务端发送 Config(0x01) 查询请求
            │
            └─ 设备上报 Config(0x02 + TLV)
                │
                └─ 服务端解析存储到数据库

控制台修改同步:

用户通过控制台修改配置
    │
    ├─ 保存变更到数据库
    │
    └─ 设备在线?
        ├─ 是 → 发送 Config(0x02 + 变更项TLV) 动态下发
        └─ 否 → 等待设备上线时同步

时间同步:

服务端会在普通 UDP 设备上线完成配置同步后发送一次时间同步包;设备上报配置后,服务端也会再发送一次时间同步包作为 ACK。

DATA = [0x03, 0x00, ts(8 bytes)]
        │     │     └───────────── Unix 时间戳(毫秒,大端序)
        │     └─ 保留字节(当前实现固定为 0)
        └─ 时间同步标识

特殊数据包格式

JWT 认证包格式 (TypeJWTAuth)

当 Type = 1 (TypeJWTAuth) 时,用于幽灵设备(App/PC 客户端)的 JWT Token 认证。

认证请求

幽灵客户端必须把版本化 UTF-8 JSON 放入 DATA。直接把 JWT Token 原文放入 DATA 的旧格式不再接受:

┌──────────────────────────────────────────────────────────────┐
│  Header (90 字节)                                            │
│    Version: "DraA"                                           │
│    Length: 90 + Token长度                                    │
│    Username: 空 (可选,用于日志)                              │
│    DevicePassword: 空                                        │
│    Type: 1 (TypeJWTAuth)                                     │
│    DevModel: 101/102/103/104                                 │
│    SSID: 0 (服务器会用 DevModel 作为 SSID)                    │
│    DMRID: 0                                                  │
│    CallSign: 空 (服务器填充)                                  │
│    Reserved: 认证请求填 0                                     │
├─────────────────────────────────────────────────────────—────┤
│  DATA 区域                                                    │
│    版本化认证 JSON                                             │
└──────────────────────────────────────────────────────────────┘
{
  "version": 1,
  "token": "<JWT access token>",
  "client_instance_id": "41a065d7-f9e1-4785-bbcb-22c3ca8784ad",
  "capabilities": ["multi_receive_v1", "source_group_v1"]
}
  • client_instance_id 是安装范围持久化的随机 UUID,不得使用 IMEI、MAC 或 Android ID。
  • multi_receive_v1 与 source_group_v1 必须同时声明,缺少任一能力时认证失败。
  • 客户端允许同账号多端在线;同一安装实例重连只替换自己的旧 Session。
  • Type 1 只用于 UDP 幽灵型号 101-104。Web 105 在 WebSocket HTTP 握手中通过 HttpOnly Cookie 认证。

认证响应

服务器返回认证结果:

┌──────────────────────────────────────────────────────────────┐
│  Header (90 字节)                                            │
│    Version: "DraA"                                           │
│    Length: 90 + DATA长度                                     │
│    Username: 回显用户名                                       │
│    DevicePassword: 空                                        │
│    Type: 1                                                   │
│    DevModel: 回显                                            │
│    SSID: 服务器分配 (等于 DevModel)                           │
│    DMRID: 0                                                  │
│    CallSign: 成功时填充用户呼号,失败时为空                    │
│    Reserved: 成功响应为 session_tag                            │
├──────────────────────────────────────────────────────────────┤
│  DATA 区域                                                    │
│    [0]: 状态码                                                │
│         0 = 认证成功                                          │
│         1 = Token 无效或过期                                  │
│         2 = 用户不存在                                        │
│         3 = 用户已禁用                                        │
│         4 = 用户未审核                                        │
│         5 = 无效的设备型号 (非 101-104)                       │
│    [1:]: 成功时为空,失败时为错误消息文本                      │
└──────────────────────────────────────────────────────────────┘

现代成功响应仍保留 DATA[0] = 0,随后为 JSON,同时 Reserved 携带相同的 session_tag:

{
  "version": 1,
  "session_id": "7fbbbd37-9207-4df2-b79d-e400e0a09fd0",
  "session_tag": 270544961,
  "client_instance_id": "41a065d7-f9e1-4785-bbcb-22c3ca8784ad",
  "tx_group_id": 1001,
  "rx_group_ids": [1001, 1002]
}

后续现代 UDP 上行包必须使用服务端返回的 Username/SSID/DevModel,并在 Reserved 中携带 session_tag。媒体包不能声明任意发送群组,服务端始终取 Session 的唯一 tx_group_id。

认证流程

JWT认证流程

注意:JWT 认证包仅用于 UDP 幽灵设备(DevModel 101-104)。普通设备(如 ESP32、互联设备、各类对讲桥接器)仍使用设备密码认证并严格保持单端;Web 幽灵设备在 WebSocket 握手中认证。

Opus 语音格式 (TypeOpus16K)

当 Type = 5 (TypeOpus16K) 时,DATA 区域携带 Opus 编码的语音数据。

编码参数

参数 值 说明
采样率 16000 Hz 16kHz 宽带语音
声道数 1 单声道
帧时长 60 ms 每个 Opus 帧 60ms
每帧采样数 960 16000 × 0.06
比特率 16-32 kbps VOIP 模式

合并帧格式(WebSocket 客户端)

为优化弱网性能,WebSocket 客户端采用 2 帧合并发送 策略:

  • 每 120ms 发送一个数据包
  • 每个数据包包含 2 个 60ms 的 Opus 帧
  • 使用帧长度前缀标识每帧边界

DATA 区域格式:

+-------------+-------------+-------------+-------------+
| Frame1 Len  | Frame1 Data | Frame2 Len  | Frame2 Data |
| 2B          | N bytes     | 2B          | M bytes     |
+-------------+-------------+-------------+-------------+
字段 长度 说明
Frame1 Len 2B 第 1 帧长度(大端序 uint16)
Frame1 Data 变长 第 1 帧 Opus 数据(60ms)
Frame2 Len 2B 第 2 帧长度(大端序 uint16)
Frame2 Data 变长 第 2 帧 Opus 数据(60ms)

性能优化说明

项目 优化前 优化后
帧时长 20ms 60ms
发送间隔 20ms 120ms
每秒发包数 50 ~8.3
单包大小 110-170B 330-570B
头部开销占比 50-80% ~25-35%
额外延迟 - +100ms

注意:120ms 延迟对半双工语音通信完全可接受,且大幅降低弱网丢包影响。

解码兼容性

接收端应支持:

  1. 合并帧格式:按长度前缀解析多个 Opus 帧
  2. 单帧格式:兼容无长度前缀的单帧数据(用于 UDP 设备直发)

判断逻辑:

  • 从 DATA 起始位置开始,按 2B 长度 + 帧数据 循环尝试解析
  • 若首个长度字段非法(0、>1000、或越界),回退为单帧格式
  • 若已成功解析出至少 1 帧,后续出现非法长度时停止继续解析并保留已解析帧

心跳包扩展格式

心跳包的 DATA 区域可携带位置信息:

+--------+--------+--------+--------+
| 0-7    | 8-15   | 16-23  | 24+    |
| Lat    | Lon    | Alt    | Extra  |
| 8B     | 8B     | 8B     | 变长    |
+--------+--------+--------+--------+
偏移 长度 字段名 说明
0 8B Latitude 纬度 (float64 BE)
8 8B Longitude 经度 (float64 BE)
16 8B Altitude 海拔高度 (float64 BE)

扩展约定:

  • 固定头部保持不变:MAC 仅允许追加在心跳包 DATA 区域,90 字节 header 的字段、偏移、长度均不变。
  • DATA[24:] 为可选扩展区;当前约定可放置设备 MAC 文本,格式为 ASCII 大写冒号分隔,例如 AA:BB:CC:DD:EE:FF。
  • 旧设备如果不携带 MAC,服务端仍按旧规则兼容处理。
  • 如果设备携带合法 MAC,服务端会将其作为“同一物理设备快速重连”的辅助判据。

示例:

DATA = [24字节GPS][ASCII "AA:BB:CC:DD:EE:FF"]

设备认证流程

认证方式(三轨制)

平台支持三种认证方式,根据设备类型和场景自动选择:

设备类型 SSID 范围 认证方式 说明
普通设备 1-99, 106-254 设备密码 ESP32、互联设备、对讲桥接器等嵌入式/桥接设备
幽灵/保留段 100-105 100 预留;101-105 为 JWT Token 100 为预留位;101-104 为 App/PC 客户端;105 为 Web
新设备绑定 1-99, 106-254 动态码绑定 新设备首次上线,通过动态码完成账号绑定

设计说明:

  • 普通设备(如 ESP32 链路台/手咪、互联设备、对讲桥接器)性能有限,不适合进行 JWT 解析,继续使用传统的设备密码认证
  • 幽灵设备(App/PC 客户端)性能充足,使用 JWT 认证可以复用现有登录接口,无需预共享密钥
  • 动态码绑定:新设备首次上线时,通过 HTTP 接口完成与用户账号的绑定,绑定成功后获取认证信息
  • 普通 UDP 在线冲突:同一用户同一 SSID 只允许一台在线,新地址冲突时新设备收到拒绝响应,旧设备保持在线
  • 普通 UDP 同 MAC 快速重连:如果新心跳携带的 MAC 与当前在线实例记录的 MAC 一致,则视为同一物理设备短暂断线后的重连,允许直接接管新地址
  • 普通 UDP MAC 映射生命周期:设备在线时记录 owner_id + ssid -> mac;设备被彻底判定离线后删除该映射
  • 幽灵设备多端:携带稳定 client_instance_id 和版本化能力的幽灵客户端按 Session 隔离,同一账号同平台可以多端在线
  • 旧幽灵协议不兼容:raw-JWT、缺少实例 ID 或缺少现代能力的客户端认证失败,不再进入单端降级路径
  • 多收单发:每个 Session 有一个 tx_group_id 和多个 rx_group_ids;发送频道必须包含在接收集合中

1. 普通设备上线认证(设备密码)

普通设备认证流程

2. 幽灵设备认证(JWT Token)

幽灵设备(App/PC 客户端)使用 JWT Token 进行认证,详见下方“JWT 认证包格式 (TypeJWTAuth)”章节。

3. 新设备动态码绑定流程

新设备首次上线时,通过动态码绑定流程完成与用户账号的关联。此流程适用于没有密码输入功能的设备(如无键盘/触摸屏的嵌入式硬件),用户通过 Web 端输入设备显示的动态码完成绑定。

绑定流程时序图

设备动态码绑定流程

接口说明

接口 方法 认证 说明
/api/device/pre-check POST 无 设备上电后检查存储的账号密码是否有效
/api/device/request-code POST 无 请求生成 6 位动态码
/api/device/bind POST JWT 用户通过动态码绑定设备
/api/device/submit-config POST JWT 用户提交设备配置(SSID)
/api/device/confirm-bind POST 无 设备轮询获取绑定状态和配置

详细步骤

步骤 1:预检查 (pre-check)

设备上电后,使用本地存储的账号密码(如果有)进行预检查:

  • 验证成功:返回 status: "authenticated",设备可直接进入 UDP 认证流程
  • 验证失败/无存储:返回 status: "need_bind",设备进入动态码绑定流程

步骤 2:请求动态码 (request-code)

设备请求生成动态码:

  • 服务器生成 6 位数字动态码
  • 动态码有效期 5 分钟
  • 设备在屏幕/LED 上显示动态码

步骤 3:用户绑定 (bind)

用户在 Web 端输入设备显示的动态码:

  • 验证动态码有效性和设备状态
  • 将设备 MAC 与用户账号关联
  • 返回设备 MAC 和用户呼号

步骤 4:提交配置 (submit-config)

用户为设备配置 SSID:

  • SSID 范围:1-99 或 106-254(普通设备可用范围)
  • 服务器将用户名、设备密码、SSID、DMRID 写入设备配置

步骤 5:确认绑定 (confirm-bind)

设备轮询此接口获取配置:

  • 状态 waiting:等待用户完成绑定
  • 状态 ready:绑定完成,返回认证信息

安全机制

  • 动态码单次使用:绑定成功后动态码立即失效
  • 有效期限制:动态码 5 分钟过期,绑定状态 10 分钟过期
  • 限速保护:各接口均有独立的限速策略
  • 账号状态校验:用户必须已审核通过才能绑定设备

4. 认证失败处理

  • 当前实现中,连续认证失败第 4 次触发首轮封禁:10 秒
  • 后续继续失败依次升级:30 秒、60 秒、300 秒(阶梯递增)

封禁 Key:IP + Username


语音转发流程

语音转发流程


字符编码规则

字段 编码 字符集 说明
Username UTF-8 字母、数字、下划线 用户名
DevicePassword ASCII 大小写字母、数字 设备准入密码,6-10 位
CallSign ASCII 大写字母、数字 业余电台呼号
TextMessage UTF-8 任意 Unicode 文本消息

安全限制

UDP 服务端实施以下安全策略,超出限制的数据包将被静默丢弃:

限制项 阈值 说明
最大包体 800 字节 含 90 字节头部,防止异常大包攻击
包速率 150 包/秒 按 IP+Port 维度限速,兼顾 FRP 隧道共享 IP 场景

注意:正常语音通信(合并帧 ~8.3 包/秒,单帧 ~16.7 包/秒)远低于限速阈值,不会受到影响。 150 包/秒的阈值设计考虑了:

  • FRP/NAT 场景下多设备共享同一出口 IP
  • 客户端网络抖动后的"追赶发送"突发场景
  • 60-180ms 大包架构下丢包的严重影响

版本历史

版本 日期 说明
v1.0 2026-03 初始版本,替代 NRL2 协议
v1.1 2026-03 移除 G.711 编解码支持,统一使用 Opus 16K 格式
v1.2 2026-03 简化协议头,移除 Status 和 SeqNum 字段,头部从 93 字节简化为 90 字节
v1.3 2026-03 优化弱网性能:Opus 帧时长从 20ms 改为 60ms,WebSocket 客户端采用 2 帧合并发送(120ms 间隔)
v1.4 2026-03 新增 JWT 认证方式(Type=1),支持幽灵设备(App/PC 客户端)接入;新增设备型号 104 (macOS)、107 (ESP32);定义 SSID 分配规则
v1.5 2026-03 性能优化:PTT 会话阈值、限速器和直接发送路径;补全 WS→UDP Ghost 路由与时长统计
v1.6 2026-03 新增 Config 包协议 (Type=3),支持 UDP 普通设备配置同步(TLV 格式),包含查询、下发、上报、时间同步四种操作
v1.7 2026-03 新增动态码绑定流程,支持无输入能力的普通设备通过 Web 端完成账号绑定
v1.8 2026-04 新增设备型号 236/237/238/239,分别对应南山、涛涛、本视对讲(HT)、NRL2 系统软件桥接器
v1.9 2026-04 重构 DevModel 分段与动态绑定 SSID 规则;新增 rx_tone_* / tx_tone_* 数字亚音表达;SQL 收敛为 0-8;功率统一为高/低两档并兼容历史中档
v1.10 2026-04 Config 包新增 rf_guard_* 四项射频保护配置,支持单次发射上限、统计窗口与窗口内累计发射上限
v1.11 2026-08 新增版本化幽灵认证、客户端实例与 Session、UDP session tag、多收单发及下行来源频道;标准实体设备保持原单端语义