Modbus TCP通信协议完整字节级详解
📌 本文提供Modbus TCP协议每个字节的详细说明,包括帧头、功能码、数据字段的精确位置和含义,是调试和开发Modbus TCP的必备参考手册。
1. 📊 Modbus TCP帧结构总览
1.1 完整帧格式(字节级分解)
字节位置: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... N
字段: [---MBAP头(7字节)---] [---------PDU(变长)---------]
T T P P L L U F
I I I I H L I C
D D H L H L D
H L + 功能码 + 数据
1.2 MBAP头固定格式(7字节)
| 字节偏移 | 字段名称 | 长度 | 固定值 | 说明 |
|---|---|---|---|---|
| 0 | 事务标识符高字节 | 1字节 | 0x00-0xFF | 请求ID高8位 |
| 1 | 事务标识符低字节 | 1字节 | 0x00-0xFF | 请求ID低8位 |
| 2 | 协议标识符高字节 | 1字节 | 0x00 | 固定为0 |
| 3 | 协议标识符低字节 | 1字节 | 0x00 | 固定为0 |
| 4 | 长度字段高字节 | 1字节 | 0x00 | 后续字节数高8位 |
| 5 | 长度字段低字节 | 1字节 | 0xXX | 后续字节数低8位 |
| 6 | 单元标识符 | 1字节 | 0x01-0xFF | 从站地址 |
重要公式:长度字段 = 单元标识符(1) + 功能码(1) + 数据字节数
或者更准确地:
长度字段 = 从单元标识符开始到报文结束的总字节数
2. 📖 功能码0x03:读保持寄存器
2.1 请求帧(客户端→服务器)
字节: 0 1 2 3 4 5 6 7 8 9 10 11
值: [T T P P L L U F S S Q Q]
I I I I H L I C A A T T
D D H L H L D H L H L
示例:00 01 00 00 00 06 01 03 00 0A 00 03
| 字节偏移 | 字段名称 | 长度 | 示例值 | 详细说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0001 | 每次请求递增 |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | Modbus TCP固定 |
| 4-5 | 长度 | 2字节 | 0x0006 | 后续6字节 |
| 6 | 单元标识符 | 1字节 | 0x01 | 设备地址1 |
| 7 | 功能码 | 1字节 | 0x03 | 读保持寄存器 |
| 8-9 | 起始地址 | 2字节 | 0x000A | 从地址10开始 |
| 10-11 | 寄存器数量 | 2字节 | 0x0003 | 读取3个寄存器 |
帧长度:固定12字节
2.2 响应帧(服务器→客户端)
字节: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
值: [T T P P L L U F B D D D D D]
I I I I H L I C C
D D H L H L D + 寄存器数据
示例:00 01 00 00 00 09 01 03 06 00 12 00 34 00 56
| 字节偏移 | 字段名称 | 长度 | 示例值 | 详细说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0001 | 同请求帧 |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x0009 | 后续9字节(1+1+1+6) |
| 6 | 单元标识符 | 1字节 | 0x01 | |
| 7 | 功能码 | 1字节 | 0x03 | |
| 8 | 字节计数 | 1字节 | 0x06 | 寄存器数×2=6 |
| 9-10 | 寄存器1值 | 2字节 | 0x0012 | 高字节在9,低字节在10 |
| 11-12 | 寄存器2值 | 2字节 | 0x0034 | 寄存器11的值 |
| 13-14 | 寄存器3值 | 2字节 | 0x0056 | 寄存器12的值 |
长度计算:7 + 1 + 1 + 1 + (寄存器数×2) = 9 + 6 = 15字节
3. 🔍 功能码0x04:读输入寄存器
3.1 请求帧
字节: 0 1 2 3 4 5 6 7 8 9 10 11
值: [T T P P L L U F S S Q Q]
I I I I H L I C A A T T
D D H L H L D H L H L
示例:00 02 00 00 00 06 01 04 00 05 00 02
| 字节偏移 | 字段名称 | 长度 | 示例值 | 说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0002 | ID=2 |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x0006 | |
| 6 | 单元标识符 | 1字节 | 0x01 | |
| 7 | 功能码 | 1字节 | 0x04 | 读输入寄存器 |
| 8-9 | 起始地址 | 2字节 | 0x0005 | 地址5 |
| 10-11 | 寄存器数量 | 2字节 | 0x0002 | 读取2个 |
3.2 响应帧
字节: 0 1 2 3 4 5 6 7 8 9 10 11 12
值: [T T P P L L U F B D D D D]
示例:00 02 00 00 00 07 01 04 04 12 34 56 78
| 字节偏移 | 字段名称 | 长度 | 示例值 | 说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0002 | |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x0007 | 后续7字节 |
| 6 | 单元标识符 | 1字节 | 0x01 | |
| 7 | 功能码 | 1字节 | 0x04 | |
| 8 | 字节计数 | 1字节 | 0x04 | 2×2=4字节 |
| 9-10 | 寄存器1值 | 2字节 | 0x1234 | 高字节在9 |
| 11-12 | 寄存器2值 | 2字节 | 0x5678 | 高字节在11 |
字节12:最后一个数据的低字节
4. ✍️ 功能码0x06:写单个寄存器
4.1 请求帧
字节: 0 1 2 3 4 5 6 7 8 9 10 11
值: [T T P P L L U F S S D D]
I I I I H L I C A A H L
D D H L H L D H L
示例:00 03 00 00 00 06 01 06 00 0F 12 34
| 字节偏移 | 字段名称 | 长度 | 示例值 | 详细说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0003 | ID=3 |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x0006 | 后续6字节 |
| 6 | 单元标识符 | 1字节 | 0x01 | |
| 7 | 功能码 | 1字节 | 0x06 | 写单个寄存器 |
| 8-9 | 寄存器地址 | 2字节 | 0x000F | 寄存器15 |
| 10-11 | 寄存器值 | 2字节 | 0x1234 | 写入值0x1234 |
字节10:写入值的高字节
字节11:写入值的低字节
4.2 响应帧
重要:响应帧与请求帧完全相同
00 03 00 00 00 06 01 06 00 0F 12 34
字节位置完全对应
5. 📝 功能码0x10:写多个寄存器
5.1 请求帧
字节: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
值: [T T P P L L U F S S Q Q B D D D D D D]
I I I I H L I C A A T T C
D D H L H L D H L H L
示例:00 04 00 00 00 0B 01 10 00 20 00 03 06 11 11 22 22 33 33
| 字节偏移 | 字段名称 | 长度 | 示例值 | 详细说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0004 | ID=4 |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x000B | 后续11字节 |
| 6 | 单元标识符 | 1字节 | 0x01 | |
| 7 | 功能码 | 1字节 | 0x10 | 写多个寄存器 |
| 8-9 | 起始地址 | 2字节 | 0x0020 | 从地址32开始 |
| 10-11 | 寄存器数量 | 2字节 | 0x0003 | 写入3个寄存器 |
| 12 | 字节计数 | 1字节 | 0x06 | 3×2=6字节 |
| 13-14 | 寄存器1值 | 2字节 | 0x1111 | 字节13=高,14=低 |
| 15-16 | 寄存器2值 | 2字节 | 0x2222 | 字节15=高,16=低 |
| 17-18 | 寄存器3值 | 2字节 | 0x3333 | 字节17=高,18=低 |
字节12是关键:它告诉服务器后面有多少数据字节
5.2 响应帧
字节: 0 1 2 3 4 5 6 7 8 9 10 11
值: [T T P P L L U F S S Q Q]
示例:00 04 00 00 00 06 01 10 00 20 00 03
| 字节偏移 | 字段名称 | 长度 | 示例值 | 说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 0x0004 | |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x0006 | 后续6字节 |
| 6 | 单元标识符 | 1字节 | 0x01 | |
| 7 | 功能码 | 1字节 | 0x10 | |
| 8-9 | 起始地址 | 2字节 | 0x0020 | 回显地址 |
| 10-11 | 寄存器数量 | 2字节 | 0x0003 | 回显数量 |
6. ❌ 错误响应格式(所有功能码通用)
6.1 错误响应帧格式
字节: 0 1 2 3 4 5 6 7 8
值: [T T P P L L U E C]
I I I I H L I F O
D D H L H L D C D
示例:00 01 00 00 00 03 01 83 02
| 字节偏移 | 字段名称 | 长度 | 示例值 | 详细说明 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2字节 | 同请求 | 与请求相同 |
| 2-3 | 协议标识符 | 2字节 | 0x0000 | |
| 4-5 | 长度 | 2字节 | 0x0003 | 后续3字节 |
| 6 | 单元标识符 | 1字节 | 同请求 | |
| 7 | 错误功能码 | 1字节 | 0x8X | 原功能码+0x80 |
| 8 | 异常码 | 1字节 | 错误码 | 具体错误原因 |
6.2 各功能码错误码对应
| 原功能码 | 错误功能码 | 含义 |
|---|---|---|
| 0x03 | 0x83 | 读保持寄存器错误 |
| 0x04 | 0x84 | 读输入寄存器错误 |
| 0x06 | 0x86 | 写单个寄存器错误 |
| 0x10 | 0x90 | 写多个寄存器错误 |
6.3 异常码详解
| 异常码 | 名称 | 含义 |
|---|---|---|
| 0x01 | 非法功能码 | 服务器不支持此功能 |
| 0x02 | 非法数据地址 | 请求地址不存在 |
| 0x03 | 非法数据值 | 数据值超出范围 |
| 0x04 | 从站设备故障 | 服务器执行失败 |
| 0x05 | 确认 | 服务器已接受请求正在处理 |
| 0x06 | 从站设备忙 | 服务器正忙 |
| 0x07 | 负确认 | 服务器无法执行 |
| 0x08 | 存储奇偶性错 | 内存校验错误 |
7. 📊 字节偏移速查表
7.1 通用位置(所有帧)
| 字节 | 字段 | 长度 | 值域 | 备注 |
|---|---|---|---|---|
| 0-1 | 事务标识符 | 2 | 0x0000-0xFFFF | 客户端维护 |
| 2-3 | 协议标识符 | 2 | 0x0000 | 固定 |
| 4-5 | 长度字段 | 2 | 可变 | 后续字节数 |
| 6 | 单元标识符 | 1 | 0x01-0xFF | 从站地址 |
| 7 | 功能码 | 1 | 见功能码表 |
7.2 各功能码数据起始位置
| 功能码 | 操作 | 数据开始字节 | 示例偏移 | 说明 |
|---|---|---|---|---|
| 0x03 | 读保持寄存器请求 | 8 | 8-9:地址, 10-11:数量 | |
| 0x03 | 读保持寄存器响应 | 9 | 9-10:第一个值 | 字节8是计数 |
| 0x04 | 读输入寄存器请求 | 8 | 同0x03 | |
| 0x04 | 读输入寄存器响应 | 9 | 同0x03 | |
| 0x06 | 写单个寄存器请求 | 8 | 8-9:地址, 10-11:值 | |
| 0x06 | 写单个寄存器响应 | 8 | 同请求 | 完全回显 |
| 0x10 | 写多个寄存器请求 | 8 | 8-9:地址, 10-11:数量, 12:计数, 13+:数据 | 字节13是第一个数据高字节 |
| 0x10 | 写多个寄存器响应 | 8 | 8-9:地址, 10-11:数量 | 只回显地址和数量 |
7.3 关键字节位置记忆
-
字节6:总是单元标识符
-
字节7:总是功能码
-
字节8-9:通常是地址(0x03/0x04/0x06/0x10请求)
-
字节10-11:通常是数量或数据(0x03/0x04数量,0x06数据)
-
字节12:0x10请求的字节计数
-
字节13:0x10请求的第一个数据高字节
8. 🎯 字节级分析示例
示例1:0x10写多个寄存器帧分析
收到帧: 00 04 00 00 00 0B 01 10 00 20 00 03 06 11 11 22 22 33 33
逐字节分析:
字节0: 0x00 事务ID高
字节1: 0x04 事务ID低 → ID=0x0004
字节2: 0x00 协议ID高
字节3: 0x00 协议ID低 → 协议=0x0000
字节4: 0x00 长度高
字节5: 0x0B 长度低 → 长度=0x000B=11字节
字节6: 0x01 单元标识符 → 设备地址1
字节7: 0x10 功能码 → 写多个寄存器
字节8: 0x00 起始地址高
字节9: 0x20 起始地址低 → 地址=0x0020=32
字节10: 0x00 寄存器数量高
字节11: 0x03 寄存器数量低 → 数量=0x0003=3个
字节12: 0x06 字节计数 → 3×2=6字节
字节13: 0x11 第一个寄存器值高字节
字节14: 0x11 第一个寄存器值低字节 → 值=0x1111=4369
字节15: 0x22 第二个寄存器值高字节
字节16: 0x22 第二个寄存器值低字节 → 值=0x2222=8738
字节17: 0x33 第三个寄存器值高字节
字节18: 0x33 第三个寄存器值低字节 → 值=0x3333=13107
示例2:0x03读保持寄存器响应分析
收到帧: 00 01 00 00 00 09 01 03 06 00 12 00 34 00 56
字节0-1: 0x0001 事务ID
字节2-3: 0x0000 协议ID
字节4-5: 0x0009 长度 → 后续9字节
字节6: 0x01 单元ID
字节7: 0x03 功能码 → 读保持寄存器
字节8: 0x06 字节计数 → 6字节数据
字节9: 0x00 第一个值高 → 寄存器1值高
字节10: 0x12 第一个值低 → 寄存器1值=0x0012=18
字节11: 0x00 第二个值高 → 寄存器2值高
字节12: 0x34 第二个值低 → 寄存器2值=0x0034=52
字节13: 0x00 第三个值高 → 寄存器3值高
字节14: 0x56 第三个值低 → 寄存器3值=0x0056=86
9. ✅ 调试检查清单
9.1 发送前检查
-
\] 事务ID是否正确递增
-
\] 长度字段计算是否正确
-
\] 地址是否在有效范围
-
\] 字节计数(0x10)是否正确
-
\] 事务ID是否匹配请求
-
\] 字节计数是否匹配
-
\] 异常码处理
| 问题现象 | 可能原因 | 检查字节 |
|---|---|---|
| 无响应 | 网络问题/设备离线 | 所有 |
| 错误响应0x83/0x84 | 功能码错误 | 字节7 |
| 错误响应0x02 | 地址错误 | 字节8-9 |
| 错误响应0x03 | 数据值错误 | 字节10-11或13+ |
| 数据不全 | 数量太多 | 字节10-11 |
| 字节不匹配 | 长度错误 | 字节4-5 |
📊 总结:核心字节位置
| 功能 | 关键字节 | 作用 | 示例 |
|---|---|---|---|
| 所有请求 | 字节6 | 设备地址 | 0x01=地址1 |
| 字节7 | 功能码 | 0x03=读保持 | |
| 0x03/0x04请求 | 字节8-9 | 起始地址 | 0x000A=地址10 |
| 字节10-11 | 寄存器数量 | 0x0003=3个 | |
| 0x06请求 | 字节8-9 | 寄存器地址 | 0x000F=地址15 |
| 字节10-11 | 写入值 | 0x1234=写入值 | |
| 0x10请求 | 字节8-9 | 起始地址 | 0x0020=地址32 |
| 字节10-11 | 寄存器数量 | 0x0003=3个 | |
| 字节12 | 字节计数 | 0x06=6字节 | |
| 字节13+ | 寄存器值 | 从13开始 | |
| 响应帧 | 字节8 | 字节计数 | 0x06=6字节数据 |
| 字节9+ | 寄存器值 | 从9开始 |
掌握这些字节级信息,您可以:
-
✅ 精确构造任意Modbus TCP帧
-
✅ 快速定位通信问题
-
✅ 高效调试协议交互
-
✅ 实现可靠的工业通信
本文提供的字节级细节是Modbus TCP开发、调试和分析的核心参考,建议保存以备查阅。
如何计算长度????