一个功能完整、高性能的 Modbus 客户端库,支持 .NET 8 / 9 / 10 平台和多种 Modbus 协议变体
注意:本项目由 AI 辅助生成,主要使用了 Trae、Codex 以及 Qoder 等工具,主要模型是 Qwen-3-Coder、Qwen-3.5-Plus、MiniMax-2.7、GLM-5.2、DeepSeek-V4 等。
dotnet add package Himmelt.ModbusLib
- Modbus RTU - 基于 RTU 帧格式的 Modbus 协议,使用 CRC16 校验
- Modbus TCP - 基于 TCP/IP 的标准 Modbus 协议,使用 MBAP 报文头
- Serial (串口) - 通过串口进行 RTU 通信
- TCP - 通过 TCP 网络进行 TCP 或 RTU 帧传输
- UDP - 通过 UDP 网络进行 TCP 或 RTU 帧传输
- Pipe - 通过 .NET Pipe 进行自定义数据流传输
- Channel - 通过 .NET Channel 进行自定义数据流传输
| 传输方式 | RTU 协议 | TCP 协议 |
|---|---|---|
| Serial | ✅ ModbusRtuClient | ❌ |
| TCP | ✅ ModbusTcpClient (ProtocolType.Rtu) | ✅ ModbusTcpClient (ProtocolType.Tcp) |
| UDP | ✅ ModbusUdpClient (ProtocolType.Rtu) | ✅ ModbusUdpClient (ProtocolType.Tcp) |
| Pipe | ✅ ModbusPipeClient (ProtocolType.Rtu) | ✅ ModbusPipeClient (ProtocolType.Tcp) |
| Channel | ✅ ModbusChannelClient (ProtocolType.Rtu) | ✅ ModbusChannelClient (ProtocolType.Tcp) |
- 🚀 异步编程模型,提供卓越的性能
- 🔄 同步方法支持,便于传统应用集成
- 📋 支持所有标准 Modbus 功能码
- ⚙️ 灵活的配置选项
- 🛡️ 完整的错误处理和重试机制
- 💾 内存优化,使用 ArrayPool 减少分配
- 🔀 泛型支持,可直接读写基本数据类型(byte, short, int, long, float, double 等)
- 📊 字节序和字序控制,支持不同平台数据格式
- 🔌 多种传输方式,支持串口、TCP、UDP、Pipe、Channel
- 🎛️ 协议可选,通过网络客户端可灵活选择 RTU 或 TCP 协议
using ModbusLib.Clients; using ModbusLib.Models; // 创建 RTU 客户端(串口传输,默认使用 RTU 协议) var rtuClient = new ModbusRtuClient(new SerialConfig { PortName = "COM1", BaudRate = 9600, Parity = System.IO.Ports.Parity.None, DataBits = 8, StopBits = System.IO.Ports.StopBits.One }); // 连接并读取数据 await rtuClient.ConnectAsync(); var coils = await rtuClient.ReadCoilsAsync(unitId: 1, startAddress: 0, quantity: 10);
using ModbusLib.Clients; using ModbusLib.Enums; using ModbusLib.Models; // TCP 传输 + TCP 协议(标准 Modbus TCP) var tcpClient = new ModbusTcpClient(new NetworkConfig { RemoteHost = "192.168.1.100", RemotePort = 502 }, protocol: ProtocolType.Tcp); // 或省略 protocol 参数,默认为 Tcp await tcpClient.ConnectAsync(); await tcpClient.WriteMultipleRegistersAsync(unitId: 1, startAddress: 0, new ushort[] { 100, 200, 300 });
// TCP 传输 + RTU 协议(RTU over TCP) var rtuOverTcpClient = new ModbusTcpClient(new NetworkConfig { RemoteHost = "192.168.1.100", RemotePort = 502 }, protocol: ProtocolType.Rtu); await rtuOverTcpClient.ConnectAsync(); var registers = await rtuOverTcpClient.ReadHoldingRegistersAsync(unitId: 1, startAddress: 0, quantity: 10);
// UDP 传输 + RTU 协议(RTU over UDP) var rtuOverUdpClient = new ModbusUdpClient(new NetworkConfig { RemoteHost = "192.168.1.100", RemotePort = 502 }, protocol: ProtocolType.Rtu); await rtuOverUdpClient.ConnectAsync(); var inputs = await rtuOverUdpClient.ReadDiscreteInputsAsync(unitId: 1, startAddress: 0, quantity: 8);
// 串口 RTU 客户端(同步) var rtuClient = new ModbusRtuClient("COM1", 9600); rtuClient.Connect(); var coils = rtuClient.ReadCoils(unitId: 1, startAddress: 0, quantity: 10);
// TCP 客户端 + RTU 协议(同步) var rtuOverTcpClient = new ModbusTcpClient("192.168.1.100", 502, protocol: ProtocolType.Rtu); rtuOverTcpClient.Connect(); var registers = rtuOverTcpClient.ReadHoldingRegisters(unitId: 1, startAddress: 0, quantity: 10);
// 读取浮点数数组 var floatValues = await client.ReadHoldingRegistersAsync<float>(unitId: 1, startAddress: 0, count: 5); // 读取双精度浮点数 var doubleValue = await client.ReadHoldingRegistersAsync<double>(unitId: 1, startAddress: 10, count: 1); // 写入浮点数数组 float[] valuesToWrite = new float[] { 1.23f, 4.56f, 7.89f }; await client.WriteMultipleRegistersAsync(unitId: 1, startAddress: 0, valuesToWrite); // 写入单个双精度浮点数 await client.WriteMultipleRegistersAsync(unitId: 1, startAddress: 10, 123.456);
// 使用小端字节序读取寄存器 var registersLittleEndian = await client.ReadHoldingRegistersAsync( unitId: 1, startAddress: 0, quantity: 10, byteOrder: ByteOrder.LittleEndian); // 使用低字在前的字序写入寄存器 await client.WriteMultipleRegistersAsync( unitId: 1, startAddress: 0, values, byteOrder: ByteOrder.BigEndian, wordOrder: WordOrder.LowFirst);
using ModbusLib.Clients; using ModbusLib.Enums; using ModbusLib.Models; // Pipe 客户端 + TCP 协议(默认) var pipeSession = new PipeSession(); var pipeTcpClient = new ModbusPipeClient(pipeSession, protocol: ProtocolType.Tcp); await pipeTcpClient.ConnectAsync(); // Pipe 客户端 + RTU 协议 var pipeRtuClient = new ModbusPipeClient(pipeSession, protocol: ProtocolType.Rtu); await pipeRtuClient.ConnectAsync(); // Channel 客户端 + TCP 协议(默认) var channelSession = new ChannelSession(); var channelTcpClient = new ModbusChannelClient(channelSession, protocol: ProtocolType.Tcp); await channelTcpClient.ConnectAsync(); // Channel 客户端 + RTU 协议 var channelRtuClient = new ModbusChannelClient(channelSession, protocol: ProtocolType.Rtu); await channelRtuClient.ConnectAsync();
提示:Pipe 和 Channel 客户端适用于需要自定义数据源的场景,例如模拟设备、测试环境或通过其他传输层进行通信。
ModbusLib/
├── Clients/ # 客户端实现
│ ├── ModbusClientBase.cs # 客户端基类
│ ├── ModbusRtuClient.cs # RTU 客户端
│ ├── ModbusTcpClient.cs # TCP 客户端
│ ├── ModbusUdpClient.cs # UDP 客户端
│ ├── ModbusPipeClient.cs # Pipe 客户端
│ └── ModbusChannelClient.cs # Channel 客户端
├── Interfaces/ # 核心接口定义
│ ├── IModbusClient.cs # Modbus 客户端接口
│ ├── IModbusProtocol.cs # 协议处理接口
│ └── IModbusTransport.cs # 传输层接口
├── Enums/ # 枚举类型
│ ├── ByteOrder.cs # 字节序枚举
│ ├── WordOrder.cs # 字序枚举
│ ├── ModbusFunction.cs # Modbus 功能码
│ ├── ModbusExceptionCode.cs # Modbus 异常码
│ └── ProtocolType.cs # 协议类型
├── Models/ # 数据模型
│ ├── ModbusRequest.cs # Modbus 请求模型
│ ├── ModbusResponse.cs # Modbus 响应模型
│ ├── SerialConfig.cs # 串口连接配置
│ ├── NetworkConfig.cs # 网络连接配置
│ ├── PipeSession.cs # Pipe 会话模型
│ └── ChannelSession.cs # Channel 会话模型
├── Transports/ # 传输层实现
│ ├── SerialTransport.cs # 串口传输实现
│ ├── TcpTransport.cs # TCP 传输实现
│ ├── UdpTransport.cs # UDP 传输实现
│ ├── PipeTransport.cs # Pipe 传输实现
│ └── ChannelTransport.cs # Channel 传输实现
├── Protocols/ # 协议处理层
│ ├── RtuProtocol.cs # RTU 协议实现
│ └── TcpProtocol.cs # TCP 协议实现
├── Exceptions/ # 异常定义
│ ├── ModbusException.cs # Modbus 异常基类
│ └── ModbusCommunicationExceptions.cs # 通信异常
└── Utils/ # 工具类
├── Crc16Utils.cs # CRC16 校验工具
├── DataConverter.cs # 数据转换工具
└── CancelTokenExtensions.cs # 取消令牌扩展
ModbusLib.Tests/ # 单元测试
├── Clients/ # 客户端测试
├── Functional/ # 功能测试
├── Mocks/ # 模拟对象
├── Transports/ # 传输层测试
└── Utils/ # 工具类测试
# 构建项目 dotnet build # 运行测试 dotnet test # 发布 NuGet 包 dotnet pack
using ModbusLib.Clients; using ModbusLib.Models; // 简化构造(推荐) var rtuClient = new ModbusRtuClient("COM1", 9600); // 或使用完整配置 var rtuClient = new ModbusRtuClient(new SerialConfig { PortName = "COM1", BaudRate = 9600, Parity = Parity.None, DataBits = 8, StopBits = StopBits.One });
说明:
ModbusRtuClient固定使用 RTU 协议,无需指定ProtocolType
using ModbusLib.Clients; using ModbusLib.Enums; using ModbusLib.Models; // TCP 传输 + TCP 协议(默认,标准 Modbus TCP) var tcpClient = new ModbusTcpClient("192.168.1.100", 502); // 或显式指定 var tcpClient = new ModbusTcpClient("192.168.1.100", 502, protocol: ProtocolType.Tcp); // TCP 传输 + RTU 协议(RTU over TCP) var rtuOverTcpClient = new ModbusTcpClient("192.168.1.100", 502, protocol: ProtocolType.Rtu); // 使用完整配置 var tcpClient = new ModbusTcpClient(new NetworkConfig { RemoteHost = "192.168.1.100", RemotePort = 502, ConnectTimeout = 5000, ReceiveTimeout = 3000, SendTimeout = 3000 }, protocol: ProtocolType.Tcp);
using ModbusLib.Clients; using ModbusLib.Enums; // UDP 传输 + TCP 协议(默认) var udpClient = new ModbusUdpClient("192.168.1.100", 502); // UDP 传输 + RTU 协议(RTU over UDP) var rtuOverUdpClient = new ModbusUdpClient("192.168.1.100", 502, protocol: ProtocolType.Rtu);
using ModbusLib.Clients; using ModbusLib.Enums; using ModbusLib.Models; // Pipe + TCP 协议(默认) var pipeSession = new PipeSession(); var pipeClient = new ModbusPipeClient(pipeSession); // Pipe + RTU 协议 var pipeRtuClient = new ModbusPipeClient(pipeSession, protocol: ProtocolType.Rtu);
using ModbusLib.Clients; using ModbusLib.Enums; using ModbusLib.Models; // Channel + TCP 协议(默认) var channelSession = new ChannelSession(); var channelClient = new ModbusChannelClient(channelSession); // Channel + RTU 协议 var channelRtuClient = new ModbusChannelClient(channelSession, protocol: ProtocolType.Rtu);
namespace ModbusLib.Enums; public enum ProtocolType { Rtu, // RTU 协议(使用 CRC16 校验) Tcp // TCP 协议(使用 MBAP 报文头) }
| 客户端类型 | 默认协议 | 可选协议 | 典型应用场景 |
|---|---|---|---|
| ModbusRtuClient | RTU | ❌ 不可选 | 串口通信 |
| ModbusTcpClient | TCP | ✅ TCP/RTU | 以太网通信,RTU over TCP |
| ModbusUdpClient | TCP | ✅ TCP/RTU | UDP 广播/组播,RTU over UDP |
| ModbusPipeClient | TCP | ✅ TCP/RTU | 自定义数据流,测试模拟 |
| ModbusChannelClient | TCP | ✅ TCP/RTU | 自定义数据流,测试模拟 |
| 功能码 | 名称 | 描述 |
|---|---|---|
| 0x01 | Read Coils | 读取线圈状态 |
| 0x02 | Read Discrete Inputs | 读取离散输入状态 |
| 0x03 | Read Holding Registers | 读取保持寄存器 |
| 0x04 | Read Input Registers | 读取输入寄存器 |
| 0x05 | Write Single Coil | 写单个线圈 |
| 0x06 | Write Single Register | 写单个寄存器 |
| 0x0F | Write Multiple Coils | 写多个线圈 |
| 0x10 | Write Multiple Registers | 写多个寄存器 |
| 0x17 | Read/Write Multiple Registers | 读写多个寄存器 |
ConnectAsync()/Connect()- 连接到设备DisconnectAsync()/Disconnect()- 断开连接IsConnected- 获取连接状态
ReadCoilsAsync()/ReadCoils()- 读取线圈状态ReadDiscreteInputsAsync()/ReadDiscreteInputs()- 读取离散输入ReadHoldingRegistersAsync()/ReadHoldingRegisters()- 读取保持寄存器ReadInputRegistersAsync()/ReadInputRegisters()- 读取输入寄存器ReadHoldingRegistersRawAsync()/ReadHoldingRegistersRaw()- 读取保持寄存器原始字节ReadInputRegistersRawAsync()/ReadInputRegistersRaw()- 读取输入寄存器原始字节
WriteSingleCoilAsync()/WriteSingleCoil()- 写单个线圈WriteMultipleCoilsAsync()/WriteMultipleCoils()- 写多个线圈WriteSingleRegisterAsync()/WriteSingleRegister()- 写单个寄存器WriteMultipleRegistersAsync()/WriteMultipleRegisters()- 写多个寄存器WriteMultipleRegistersRawAsync()/WriteMultipleRegistersRaw()- 写原始字节到寄存器
ReadWriteMultipleRegistersAsync()/ReadWriteMultipleRegisters()- 读写多个寄存器
ReadHoldingRegistersAsync<T>()/ReadHoldingRegisters<T>()- 泛型读取保持寄存器ReadInputRegistersAsync<T>()/ReadInputRegisters<T>()- 泛型读取输入寄存器WriteMultipleRegistersAsync<T>()/WriteMultipleRegisters<T>()- 泛型写入寄存器
public class SerialConfig { public string PortName { get; set; } // 串口名称 (如 COM1) public int BaudRate { get; set; } // 波特率,默认 9600 public Parity Parity { get; set; } // 校验位,默认 None public int DataBits { get; set; } // 数据位,默认 8 public StopBits StopBits { get; set; } // 停止位,默认 One public int ReadTimeout { get; set; } // 读取超时(毫秒) public int WriteTimeout { get; set; } // 写入超时(毫秒) }
public class NetworkConfig { public string RemoteHost { get; set; } // 远程主机地址 public int RemotePort { get; set; } // 远程端口,默认 502 public int ConnectTimeout { get; set; } // 连接超时(毫秒) public int ReceiveTimeout { get; set; } // 接收超时(毫秒) public int SendTimeout { get; set; } // 发送超时(毫秒) public int ReceiveBufferSize { get; set; } // 接收缓冲区大小 public int SendBufferSize { get; set; } // 发送缓冲区大小 public string? LocalHost { get; set; } // 本地主机地址(可选) public int? LocalPort { get; set; } // 本地端口(可选) }
Timeout(客户端/传输层属性)为单次请求的整体超时,默认-1表示不启用;设为>= 0后生效(毫秒)。NetworkConfig.ReceiveTimeout/SendTimeout(串口对应ReadTimeout/WriteTimeout)同样作用于异步收发,不再只是同步 I/O 的配置。- 通信超时或异常后,TCP 传输会自动断开底层连接并清理可能残留的半帧数据,避免后续请求读到错位数据;配合
Retries > 0时,客户端会在重试前自动重连。 Retries默认0(不重试)。需要自动恢复时请显式设置,例如client.Retries = 2。- 触发重试的异常包括:
ModbusTimeoutException、ModbusCommunicationException、ModbusConnectionException以及设备忙(TargetDeviceBusy)异常。
库支持所有 unmanaged 类型的泛型读写,包括但不限于:
byte,sbyte- 8 位整数short,ushort- 16 位整数int,uint- 32 位整数long,ulong- 64 位整数float- 单精度浮点数double- 双精度浮点数- 任何
struct类型
// 读取 10 个字节(占用 5 个寄存器) var bytes = await client.ReadHoldingRegistersAsync<byte>(unitId: 1, startAddress: 0, count: 10); // 读取 5 个整数(占用 10 个寄存器) var ints = await client.ReadHoldingRegistersAsync<int>(unitId: 1, startAddress: 10, count: 5); // 写入浮点数数组 float[] values = new float[] { 1.5f, 2.5f, 3.5f }; await client.WriteMultipleRegistersAsync(unitId: 1, startAddress: 0, values);
欢迎提交 Issue 和 Pull Request!
本项目采用 MIT 许可证。