3. Modbus TCP 协议帧结构:MBAP 报文头与 PDU
Modbus TCP 协议帧是 Modbus 协议在 TCP/IP 网络上的封装形式。与传统的串行链路(RTU/ASCII)不同,TCP 帧去除了校验码(由 TCP 协议保证),并引入了专用的报文头(MBAP)来标识事务和单元。理解帧结构是开发采集程序的基础。
3.1 整体帧结构
一个完整的 Modbus TCP 请求/响应帧由两部分组成:MBAP 报文头(7 字节)和 PDU(协议数据单元,可变长度)。
| 组成部分 | 长度 | 描述 |
|---|---|---|
| MBAP 报文头 | 7 字节 | 事务标识符、协议标识符、长度、单元标识符 |
| PDU | ≥ 2 字节 | 功能码(1 字节)+ 数据(可变) |
关键区别:Modbus TCP 帧没有 CRC/LRC 校验,因为 TCP 协议本身已提供可靠传输和校验。MBAP 头中的“长度”字段替代了串行帧中的帧结束判定。
3.2 MBAP 报文头详解
MBAP(Modbus Application Protocol)报文头是 TCP 帧特有的 7 字节头部,用于多事务管理和设备寻址。
| 字段 | 字节数 | 说明 | 示例值(十六进制) |
|---|---|---|---|
| 事务标识符 | 2 | 用于请求/响应配对。客户端递增,服务器原样返回。 | 0x0001 |
| 协议标识符 | 2 | 固定为 0x0000,表示 Modbus 协议。 |
0x0000 |
| 长度 | 2 | 后续字节数(从单元标识符开始到帧尾)。 | 0x0006 |
| 单元标识符 | 1 | 用于标识远程从站(类似 RTU 的从站地址)。 | 0x01 |
事务标识符的作用:当客户端同时发送多个请求时,通过事务标识符匹配响应。例如,请求 A 使用 0x0001,请求 B 使用 0x0002,服务器返回的响应中会携带对应的事务标识符,客户端据此区分。
3.3 PDU:功能码与数据
PDU(Protocol Data Unit)是 Modbus 协议的核心载荷,与传输介质无关。它由 1 字节功能码和可变长度的数据组成。
- 功能码(1 字节):指示操作类型,如读线圈(0x01)、读保持寄存器(0x03)、写单个寄存器(0x06)等。
- 数据(可变):包含起始地址、数量、写入值等参数。响应数据则包含字节计数和实际寄存器值。
| 方向 | 功能码 | 数据内容 | 示例(十六进制) |
|---|---|---|---|
| 请求 | 0x03 |
起始地址(2 字节)+ 寄存器数量(2 字节) | 03 00 00 00 0A |
| 响应 | 0x03 |
字节计数(1 字节)+ 寄存器值(N 字节) | 03 14 00 01 00 02 ... |
3.4 完整帧示例:读取保持寄存器
以下是一个完整的 Modbus TCP 请求帧,用于读取从站 1 的保持寄存器(起始地址 0,数量 10):
事务标识符 协议标识符 长度 单元标识符 功能码 起始地址 寄存器数量
00 01 00 00 00 06 01 03 00 00 00 0A
对应的响应帧:
事务标识符 协议标识符 长度 单元标识符 功能码 字节计数 寄存器值(20 字节)
00 01 00 00 00 17 01 03 14 00 01 00 02 ...
注意:长度字段的值是“从单元标识符开始到帧尾的字节数”。在请求帧中,长度 = 1(单元标识符)+ 1(功能码)+ 4(数据)= 6(0x0006)。在响应帧中,长度 = 1 + 1 + 1 + 20 = 23(0x0017)。
3.5 MBAP 与 PDU 组合关系图
下图展示了 Modbus TCP 帧的完整结构,以及 MBAP 头与 PDU 的拼接方式:
3.6 请求与响应帧对比
下表对比了读取保持寄存器(功能码 0x03)的请求帧和响应帧结构:
| 字段 | 请求帧(客户端 → 服务器) | 响应帧(服务器 → 客户端) |
|---|---|---|
| 事务标识符 | 客户端生成(如 0x0001) | 原样返回 |
| 协议标识符 | 0x0000 | 0x0000 |
| 长度 | 0x0006 | 0x0017(示例) |
| 单元标识符 | 目标从站地址(如 0x01) | 原样返回 |
| 功能码 | 0x03 | 0x03 |
| 数据 | 起始地址(2 字节)+ 数量(2 字节) | 字节计数(1 字节)+ 寄存器值(N 字节) |
实践要点:在编写采集程序时,务必正确设置事务标识符(用于多线程并发请求)和长度字段(避免粘包问题)。单元标识符通常设为 0x01 或 0xFF(广播),具体取决于从站配置。