Modbus TCP 协议详解:报文结构、功能码与通信示例

举报
yd_247939230 发表于 2026/10/15 10:45:10 2026/10/15
【摘要】 Modbus TCP 协议详解:报文结构、功能码与通信示例 一、Modbus TCP 简介 1.1 什么是 Modbus TCP?Modbus 最初由 Modicon 公司于 1979 年开发,广泛应用于工业自动化领域。随着以太网和 TCP/IP 技术的发展,Modbus TCP 被用于通过 TCP/IP 网络进行 Modbus 通信。Modbus 是一种应用层通信协议。常见的传输形式包括...

Modbus TCP 协议详解:报文结构、功能码与通信示例

一、Modbus TCP 简介

1.1 什么是 Modbus TCP?

Modbus 最初由 Modicon 公司于 1979 年开发,广泛应用于工业自动化领域。随着以太网和 TCP/IP 技术的发展,Modbus TCP 被用于通过 TCP/IP 网络进行 Modbus 通信。

Modbus 是一种应用层通信协议。常见的传输形式包括 Modbus RTU、Modbus ASCII 和 Modbus TCP。其中,Modbus RTU 和 Modbus ASCII 通常用于串行通信,而 Modbus TCP 通常通过以太网和 TCP/IP 网络传输。

Modbus TCP 常见于以下设备和场景:

  • PLC 与上位机之间的数据交换;
  • HMI 与控制器之间的通信;
  • 工业传感器、仪表和变频器的数据读取;
  • 工业网关、嵌入式设备与监控系统之间的通信。

1.2 Modbus TCP 的通信模型

传统 Modbus 串行通信通常采用主从模式;Modbus TCP 则通常使用客户端(Client)和服务器(Server)的概念:

  • 客户端主动建立 TCP 连接并发送 Modbus 请求;
  • 服务器端接收请求、执行操作并返回响应;
  • 一条 TCP 连接可以承载多次 Modbus 请求与响应;
  • 服务器端通常使用 TCP 端口 502 提供标准 Modbus TCP 服务。

Modbus TCP 的单元标识符(Unit Identifier)可用于标识目标设备,尤其是在通过 TCP 网关访问下游串行设备时。对于直接连接的 TCP 设备,该字段的具体用途取决于设备实现。

二、Modbus TCP 报文结构

Modbus TCP 应用报文通常由两部分组成:

+----------------------+----------------------+
|        MBAP          |         PDU          |
+----------------------+----------------------+
|      7 字节          |    长度可变           |
+----------------------+----------------------+

其中:

  • MBAP(Modbus Application Protocol Header):Modbus TCP 报文头,固定为 7 字节;
  • PDU(Protocol Data Unit):协议数据单元,由功能码和数据组成。

与 Modbus RTU 不同,Modbus TCP 报文不包含 RTU 格式的 CRC 校验字段。数据完整性由 TCP/IP 协议栈负责处理。

三、MBAP 报文头

3.1 MBAP 结构

MBAP 报文头长度固定为 7 字节,包含事务处理标识、协议标识、长度和单元标识符。

字段 长度 说明
事务处理标识(Transaction Identifier) 2 字节 用于将响应与对应请求关联起来
协议标识(Protocol Identifier) 2 字节 Modbus TCP 通常为 0x0000
长度(Length) 2 字节 表示后续单元标识符与 PDU 的总字节数
单元标识符(Unit Identifier) 1 字节 标识目标单元或下游设备

3.2 事务处理标识

事务处理标识由客户端设置,服务器通常会在响应中原样返回该标识。客户端可借此判断响应对应哪一个请求。

例如:

请求:00 01 ...
响应:00 01 ...

这里的 00 01 是事务处理标识。它可以按请求递增,也可以由客户端使用其他管理策略分配,但应确保在需要匹配的请求之间不会产生歧义。

3.3 协议标识

对于标准 Modbus TCP 报文,协议标识为:

00 00

3.4 长度字段

长度字段是解析 Modbus TCP 报文时最重要的字段之一。

Length 表示紧随该字段之后的字节数,也就是:

[
\text{Length}=1+\text{PDU长度}
]

其中,额外的 1 字节是单元标识符。长度字段本身以及前面的事务处理标识、协议标识不计入 Length。

例如,读取 3 个保持寄存器的请求,其 PDU 包含:

  • 功能码:1 字节;
  • 起始地址:2 字节;
  • 寄存器数量:2 字节。

因此 PDU 长度为 5 字节,Length 为:

[
1+5=6
]

其 MBAP 长度字段为 00 06。

3.5 单元标识符

单元标识符用于标识目标单元。通过 TCP 转串行网关访问 Modbus RTU 设备时,它通常用于指定下游串行设备地址。

如果是直接连接的 Modbus TCP 设备,单元标识符可能被设备忽略,或按厂商定义使用。

四、PDU 与常用功能码

PDU 由功能码和数据字段组成:

+----------+----------------+
| 功能码   | 数据           |
| 1 字节   | 长度可变       |
+----------+----------------+

功能码用于指明本次请求执行的操作。常用标准功能码如下:

功能码 名称 操作对象 说明
0x01 读线圈 Coil 读取一个或多个线圈状态
0x02 读离散量输入 Discrete Input 读取一个或多个离散输入状态
0x03 读保持寄存器 Holding Register 读取一个或多个保持寄存器
0x04 读输入寄存器 Input Register 读取一个或多个输入寄存器
0x05 写单个线圈 Coil 写入一个线圈状态
0x06 写单个保持寄存器 Holding Register 写入一个保持寄存器
0x0F 写多个线圈 Coil 一次写入多个线圈状态
0x10 写多个保持寄存器 Holding Register 一次写入多个保持寄存器

Modbus 的四类常见数据对象如下:

对象 典型含义 标准操作权限
线圈(Coil) 开关量输出 可读、可写
离散量输入(Discrete Input) 开关量输入 只读
输入寄存器(Input Register) 设备提供的测量值等 只读
保持寄存器(Holding Register) 配置参数、控制值或其他数据 可读、可写

具体寄存器或线圈地址的含义由设备协议文档定义。

五、常见功能码报文示例

以下示例使用:

  • 事务处理标识:00 01;
  • 协议标识:00 00;
  • 单元标识符:01。

为了突出报文结构,示例均以十六进制字节表示。Modbus TCP 不包含 RTU CRC 字段。

5.1 功能码 0x01:读线圈

读取从指定起始地址开始的连续线圈状态。每个线圈状态占 1 bit。

请求格式

字段 长度
MBAP 7 字节
功能码 0x01 1 字节
起始地址 2 字节
线圈数量 2 字节

PDU 长度为 5 字节,因此 MBAP 的 Length 字段为 00 06。

响应格式

字段 长度
MBAP 7 字节
功能码 1 字节
字节计数 1 字节
线圈状态数据 按实际数量计算

线圈数据的字节数为:

[
N=\left\lceil\frac{Q}{8}\right\rceil
]

其中 (Q) 为读取的线圈数量。

示例:读取 8 个线圈

读取单元 0x01 中,从地址 0x0000 开始的 8 个线圈。

请求报文:

00 01 00 00 00 06 01 01 00 00 00 08

字段解析:

字段 数据
事务处理标识 00 01
协议标识 00 00
Length 00 06
单元标识符 01
功能码 01
起始地址 00 00
线圈数量 00 08

假设响应中第一个线圈为 ON,其余线圈为 OFF,则状态数据为 01。

响应报文:

00 01 00 00 00 04 01 01 01 01

其中:

  • Length 为 00 04,表示单元标识符 1 字节,加上 PDU 的 3 字节;
  • 字节计数为 01;
  • 状态数据为 01。

线圈状态按照每个字节的低位优先排列,即 Bit 0 对应本字节中的第一个线圈。

5.2 功能码 0x02:读离散量输入

功能码 0x02 用于读取连续的离散量输入状态,其报文结构与 0x01 类似。

请求格式

字段 长度
MBAP 7 字节
功能码 0x02 1 字节
起始地址 2 字节
输入数量 2 字节

响应格式

字段 长度
MBAP 7 字节
功能码 1 字节
字节计数 1 字节
输入状态数据 按实际数量计算

示例:读取 8 个离散量输入

请求报文:

00 01 00 00 00 06 01 02 00 00 00 08

假设第一个输入为 ON,其余输入为 OFF,响应报文为:

00 01 00 00 00 04 01 02 01 01

其中,功能码 02 表示读取离散量输入,字节计数 01 表示状态数据占 1 字节,数据 01 表示第一个输入为 ON,其余输入为 OFF。

5.3 功能码 0x03:读保持寄存器

功能码 0x03 用于读取连续的保持寄存器。每个寄存器占 2 字节。

请求格式

字段 长度
MBAP 7 字节
功能码 0x03 1 字节
起始地址 2 字节
寄存器数量 2 字节

响应格式

字段 长度
MBAP 7 字节
功能码 1 字节
字节计数 1 字节
寄存器数据 每个寄存器 2 字节

读取 (N) 个寄存器时,数据长度为:

[
B=2N
]

示例:读取 3 个保持寄存器

读取单元 0x01 中,从地址 0x0000 开始的 3 个保持寄存器。

请求报文:

00 01 00 00 00 06 01 03 00 00 00 03

假设第一个寄存器的值为 0x0021,后两个寄存器的值为 0x0000,响应报文为:

00 01 00 00 00 09 01 03 06 00 21 00 00 00 00

响应解析:

字段 数据
功能码 03
字节计数 06
寄存器 0 00 21
寄存器 1 00 00
寄存器 2 00 00

Length 为 00 09,因为单元标识符占 1 字节,PDU 占 8 字节(功能码 1 字节、字节计数 1 字节、寄存器数据 6 字节)。

5.4 功能码 0x04:读输入寄存器

功能码 0x04 用于读取连续的输入寄存器。其报文结构与 0x03 类似,主要区别是读取的数据对象不同。

请求格式

字段 长度
MBAP 7 字节
功能码 0x04 1 字节
起始地址 2 字节
寄存器数量 2 字节

响应格式

字段 长度
MBAP 7 字节
功能码 1 字节
字节计数 1 字节
寄存器数据 每个寄存器 2 字节

示例:读取 3 个输入寄存器

请求报文:

00 01 00 00 00 06 01 04 00 00 00 03

假设第一个寄存器值为 0x0021,后两个寄存器值为 0x0000,响应报文为:

00 01 00 00 00 09 01 04 06 00 21 00 00 00 00

其中,06 表示后续寄存器数据占 6 字节。

5.5 功能码 0x05:写单个线圈

功能码 0x05 用于将指定线圈设置为 ON 或 OFF。

写入值的定义如下:

写入值 含义
FF 00 ON
00 00 OFF

请求格式

字段 长度
MBAP 7 字节
功能码 0x05 1 字节
线圈地址 2 字节
输出值 2 字节

正常响应会回显请求中的线圈地址和输出值。

示例:将地址 0 的线圈设置为 ON

请求报文:

00 01 00 00 00 06 01 05 00 00 FF 00

正常响应报文:

00 01 00 00 00 06 01 05 00 00 FF 00

5.6 功能码 0x06:写单个保持寄存器

功能码 0x06 用于向指定保持寄存器写入一个 16 位值。

请求格式

字段 长度
MBAP 7 字节
功能码 0x06 1 字节
寄存器地址 2 字节
写入值 2 字节

正常响应会回显请求中的寄存器地址和写入值。

示例:将地址 0 的保持寄存器设置为 0x000A

请求报文:

00 01 00 00 00 06 01 06 00 00 00 0A

正常响应报文:

00 01 00 00 00 06 01 06 00 00 00 0A

5.7 功能码 0x0F:写多个线圈

功能码 0x0F 用于一次写入多个连续线圈。每个线圈使用 1 bit 表示,多个线圈状态按位打包。

请求格式

字段 长度 说明
MBAP 7 字节 报文头
功能码 0x0F 1 字节 写多个线圈
起始地址 2 字节 第一个线圈地址
线圈数量 2 字节 要写入的线圈总数
字节计数 1 字节 线圈数据占用的字节数
线圈数据 N 字节 按位打包的状态

数据字节数为:

[
N=\left\lceil\frac{Q}{8}\right\rceil
]

其中 (Q) 为线圈数量。

响应格式

正常响应包含功能码、起始地址和线圈数量,不会回显全部线圈数据。

示例:将地址 0 开始的 4 个线圈全部设置为 ON

4 个线圈全部置为 ON 时,低 4 位均为 1,数据字节为 0F。

请求报文:

00 01 00 00 00 08 01 0F 00 00 00 04 01 0F

字段说明:

字段 数据
Length 00 08
单元标识符 01
功能码 0F
起始地址 00 00
线圈数量 00 04
字节计数 01
线圈数据 0F

正常响应报文:

00 01 00 00 00 06 01 0F 00 00 00 04

5.8 功能码 0x10:写多个保持寄存器

功能码 0x10 用于一次写入多个连续的保持寄存器。每个寄存器占 2 字节。

请求格式

字段 长度 说明
MBAP 7 字节 报文头
功能码 0x10 1 字节 写多个保持寄存器
起始地址 2 字节 第一个寄存器地址
寄存器数量 2 字节 要写入的寄存器数量
字节计数 1 字节 寄存器数据总字节数
寄存器数据 2 × N 字节 每个寄存器 2 字节

写入 (N) 个寄存器时,数据字节数为:

[
B=2N
]

响应格式

正常响应包含功能码、起始地址和寄存器数量,不会再次返回所有寄存器数据。

示例:向地址 0 的一个寄存器写入 0x000F

请求报文:

00 01 00 00 00 09 01 10 00 00 00 01 02 00 0F

字段说明:

字段 数据
Length 00 09
单元标识符 01
功能码 10
起始地址 00 00
寄存器数量 00 01
字节计数 02
寄存器数据 00 0F

正常响应报文:

00 01 00 00 00 06 01 10 00 00 00 01

六、如何根据 MBAP 解析 Modbus TCP 报文

在编写 Modbus TCP 客户端或服务器时,不能简单地假设一次 recv() 就能接收到一整帧报文。

TCP 是面向字节流的协议,不保留应用层报文边界。一次接收可能只得到部分报文,也可能同时得到多帧报文。因此,需要根据 MBAP 中的 Length 字段判断一帧报文何时接收完整。

6.1 报文总长度计算

MBAP 固定为 7 字节,而 Length 字段表示单元标识符和 PDU 的总长度。

因此:

[
\text{报文总长度}=6+\text{Length}
]

这里的 6 字节是事务处理标识(2 字节)、协议标识(2 字节)和 Length 字段本身(2 字节)。

例如,Length 为 0x0006 时:

[
6+6=12\text{ 字节}
]

这与前面的读寄存器请求报文总长度一致。

6.2 推荐的接收流程

  1. 先接收至少 6 字节,取得事务处理标识、协议标识和 Length 字段。
  2. 解析 Length,计算整帧总长度 6 + Length。
  3. 继续接收,直到缓冲区中至少包含指定总长度的字节。
  4. 取出一帧进行解析,并保留缓冲区中可能属于下一帧的数据。
  5. 检查协议标识、事务处理标识、功能码和响应长度是否符合预期。

在实现时,还应对 Length 设置合理的上下限,避免异常数据导致缓冲区越界或无限等待。

七、Modbus TCP 与 Modbus RTU 的区别

对比项 Modbus TCP Modbus RTU
常见传输方式 TCP/IP,以太网 串行通信,如 RS-485
报文头 7 字节 MBAP 从站地址等字段
帧结束判断 根据 MBAP 的 Length 字段 通常根据串行时序和帧格式判断
CRC 校验 不使用 RTU CRC 字段 使用 CRC-16
事务标识 有 无
常见服务端口 TCP 502 不适用
常见使用场景 以太网设备、上位机、网关 PLC、仪表、串行总线设备

虽然两者使用相同或相近的功能码和数据对象,但它们的报文封装方式不同,不能直接把完整的 RTU 报文原样放进标准 Modbus TCP 报文中。

八、异常响应与通信处理

当服务器无法执行请求时,通常会返回异常响应。异常响应功能码为原功能码加上 0x80。

例如,功能码 0x03 的异常响应功能码为:

0x03 | 0x80 = 0x83

常见异常码如下:

异常码 名称 含义
0x01 Illegal Function 不支持请求的功能码
0x02 Illegal Data Address 请求的数据地址无效
0x03 Illegal Data Value 请求中的数据值不合法
0x04 Server Device Failure 服务器执行操作时发生故障

客户端实现中,除了处理异常响应,还应考虑:

  • TCP 连接断开或连接超时;
  • 请求发送成功但响应未及时到达;
  • 事务处理标识与请求不匹配;
  • 收到不完整报文或连续多帧数据;
  • 返回的功能码、Length 或数据长度不符合预期。

九、总结

Modbus TCP 是在 TCP/IP 网络上传输 Modbus 应用数据的常用方式。理解其报文结构,是编写工业设备客户端、服务器或通信网关的基础。

需要重点掌握以下内容:

  1. MBAP 报文头固定为 7 字节,包含事务处理标识、协议标识、Length 和单元标识符。
  2. Length 字段表示单元标识符与 PDU 的总长度,因此报文总长度为 6 + Length。
  3. PDU由功能码和数据组成,常见功能码包括 0x01、0x03、0x05、0x06、0x0F 和 0x10。
  4. Modbus TCP 不使用 RTU CRC 字段,但仍需校验报文结构和响应内容。
  5. TCP 是字节流协议,程序必须根据 Length 字段组装完整报文,不能假设一次接收对应一帧。
  6. 地址映射与数据解释应以设备协议文档为准,特别是寄存器编号、单元标识符以及多寄存器数据的字序。

在实际开发中,只要正确处理 MBAP、PDU、TCP 收发缓冲区和异常响应,就可以建立相对可靠的 Modbus TCP 通信程序。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。