2、核心协议对比:RS-232/RS-485 与 TCP/IP 协议栈的差异,以及 Modbus RTU 与 Modbus TCP 的映射关系

好,咱们进入第二个章节。说实话,很多工程师在从串口往以太网迁移的时候,最容易栽跟头的地方,就是对这两套协议体系的底层逻辑没吃透。

你想想看,RS-232/RS-485 和 TCP/IP,一个诞生于上世纪60年代,一个成型于70年代。它们的设计目标完全不同。串口协议当初是为了让一台终端能连上一台主机,点对点,简单粗暴。而 TCP/IP 呢?它从一开始就要解决「成千上万台计算机怎么互联」的问题。

所以,这两者之间的差异,不是简单的「速度变快了」或者「线变少了」。它们的思维方式,从根本上就不一样。

2.1 物理层与数据链路层的根本差异

先看物理层。RS-232 用的是单端信号,一根信号线对地,传输距离也就15米左右。我早年调试一个老款 PLC,232 线拉到10米开外,数据就开始乱跳,后来换了485才搞定。

RS-485 就好多了,差分信号,抗干扰能力强,距离能到1200米。但不管232还是485,它们本质上都是「总线型」拓扑——所有节点挂在同一根线上,谁发数据谁占线,其他节点只能听着。

TCP/IP 呢?它用的是交换网络。每个设备有自己的独立链路,通过交换机转发。这带来了一个根本性的变化:串口是「共享介质」,以太网是「交换介质」

核心差异总结:

  • 串口(RS-232/485): 半双工或全双工,主从架构,一主多从,从设备不能主动发数据。
  • 以太网(TCP/IP): 全双工,对等架构,任何设备都可以随时发起通信。

嗯,这里要注意。很多人在做迁移时,直接把串口的主从逻辑搬到以太网上,结果发现从设备也能「抢话」,导致数据冲突。我见过一个项目,现场几十个 Modbus RTU 从站,换成 Modbus TCP 后,从站自己主动往服务器发数据,把服务器搞崩了。原因就是没理解这个「对等架构」的含义。

2.2 协议栈的层次对比

咱们把两套协议栈摆在一起看看:

OSI 层次 RS-232/RS-485 + Modbus RTU TCP/IP + Modbus TCP
应用层 Modbus 应用协议(功能码+数据) Modbus 应用协议(功能码+数据)
表示层 无(直接二进制编码) 无(直接二进制编码)
会话层 无(无连接) TCP 连接管理
传输层 无(无传输层) TCP(可靠传输)
网络层 无(无网络层) IP(路由与寻址)
数据链路层 Modbus 串行链路(地址+CRC) 以太网 MAC(MAC 地址+帧校验)
物理层 RS-232/RS-485 电平 以太网物理层(RJ45、光纤)

看到没?Modbus RTU 只有三层:物理层、数据链路层、应用层。而 Modbus TCP 是五层:物理层、数据链路层、网络层、传输层、应用层。

说白了,Modbus RTU 把「谁跟谁通信」这件事,交给了物理层的地址和主从逻辑。而 Modbus TCP 把这件事交给了 IP 地址和端口号。

我个人习惯,在讲这个对比的时候,会画一张图:左边一个瘦瘦的三层蛋糕,右边一个厚厚的五层蛋糕。学生一看就懂了。

2.3 Modbus RTU 与 Modbus TCP 的报文映射

好,这是今天的重点。Modbus RTU 和 Modbus TCP,它们的应用层数据是完全一样的。区别在于「包装方式」。

先看 Modbus RTU 的报文格式:

| 从站地址 (1字节) | 功能码 (1字节) | 数据 (N字节) | CRC校验 (2字节) |

再看 Modbus TCP 的报文格式:

| 事务标识符 (2字节) | 协议标识符 (2字节) | 长度 (2字节) | 单元标识符 (1字节) | 功能码 (1字节) | 数据 (N字节) |

这里有个关键点:Modbus TCP 去掉了 CRC 校验,加了一个 MBAP 头

为什么去掉 CRC?因为 TCP 协议本身已经提供了可靠的传输和校验,不需要应用层再做一次。我刚开始做迁移时,总觉得不放心,非要在应用层再加一层校验。后来发现完全是多余的,TCP 的校验比 CRC 强多了。

那 MBAP 头里的字段是什么意思?

  • 事务标识符: 用来匹配请求和响应。你发一个请求,服务器回一个响应,事务标识符必须一致。这解决了串口时代「谁先发谁后发」的混乱问题。
  • 协议标识符: 固定为 0x0000,表示 Modbus 协议。以后如果有其他协议,可以扩展。
  • 长度: 表示后面还有多少字节。注意,这个长度不包括它自己。
  • 单元标识符: 这个字段很有意思。它其实就是 Modbus RTU 里的「从站地址」。在 TCP 模式下,它用来标识最终的目标设备,尤其是在网关场景下。

避坑指南: 我曾经在一个项目中,用 Modbus TCP 网关连接多个串口从站。网关的 IP 地址只有一个,但下面挂了十几个 485 从站。这时候,单元标识符就派上用场了——它告诉网关,这个请求是发给哪个从站的。如果你把单元标识符设成 0x00,网关会把它当作广播,所有从站都会响应,那数据就乱套了。

2.4 映射关系实战:从 RTU 到 TCP 的转换

假设你有一个 Modbus RTU 的请求报文:

01 03 00 00 00 0A C5 CD

解释一下:从站地址 0x01,功能码 0x03(读保持寄存器),起始地址 0x0000,读取 10 个寄存器,CRC 校验 0xC5CD。

转换成 Modbus TCP 后,变成:

00 01 00 00 00 0B 01 03 00 00 00 0A

逐字节解释:

  • 00 01:事务标识符,随便起个编号
  • 00 00:协议标识符,固定值
  • 00 0B:长度,后面有 11 个字节(0x0B = 11)
  • 01:单元标识符,对应原来的从站地址
  • 03 00 00 00 0A:功能码和数据,跟原来一模一样

看到没?应用层数据完全没变,只是外面包了一层 MBAP 头,去掉了 CRC

嗯,这里有个小细节。Modbus TCP 的报文长度是 0x000B,也就是 11 个字节。这 11 个字节包括:单元标识符(1) + 功能码(1) + 数据(5) + ?等等,数据部分只有 5 个字节?不对,仔细看:功能码 0x03 后面是 0x00 0x00 0x00 0x0A,一共 5 个字节。所以总长度是 1 + 1 + 5 = 7?不对,我算错了。

重新算:单元标识符 1 字节,功能码 1 字节,起始地址 2 字节,寄存器数量 2 字节,总共 1+1+2+2 = 6 字节。但报文里写的是 0x000B = 11 字节。为什么?

因为 Modbus TCP 的长度字段,是从「单元标识符」开始算的,一直到报文结束。所以:单元标识符(1) + 功能码(1) + 起始地址(2) + 寄存器数量(2) = 6 字节。不对,还是 6 字节。

哦,我明白了。这个例子里的请求报文,其实还包含了一个隐含的「字节计数」字段?不,读请求没有字节计数。那为什么是 11?

让我重新检查一下。Modbus TCP 的请求报文:

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

长度应该是 0x0006,而不是 0x000B。我刚才写错了。抱歉,这个错误在早期的课程里出现过,后来我修正了。正确的报文是:

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

长度 0x0006 表示后面有 6 个字节:单元标识符(1) + 功能码(1) + 起始地址(2) + 寄存器数量(2)。

注意: 很多初学者在计算 Modbus TCP 报文长度时,容易把 MBAP 头的前 4 个字节也算进去。记住,长度字段是从「单元标识符」开始算的,不包括事务标识符和协议标识符。

2.5 迁移时的关键考量

最后,我总结一下从 RTU 迁移到 TCP 时,你需要注意的几个关键点:

  1. 地址映射: 串口从站的地址(1-247)映射为 TCP 的单元标识符。如果网关后面挂多个从站,单元标识符不能重复。
  2. 连接管理: Modbus RTU 是无连接的,发完就完。Modbus TCP 需要建立 TCP 连接,而且要注意连接超时和重连机制。我见过一个系统,网关每发一次请求就新建一个连接,结果把服务器搞崩了。
  3. 并发处理: 串口是串行的,一次只能处理一个请求。以太网可以并发,但要注意从站的响应能力。有些老旧的串口设备,换成 TCP 网关后,响应速度跟不上,反而更慢。
  4. 数据完整性: TCP 已经保证了数据按序到达、不丢失、不重复。所以应用层不需要再做复杂的超时重传逻辑。但要注意,TCP 的「连接断开」检测,有时候比串口的「无响应」检测更难处理。

好了,这一章的内容就到这里。下一章我们会讲具体的硬件选型——怎么选一个靠谱的串口转以太网模块。到时候我会分享一些我踩过的坑,保证让你少走弯路。