modbustcp_c#modbustcp在.net 6+中无法运行,因其基于.net framework 2.0,依赖已废弃的同步socket阻塞模式,缺乏超时设置、ipv6支持及mbap头校验;推荐改用nmodbus4,它支持.net standard 2.0+和异步api,并需注意防火墙、plc地址配置与寄存器偏移等关键问题。

直接用 ModbusTCP_c#ModbusTCP 这个项目(GitHub 上常见但非官方维护的旧版库)在 .NET 6+ 或新项目里大概率会编译失败、连接超时或读不到数据——它依赖已废弃的 System.Net.Sockets.Socket 同步阻塞模式,且没处理 IPv6、超时重试和异常传播。
为什么 ModbusTCP_c#ModbusTCP 在新项目里跑不起来
这个库本质是 2010 年左右基于 .NET Framework 2.0 写的,核心问题有三个:
-
TcpClient.Connect()调用后没设SendTimeout/ReceiveTimeout,网络抖动时线程卡死 - 所有读写都用
NetworkStream.Read()同步阻塞,无法 await,跟现代异步编程模型冲突 - 没做 MBAP 头校验(事务 ID、协议 ID、长度字段),PLC 返回错误响应时容易解析错位,抛出
IndexOutOfRangeException
替换方案:用 NModbus4(推荐)或 QsLibraries.Modbus
NModbus4 是目前最活跃的开源 Modbus TCP 库,支持 .NET Standard 2.0+,已适配 .NET 5/6/7/8,且提供同步/异步双 API:
- 连接 PLC:
var factory = new ModbusFactory(); var master = factory.CreateTcpMaster("192.168.1.10", 502); - 读保持寄存器(4x):
var values = await master.ReadHoldingRegistersAsync(slaveId: 1, startAddress: 0, numberOfPoints: 10); - 写单个线圈(0x):
await master.WriteSingleCoilAsync(slaveId: 1, coilAddress: 0, value: true);
注意:NModbus4 默认启用 UseExtendedUnitId = false,若 PLC 要求带扩展 Unit ID(如某些 Schneider 设备),需手动设置 master.Transport.UseExtendedUnitId = true;。
关键配置与避坑点
实际部署时最容易栽在三处:
- 防火墙:Windows Defender 或企业防火墙默认拦截 502 端口,需放行
dotnet.exe或显式开放 TCP 502 入站规则 - PLC IP 和从站 ID 不匹配:多数国产 PLC(如汇川 H3U)默认从站 ID 是 1,但有些固件要求设为 255;IP 地址必须和上位机在同一子网(如 PLC 是
192.168.2.100,上位机不能是192.168.1.x) - 寄存器地址偏移:
ReadHoldingRegistersAsync(startAddress: 0)对应 PLC 的 40001,不是 40000;若文档写“读 40001~40010”,代码里要传startAddress: 0, numberOfPoints: 10
最小可运行示例(.NET 6 Console)
新建项目后执行:dotnet add package NModbus4,然后:
using NModbus;
using NModbus.Serial;
using System.Net.Sockets;
var factory = new ModbusFactory();
using var tcpClient = new TcpClient();
await tcpClient.ConnectAsync("192.168.1.10", 502);
var master = factory.CreateMaster(tcpClient);
try
{
var values = await master.ReadHoldingRegistersAsync(1, 0, 10);
Console.WriteLine($"读到:{string.Join(", ", values)}");
}
catch (Exception ex) when (ex is IOException || ex is TimeoutException)
{
Console.WriteLine($"连接或读取失败:{ex.Message}");
}
这段代码能跑通,说明基础链路没问题;如果报 SocketException: No connection could be made,优先查网线、IP、PLC 是否启用了 Modbus TCP 服务(不是 Modbus RTU over TCP 封装)。
真正麻烦的是 PLC 厂家私有协议扩展(比如寄存器映射加了 CRC 校验、地址做了非线性映射),这种没法靠通用库解决,得看具体手册——别指望一个 ReadHoldingRegisters 调用就能读到真实物理量。










