c++需依赖libmodbus等第三方库实现modbus,推荐使用3.1.10+版本;地址从0开始,注意1-based文档转换;寄存器值为大端序;异常响应0x83需调用modbus_get_exception_status解析,非网络故障。

C++ 本身不内置 Modbus 支持,必须依赖第三方库或手写协议栈;直接用 socket 发 raw 报文极易出错,不推荐新手硬刚。
用 libmodbus 最省事,但得先装对版本
libmodbus 是 C 写的轻量库,C++ 可直接调用,Windows/Linux/macOS 都支持。别下错分支:libmodbus-3.1.10 之后才稳定支持 Modbus TCP 的超时重传和多线程安全;旧版(如 3.1.4)在高并发下会卡死或返回 EBADF。
- Linux 下用包管理器装:
apt install libmodbus-dev(Ubuntu/Debian),但版本常滞后,建议从官网源码编译 - Windows 下优先用 vcpkg:
vcpkg install libmodbus:x64-windows,避免手动配libmodbus.lib和头文件路径 - 初始化 TCP 连接时,
modbus_new_tcp("192.168.1.10", 502)返回NULL多半是 IP 不通或防火墙拦了 502 端口,不是代码写错了
读保持寄存器(0x03)最常用,但地址和数量容易搞反
Modbus 地址从 0 开始编号,但很多 PLC 文档标的是“40001”这类 1-based 人眼地址。比如文档说“读 40001~40010”,实际要调用 modbus_read_registers(ctx, 0, 10, tab_reg) —— 第二个参数是起始地址(0),第三个是数量(10),不是结束地址。
- 寄存器值是大端序(Big-Endian),
uint16_t tab_reg[10]直接接收即可,不用字节翻转 - 如果读出来全是 0 或乱码,先用
modbus_set_debug(ctx, TRUE)开启日志,看发出去的报文是否含正确功能码和地址 - 某些设备要求地址加偏移(如加 1),这是厂商私有约定,不是标准行为,得查设备手册确认
异常响应(0x83)不是崩溃,是设备明确拒绝了请求
收到功能码为 0x83 的响应,说明设备返回了异常码(Exception Code),常见如 0x02(非法地址)、0x03(非法值)、0x04(从机故障)。这不是网络断开,modbus_read_registers() 会返回 -1,但 errno 不变,得调用 modbus_get_exception_status(ctx) 拿具体码。
立即学习“C++免费学习笔记(深入)”;
- 别一看到返回 -1 就重连——重连解决不了地址越界问题,反而可能触发设备限流
- 调试时把异常码打出来:
printf("Exception: 0x%02X\n", modbus_get_exception_status(ctx)) - 有些国产模块对 PDU 长度敏感,发 12 个寄存器读请求(PDU 超 30 字节)就回
0x83 + 0x04,换成两次 6 寄存器读更稳
Modbus 看似简单,真正难的是设备兼容性:同一功能码,不同厂家对地址偏移、超时判定、重试逻辑的实现天差地别。别指望一套代码通吃所有 PLC,每次对接新设备,先用 modbus-cli 或 QModMaster 手动测通,再写 C++ 封装。










