4、Modbus TCP协议:与RTU的区别、MBAP报文头、端口502、跨网段通信配置

好,咱们今天聊聊Modbus TCP。说实话,这个协议在工业现场太常见了。我最早接触它是在一个汽车焊装车间,那时候PLC和上位机之间要传数据,用的就是Modbus TCP。当时我还在想,这不就是Modbus RTU套了个TCP的壳吗?后来踩了几个坑才明白,没那么简单。

4.1 Modbus TCP与RTU:到底差在哪?

很多人觉得Modbus TCP就是把RTU的报文直接塞进TCP包里。嗯,这个说法对了一半。咱们先看一张对比图,心里就有数了。

Modbus RTU vs Modbus TCP 报文结构对比 Modbus RTU 地址 功能码 数据区 CRC校验 Modbus TCP MBAP头(7字节) 功能码 数据区 • RTU有设备地址和CRC校验,TCP没有 • TCP用MBAP头替代了地址和校验功能 • 功能码和数据区结构完全一致

你看,核心区别就三点:

  • 地址机制不同:RTU靠设备地址(1-247)区分从站,TCP靠IP地址。所以TCP报文里没有地址字段,取而代之的是MBAP头里的单元标识符。这个单元标识符在跨网关时才有用,平时填0xFF就行。
  • 校验方式不同:RTU有CRC16校验,TCP没有。为什么?因为TCP/IP协议栈自己会做校验和重传,底层已经保证了数据完整性。你再加一层CRC就是画蛇添足。
  • 报文头不同:RTU报文开头就是地址,TCP开头是7个字节的MBAP头。这个MBAP头是TCP独有的,咱们下面细说。

核心结论:Modbus TCP = Modbus RTU 去掉地址和CRC,加上MBAP头。功能码和数据区完全兼容。

4.2 MBAP报文头:7个字节的门道

MBAP全称是Modbus Application Protocol Header。说白了,它就是给Modbus报文加的一个TCP专用信封。我见过不少新手,抓包分析时看到这7个字节就懵了。其实很简单,咱们拆开看。

字段 长度 说明 典型值
事务处理标识符 2字节 用于匹配请求和响应。客户端每发一个请求,这个值加1。 0x0001, 0x0002...
协议标识符 2字节 固定为0x0000,表示Modbus协议。其他值保留。 0x0000
长度 2字节 后续字节数(从单元标识符开始到报文结束)。 0x0006(读保持寄存器)
单元标识符 1字节 相当于RTU的设备地址。直连时填0xFF或0x00。 0xFF

这里有个坑,我当年就栽过。事务处理标识符,很多设备要求请求和响应必须一致。但有些设备不检查这个字段,有些却严格匹配。我曾经遇到一个西门子S7-1200,它要求事务ID必须从0开始递增,中间不能跳号。如果你并发发了两个请求,事务ID分别是1和2,结果响应回来的是2和1,那就乱套了。所以,我建议你在客户端做事务ID的严格管理,一个请求没响应之前,不要发下一个

实战技巧:抓包时,先看协议标识符是不是0x0000。如果不是,说明对方发的不是标准Modbus TCP。再看长度字段,它告诉你后面还有多少字节。这个长度不包括MBAP头本身。

4.3 端口502:为什么是它?

Modbus TCP的默认端口是502。这个端口是IANA(互联网号码分配局)分配的,属于系统端口(0-1023)。为什么选502?说实话,没有特别深奥的原因,就是当年Modbus组织申请时,这个端口还没被占用。

但这里有个实际问题:502端口在Linux系统上需要root权限才能绑定。我记得有一次在树莓派上部署Modbus TCP服务器,程序死活起不来。查了半天,发现是端口绑定失败。后来我改用端口映射,或者直接用非特权端口(比如10502),再用iptables做转发。

注意:502端口是标准端口,但很多工业交换机、防火墙默认会放行502端口。如果你改了端口,记得检查网络设备的ACL规则。我曾经在客户现场,因为改了端口号,IT部门没更新防火墙规则,导致通信中断了整整一个下午。

4.4 跨网段通信配置:实战中的硬骨头

跨网段通信,说白了就是两个不同IP网段的设备要互相访问。比如PLC在192.168.1.x网段,上位机在192.168.2.x网段。这时候直接通信是不行的,需要路由器或三层交换机做路由转发。

我总结了几种常见方案:

  1. 静态路由:在路由器上配置路由表,告诉它192.168.2.0/24的下一跳是哪个接口。这是最稳定的方案,适合固定网络。
  2. NAT映射:把PLC的502端口映射到上位机所在网段的某个IP上。适合临时调试,但延迟会高一些。
  3. VPN隧道:两个网段通过VPN打通。适合远程访问,但配置复杂。
  4. Modbus网关:用专门的Modbus TCP网关做协议转换和路由。适合多设备场景。

我个人最推荐静态路由。为什么?因为它简单、可靠、没有额外延迟。我在一个水处理项目中,用了三层交换机做路由,配置了三条静态路由,稳定运行了三年没出过问题。

配置时要注意:Modbus TCP的请求-响应模式要求网络延迟不能太大。如果跨网段通信延迟超过几百毫秒,PLC的超时机制就会触发。我建议你把超时时间设到1-2秒,重试次数设3次。这样既能容忍网络抖动,又不会让系统卡死。

避坑指南:我曾经遇到一个案例,PLC和上位机跨了三个网段,中间经过了两台路由器。结果Modbus TCP通信时断时续。抓包发现,路由器把事务ID给改了。原来那台路由器启用了TCP优化功能,会修改TCP载荷。解决办法:关掉路由器的TCP优化,或者用Modbus网关隔离。

4.5 一个完整的Modbus TCP报文示例

说了这么多,不如看一个实际的报文。假设我们要读取从站1的保持寄存器,起始地址0,读取2个寄存器。

请求报文(十六进制):
00 01 00 00 00 06 FF 03 00 00 00 02

解析:
00 01 - 事务处理标识符(第1个请求)
00 00 - 协议标识符(Modbus协议)
00 06 - 长度(后面还有6个字节)
FF    - 单元标识符(0xFF表示广播)
03    - 功能码(读保持寄存器)
00 00 - 起始地址(0)
00 02 - 寄存器数量(2个)

响应报文(十六进制):
00 01 00 00 00 05 FF 03 04 12 34 56 78

解析:
00 01 - 事务处理标识符(与请求匹配)
00 00 - 协议标识符
00 05 - 长度(后面还有5个字节)
FF    - 单元标识符
03    - 功能码
04    - 数据字节数(4个字节)
12 34 - 第1个寄存器的值(0x1234)
56 78 - 第2个寄存器的值(0x5678)

你看,整个报文结构非常清晰。实际开发中,你不需要手动拼这些字节,用现成的库就行。但理解报文结构,对排查问题非常有帮助。

4.6 小结

Modbus TCP本质上就是Modbus RTU的TCP化改造。它去掉了地址和CRC,加上了MBAP头,用IP地址替代了设备地址。端口502是标准端口,但实际项目中可以根据需要修改。跨网段通信时,静态路由是最可靠的方案,但要注意网络延迟和中间设备的TCP优化问题。

嗯,这些内容够你应付大部分现场问题了。下次遇到Modbus TCP通信故障,记得先抓包看MBAP头,再检查路由配置。这两步走完,90%的问题都能定位到。


公众号:蓝海资料掘金营,微信deep3321