Modbus TCP 主从站实战本机回环五实验与 4629 次/s 吞吐压测pymodbus 实测Modbus 是工控领域的普通话PLC、传感器、网关、电表凡是带 RS485/TCP 口的设备多半说这门语言。网上教程分两派——纯讲协议格式的看完不会写和直接上硬件的没板子没法跟。本文用 pymodbus 在一台 Windows 上同时起 TCP 服务端和客户端做回环闭环五个实验全部真实运行连越界读取会怎样这种细节都实测了——结论和教科书写的还不一样。一、五实验总览实验内容实测E1保持寄存器 FC16 写 / FC03 读[1234,5678,4321,8765] 回读一致TrueE2线圈 FC05 写 / FC01 读写 True 读 TrueE3输入寄存器 FC04 模拟四路温度[2500, 2537, 2574, 2611]E4越界读取行为服务端零填充不报异常见 §四E5吞吐压测1000 次写读往返 0.22s →4629 次/s二、单文件自含服务端回环闭环的关键实验脚本同时启动服务端线程4 区数据块各 100 点与客户端连接不需要第二台机器或 PLC 模拟器frompymodbus.serverimportStartTcpServerfrompymodbus.datastoreimportModbusSequentialDataBlock,ModbusSlaveContext,ModbusServerContextfrompymodbus.clientimportModbusTcpClient storeModbusSlaveContext(coModbusSequentialDataBlock(0,[0]*100),# 线圈 FC01/05diModbusSequentialDataBlock(0,[0]*100),# 离散输入 FC02hrModbusSequentialDataBlock(0,[0]*100),# 保持寄存器 FC03/06/16irModbusSequentialDataBlock(0,[0]*100),# 输入寄存器 FC04)contextModbusServerContext(slavesstore,singleTrue)threading.Thread(targetStartTcpServer,args(context,),kwargs{address:(HOST,PORT)},daemonTrue).start()四区数据块与四类功能码一一对应——记不住功能码时记这张表就够。三、pymodbus 3.x 的版本坑实测踩过三个slave 参数在 3.15 改名 device_id全网老教程的client.read_holding_registers(0, count4, slave1)在 3.15 直接 TypeError数据块地址从 1 开始0 地址构造数据块抛0 address 65535ModbusDeviceContext 在 3.15 变成只有 .simdevice 的废弃壳老写法store[ir].setValues(...)三种姿势全试不通。本包锁定pymodbus 3.9.2 经典 APIpip install pymodbus3.9.2源码注释里标了 3.15 的对应改法——按教程跑不通时先看版本。四、E4 的意外发现越界不报异常教科书说读越界地址会回 0x02 非法数据地址异常。实测 pymodbus 3.9 内置服务端对越界地址默认零填充、正常返回不报错。真实设备多半按规范回 0x02——同一个客户端代码在不同服务端上行为不同。工程结论Modbus 客户端必须双判——isinstance(响应, ExceptionResponse)检查协议异常 值合理性校验温度读出 0 或 65535 都可疑。五、吞吐基线4629 次/s 意味着什么E5 压测写单个保持寄存器读回1000 次往返 0.22 秒约 0.2ms 一次。这是本机回环 TCP 的上限——现场设备按 RS485 9600 波特率走单次往返要几十毫秒慢两个数量级。轮询间隔的设定依据是设备手册的最小响应时间不是程序能跑多快。六、复现指南pip install pymodbus3.9.2后python modbus_tcp_lab.py一条命令复现脚本自带服务端线程约 5 秒跑完。源码约 70 行注释详尽含全部运行留档。_tcp_lab.py 一条命令复现脚本自带服务端线程约 5 秒跑完。源码约 70 行注释详尽含全部运行留档。配套完整资源已整理上传点击查看资源包含 Modbus TCP 五实验源码与运行留档开箱即跑