DNP3 系列(四):应用层框架——报文结构、功能码与 IIN
核心目标:掌握 DNP3 应用层的报文结构、功能码全集与 IIN 内部指示字,能独立解读任意应用层报文,为后续对象模型(Part 5)与机制(Part 8-9)打基础。
前置知识:Part 2-3(链路帧与伪传输层);本期所有示例帧均带真实 CRC,可自行用 Wireshark 复现。
4.1 应用层报文结构
4.1.1 报文总览
DNP3 应用层报文 = 应用控制字(AC)+ 功能码(FC)+ 对象头 + 数据体:
┌──────────┬────────┬──────────────┬──────────────────────────┐
│ 应用控制字│ 功能码 │ 对象头 (0~N) │ 数据体 │
│ AC (1B) │ FC(1B) │ 组/变/限定词… │ 对象数据 / 前缀 / 时间戳等 │
└──────────┴────────┴──────────────┴──────────────────────────┘
一个完整链路帧的装载关系(回忆 Part 2-3):
链路帧用户数据 = 传输头(4B) + 应用层报文
= 传输头(4B) + AC(1B) + FC(1B) + 对象头 + 数据体
4.1.2 应用控制字(AC)
┌───────┬───────┬───────┬───────┬──────────────────┐
│ bit7 │ bit6 │ bit5 │ bit4 │ bit3 ─ bit0 │
│ FIR │ FIN │ CON │ UNS │ SEQ(模 16 序号) │
└───────┴───────┴───────┴───────┴──────────────────┘
| 位 | 名称 | 含义 |
|---|---|---|
| bit7 | FIR | 应用报文第一段(配合伪传输层分段) |
| bit6 | FIN | 应用报文最后一段 |
| bit5 | CON | 请求应用层确认 |
| bit4 | UNS | =1 表示这是 Unsolicited 响应(从站主动上报) |
| bit3-0 | SEQ | 应用层序号,请求-响应配对依据 |
注意 AC 与传输头 TH 的相似性:两者都有 FIR/FIN,但 TH 属于伪传输层(管"链路帧的段"),AC 属于应用层(管"应用报文的段")。多帧应用报文时,TH 和 AC 的 FIR/FIN 同步置位。UNS 位是应用层独有的——响应功能码 130(Unsolicited Response)与 UNS=1 配合出现。
4.1.3 应用层 SEQ 与请求-响应配对
- 主站每个新请求的 SEQ 递增(模 16)
- 从站响应的 SEQ 必须等于请求的 SEQ——这是主站匹配响应与请求的手段(尤其多个轮询并发时)
- 重发的请求保持原 SEQ
4.2 功能码全集
4.2.1 请求方向功能码(主站 → 从站)
| 功能码 | 名称 | 说明 |
|---|---|---|
| 0 | CONFIRM | 确认(主要对从站的 Unsolicited 响应) |
| 1 | READ | 读对象(静态/事件/任意对象组) |
| 2 | WRITE | 写对象(死区、时间、私有对象等) |
| 3 | SELECT | 选择(SBO 控制第一阶段) |
| 4 | OPERATE | 执行(SBO 控制第二阶段) |
| 5 | DIRECT OPERATE | 直接操作(一步遥控) |
| 6 | DIRECT OPERATE, NO ACK | 直接操作(免确认) |
| 7 | IMMED FREEZE | 立即冻结(计数器/模拟量) |
| 8 | IMMED FREEZE, NO ACK | 立即冻结(免确认) |
| 9 | FREEZE CLEAR | 冻结并清零 |
| 10 | FREEZE CLEAR, NO ACK | 冻结清零(免确认) |
| 11 | FREEZE AT TIME | 定时冻结 |
| 12 | FREEZE AT TIME, NO ACK | 定时冻结(免确认) |
| 13 | COLD RESTART | 冷重启 |
| 14 | WARM RESTART | 热重启 |
| 15 | INITIALIZE DATA | 初始化数据(清事件缓冲) |
| 16 | INITIALIZE APPLICATION | 初始化应用 |
| 17 | START APPLICATION | 启动应用 |
| 18 | STOP APPLICATION | 停止应用 |
| 19 | SAVE CONFIGURATION | 保存配置 |
| 20 | ENABLE UNSOLICITED | 使能从站主动上报 |
| 21 | DISABLE UNSOLICITED | 禁止主动上报 |
| 22 | ASSIGN CLASS | 分配事件类(Class 1/2/3) |
| 23 | DELAY MEASURE | 延迟测量(时钟同步基础) |
| 24 | RECORD CURRENT TIME | 记录当前时间 |
| 25-31 | OPEN/CLOSE/DELETE/GET INFO/AUTHENTICATE/GET/ABORT FILE | 文件传输系列 |
| 32 | AUTHENTICATE REQUEST | SAv5 认证请求 |
| 33 | AUTHENTICATE RESPONSE | SAv5 认证响应 |
4.2.2 响应方向功能码(从站 → 主站)
| 功能码 | 名称 | 说明 |
|---|---|---|
| 129 (0x81) | RESPONSE | 正常响应(携带数据 + IIN) |
| 130 (0x82) | UNSOLICITED RESPONSE | 主动上报(AC.UNS=1) |
| 131 (0x83) | AUTHENTICATE RESPONSE | SAv5 认证响应 |
| 132 (0x84) | AUTHENTICATE REQUEST | SAv5 认证请求(从站发起挑战) |
读功能码对照:DNP3 的 READ 一次只能读一种"对象组+变化+范围";要读多种数据(遥信+遥测),主站需发多个 READ 或依赖 Class 轮询(一个 READ Class 即可拿全某类事件/静态数据)。这与 IEC 104 总召唤(一次拿全部)思路不同,详见 Part 8。
4.3 IIN 内部指示字
IIN(Internal Indication)是从站每次响应都会携带的 2 字节状态字——它是排障的第一手线索。
4.3.1 IIN1(第一个字节)
| 位 | 值 | 名称 | 含义 | 处理建议 |
|---|---|---|---|---|
| bit0 | 0x01 | ALL_STATIONS | 收到过广播报文 | 通常忽略 |
| bit1 | 0x02 | CLASS1 | Class 1 事件数据可用 | 发 Class 1 事件轮询 |
| bit2 | 0x04 | CLASS2 | Class 2 事件数据可用 | 发 Class 2 事件轮询 |
| bit3 | 0x08 | CLASS3 | Class 3 事件数据可用 | 发 Class 3 事件轮询 |
| bit4 | 0x10 | NEED_TIME | 从站需要时间同步 | 执行时钟同步(Part 9) |
| bit5 | 0x20 | LOCAL_CONTROL | 从站处于本地控制 | 需现场确认,遥控可能被拒 |
| bit6 | 0x40 | DEVICE_TROUBLE | 设备故障 | 检查从站告警 |
| bit7 | 0x80 | DEVICE_RESTART | 设备刚重启 | 通常伴随完整轮询重新初始化 |
4.3.2 IIN2(第二个字节)
按 IEEE 1815-2012 现行定义:
| 位 | 值 | 名称 | 含义 | 处理建议 |
|---|---|---|---|---|
| bit0 | 0x01 | FUNC_NOT_SUPPORTED | 不支持的功能码 | 检查请求功能码 |
| bit1 | 0x02 | OBJECT_UNKNOWN | 未知对象(组/变化/索引) | 检查对象头编码 |
| bit2 | 0x04 | PARAMETER_ERROR | 参数无效 | 检查限定词/范围/控制参数 |
| bit3 | 0x08 | EVENT_BUFFER_OVERFLOW | 事件缓冲溢出(曾丢失事件) | 加大缓冲/提高轮询频率 |
| bit4 | 0x10 | ALREADY_EXECUTING | 已有操作执行中 | 等待完成或中止 |
| bit5 | 0x20 | CONFIG_CORRUPT | 配置损坏 | 重新下发配置 |
| bit6-7 | — | RESERVED | 保留 | — |
版本差异提醒:DNP3 早期规范中 IIN2 的 bit3-7 定义不同(bit3=对象不存在、bit4=无更多数据、bit5=文件错误、bit6=未授权、bit7=事件缓冲溢出)。IEEE 1815-2012 重新定义了 bit3-5,联调时注意从站固件基于哪个版本实现。opendnp3 按 1815-2012 实现。
4.3.3 IIN 的排障价值
现象:轮询事件,从站总是"响应但无数据"
→ 看 IIN1 的 CLASS1/2/3 位:若置位却有数据?若没置位 → 主站轮询的对象组/类不匹配
现象:从站时间总不对
→ IIN1.NEED_TIME 常置 → 时钟同步流程未完成(Part 9)
现象:遥控总失败
→ 检查 IIN1.LOCAL_CONTROL(就地/远方切换)、IIN2.PARAMETER_ERROR(控制码非法)
4.4 请求/响应报文逐字段剖析
4.4.1 完整示例:Read Class 1 轮询
以下是一对真实的请求-响应(主站地址 1,从站地址 10),完整链路帧已含正确 CRC:
主站 → 从站:READ Class 1(功能码 1)
05 64 0D 44 0A 00 01 00 C0 C0 01 3C 02 06 00 00 6A 2A 41 F9
┌───────┬───────┬───────┬────────────┬────────────┬──────────┬──────────────────────────────────┬──────────┐
│ 起始 │ LEN │ CTRL │ DST=从站10 │ SRC=主站1 │ 头CRC │ 用户数据 │ 块CRC │
│ 05 64 │ 0D=13 │ 44 │ 0A 00 │ 01 00 │ 6A 2A │ C0 C0 01 3C 02 06 00 00 │ 41 F9 │
└───────┴───────┴───────┴────────────┴────────────┴──────────┴──────────────────────────────────┴──────────┘
用户数据分解:
C0 → 传输头:FIR=1, FIN=1, SEQ=0(单帧)
C0 → 应用控制字 AC:FIR=1, FIN=1, SEQ=0
01 → 功能码:READ
3C 02 → 对象头:对象组 60 (0x3C)、变化 2 = Class 1 对象
06 → 限定词:2 字节计数 + 1 字节前缀
00 00 → 计数 = 0:读取全部 Class 1 事件
从站 → 主站:RESPONSE(功能码 129,当前无事件)
05 64 0A 80 01 00 0A 00 C0 C0 81 00 00 ED D7 63 17
用户数据分解:
C0 → 传输头:单帧
C0 → AC:FIR=1, FIN=1, SEQ=0(与请求 SEQ 相同 → 配对成功)
81 → 功能码:RESPONSE (129)
00 → IIN1:无待轮询事件、时间已同步、设备正常
00 → IIN2:无错误
(无对象头 → 响应体为空)
用 Wireshark 复现:过滤
dnp3,找到 READ(功能码 1)报文与 RESPONSE(功能码 129)报文,展开DNP3 > Application Layer > Object Header逐字段对照。
4.4.2 一个带数据的响应长什么样
当 Class 1 有 2 个二进制输入事件时,响应会携带对象头 + 数据体(Part 7 详解):
81 (RESPONSE) | IIN1(IIN2) | 对象头 G2V2 | Q=0x06 | 计数=2 | [前缀+数据]×2
↓
前缀 = 对象索引(1B) | 品质(1B) | 绝对时间(6B) | 值(1B)
4.5 应用层可靠性与重试
4.5.1 分层可靠性分工
┌──────────┬──────────────────────────────────────────────┐
│ 层 │ 可靠性职责 │
├──────────┼──────────────────────────────────────────────┤
│ 应用层 │ 请求-响应配对(SEQ)、Unsolicited 的 CONFIRM │
│ 伪传输层 │ 分段重组、段序号连续性 │
│ 链路层 │ 帧 CRC、FCB 去重、ACK/NACK、重发 │
└──────────┴──────────────────────────────────────────────┘
4.5.2 主站侧轮询节奏
主站应用层是"请求-响应"的发起方,需要管理:
| 参数 | 含义 | 工程建议 |
|---|---|---|
| 请求超时 | 等待响应的时间 | 1000-3000ms(串口按帧长折算) |
| 重试次数 | 同一请求的重发 | 2-3 次后报"通信故障" |
| 轮询间隔 | 相邻轮询的间隔 | 事件轮询 1-5s,完整性轮询 5-15min |
| 并发限制 | 同时未确认的请求数 | 通常 1 个(严格主从) |
4.5.3 Unsolicited 的确认语义
从站主动上报(功能码 130,UNS=1)时:
从站发 Unsolicited(TH.CON=1 或 AC.CON=1)→ 主站回 CONFIRM(功能码 0,SEQ 相同)
从站未收到 CONFIRM → 超时重发(重发次数限制后停止)
主站收到 Unsolicited 后必须回 CONFIRM,否则从站会反复重发占用链路。
小结与导航
本期完成了协议栈的最上层:
- 报文结构 —— AC + 功能码 + 对象头 + 数据体;AC 的 FIR/FIN/CON/UNS/SEQ 语义
- 功能码全集 —— 请求方向 0-33、响应方向 129-132,覆盖读/写/控制/冻结/文件/认证
- IIN 内部指示字 —— IIN1/IIN2 共 16 位的含义与排障用法,注意 IEEE 1815-2012 对 IIN2 的重新定义
- 报文剖析 —— 一对真实 Read Class 1 请求-响应帧的逐字节分解
- 可靠性分层 —— 应用层配对、传输层重组、链路层确认的三层协作
下期预告
[Part 5:对象模型与寻址] 将聚焦 DNP3 的灵魂——对象模型:
- 对象头结构与"对象组 + 变化"二维寻址
- 限定词(Qualifier)全表与范围寻址
- 前缀(Prefix)与打包(Packed)编码
- 构造与解析各类对象请求/响应
参考标准
- DNP Users Group Specification Volume 4: Application Layer Protocol
- IEEE 1815-2012 §Application layer(功能码与 IIN 定义)
推荐工具
- Wireshark(过滤
dnp3,展开dnp3.app字段树)—— 应用层逐字段观察- opendnp3 ——
IINField类的位定义与FunctionCode枚举
更多推荐



所有评论(0)