NMODBUS 实战:C# 实现 Modbus TCP 主站与从站模拟
简介NMODBUS.zip 是一套面向 C# 开发者的 MODBUS TCP/IP 通信库资源适合刚接触工业自动化协议、需要快速与 PLC 建立数据交互的新手与中级开发者。它封装了 ModbusTcpMaster 等核心类提供读写保持寄存器、线圈以及数据转换、异常处理等常用能力可帮助读者省去从零实现协议栈的繁琐过程直接集成到上位机或采集项目中。压缩包共 30 个文件约 529KB包含 5 个 dll 动态库、5 个 xml 文档、3 个 cs 源码、3 个 config 配置、2 个 exe 示例程序以及 nupkg 包、sln 解决方案和 csproj 工程文件覆盖从引用库到运行示例的完整链路。目录中可见 ModbusTcpip 示例工程与 NModbus 3.0.62 依赖便于对照理解客户端创建、请求发送与响应解析流程。目前已有 305 人学习适合作为 MODBUS 通信入门与项目集成的参考素材。1. 从一次现场联调说起NMODBUS.zip 到底能解决什么去年冬天某产线改造项目PLC 侧用的是 Modbus TCP上位机是 C# 写的 WinForm 监控界面。现场调试时发现用现成的商业库要么授权费用高要么在 .NET Framework 4.0 环境下跑不起来。折腾了两天最后翻出一个叫 NMODBUS 的开源库解压后直接引用 DLL半小时跑通读写保持寄存器。那次之后我手里常备一份 NMODBUS.zip遇到 Modbus TCP/IP 的活儿基本先拿它试。NMODBUS 是一个纯 C# 实现的 Modbus 协议库支持 Modbus TCP、Modbus RTU、Modbus ASCII 三种模式覆盖主站Master和从站Slave两种角色。它不依赖任何第三方组件源码结构清晰适合嵌入到工控上位机、数据采集网关、设备模拟器等场景。如果你正在用 C# 做 Modbus TCP/IP 通信又不想被商业库的授权和平台限制卡住这份源码包值得拆开看看。它适合谁一是做工业自动化上位机的开发者需要快速读写 PLC 寄存器二是做设备模拟测试的工程师要用从站模式模拟一台 Modbus 设备三是想理解 Modbus 协议帧结构的学习者源码里把功能码、事务标识、单元标识都拆得很清楚。不适合谁如果你的项目要求跨平台到 Linux 且用 .NET Core需要确认版本兼容性如果只是偶尔发一两条 Modbus 指令用串口调试助手可能更快。2. 拆开 NMODBUS.zip源码结构、核心类与选型理由2.1 源码目录里有什么解压 NMODBUS.zip 后通常能看到这样的结构不同版本略有差异以实际为准NMODBUS/ ├── Modbus/ │ ├── Device/ │ │ ├── ModbusMaster.cs │ │ ├── ModbusSlave.cs │ │ └── ModbusSerialMaster.cs │ ├── Data/ │ │ ├── DataStore.cs │ │ └── RegisterEventArgs.cs │ ├── IO/ │ │ ├── ModbusTransport.cs │ │ └── TcpTransport.cs │ └── Utility/ │ └── ModbusUtility.cs ├── Samples/ │ ├── TcpMasterSample.cs │ └── TcpSlaveSample.cs └── NMODBUS.csproj核心类集中在Modbus.Device命名空间下。ModbusIpMaster是 TCP 主站入口ModbusSerialMaster是串口主站入口ModbusSlave用于从站模拟。DataStore是从站的数据存储容器负责维护线圈、离散输入、保持寄存器、输入寄存器的值。2.2 为什么选 NMODBUS 而不是自己写 Socket自己用TcpClient拼 Modbus TCP 帧不是不行但有几个坑事务标识符Transaction ID需要自己维护递增多线程并发时容易重复异常码解析要对照协议表粘包处理需要自己写缓冲区。NMODBUS 把这些都封装好了对外暴露的是ReadHoldingRegisters、WriteSingleCoil这类方法调用时不用关心底层字节序。另一个选型理由是它的从站实现。很多轻量库只做 Master但 NMODBUS 的ModbusSlave配合DataStore可以快速搭一个模拟设备用来测试上位机逻辑不用真接 PLC。这在开发阶段很省事。2.3 环境准备与引用方式NMODBUS 通常以源码形式分发可以直接把Modbus文件夹拖进你的项目也可以编译成 DLL 后引用。我一般用后者方便多个项目复用。# 假设已安装 .NET SDK进入源码目录 cd NMODBUS dotnet build -c Release # 编译产物在 bin/Release/ 下找到 NMODBUS.dll如果项目是 .NET Framework 4.0 及以上用 Visual Studio 新建类库项目把源码文件添加进去编译即可。注意部分老版本 NMODBUS 使用了System.IO.Ports在 .NET Core 下需要额外安装System.IO.PortsNuGet 包。提示编译前确认目标框架。.NET Framework 项目直接引用源码最省事.NET Core / .NET 5 项目建议先编译成 DLL再通过项目引用或 NuGet 本地源引入。3. 用 NMODBUS 写一个 Modbus TCP 主站连接、读写与参数配置3.1 建立 TCP 连接与主站实例Modbus TCP 默认端口是 502但现场设备经常改成 503 或 1502。连接时用TcpClient指定 IP 和端口然后传给ModbusIpMaster.CreateIp。using System.Net.Sockets; using Modbus.Device; // 创建 TCP 客户端连接目标设备 var tcpClient new TcpClient(192.168.1.10, 502); // 基于 TCP 客户端创建 Modbus 主站 var master ModbusIpMaster.CreateIp(tcpClient); // 设置超时避免网络异常时线程卡死 tcpClient.ReceiveTimeout 2000; tcpClient.SendTimeout 2000;ModbusIpMaster.CreateIp接收一个TcpClient对象内部会创建TcpTransport处理收发。超时设置建议放在TcpClient上而不是 Modbus 层因为底层 Socket 超时更直接。如果现场网络抖动大ReceiveTimeout 可以放宽到 3000 毫秒。3.2 读保持寄存器的参数怎么填读保持寄存器用ReadHoldingRegisters两个参数起始地址和数量。注意这里的地址是协议地址不是 PLC 手册上的 Modbus 地址。比如手册写 40001对应协议地址 0写 40010对应协议地址 9。// 从站地址单元标识为 1起始地址 0读 10 个寄存器 byte slaveId 1; ushort startAddress 0; ushort numRegisters 10; ushort[] registers master.ReadHoldingRegisters(slaveId, startAddress, numRegisters); // 遍历输出实际项目中通常按业务解析 for (int i 0; i registers.Length; i) { Console.WriteLine($寄存器 {startAddress i}: {registers[i]}); }slaveId在 Modbus TCP 里对应 MBAP 头中的单元标识如果设备不支持多从站填 1 即可。startAddress是 0 基地址numRegisters一次最多读 125 个协议限制超了会抛异常。读回来的ushort[]每个元素是一个 16 位寄存器值32 位浮点数需要自己拼两个寄存器。3.3 写单个线圈与写多个寄存器写线圈用WriteSingleCoil值用bool写多个寄存器用WriteMultipleRegisters传ushort[]。// 写单个线圈地址 5置为 true master.WriteSingleCoil(1, 5, true); // 写多个保持寄存器起始地址 10写入 3 个值 ushort[] values { 100, 200, 300 }; master.WriteMultipleRegisters(1, 10, values);写操作没有返回值成功即静默。如果从站返回异常码NMODBUS 会抛SlaveException里面包含SlaveExceptionCode可以据此判断是非法地址还是非法数据值。实际项目中建议用 try-catch 包住记录日志。3.4 从站模拟用 DataStore 搭一个虚拟设备开发阶段没有真实 PLC 时可以用 NMODBUS 的从站功能模拟。核心是DataStore和ModbusSlave。using Modbus.Data; using Modbus.Device; // 创建数据存储默认包含线圈、离散输入、保持寄存器、输入寄存器 var dataStore DataStoreFactory.CreateDefaultDataStore(); // 初始化保持寄存器前 10 个值 for (ushort i 0; i 10; i) { dataStore.HoldingRegisters[i] (ushort)(i * 10); } // 监听 502 端口创建从站 var listener new TcpListener(System.Net.IPAddress.Any, 502); listener.Start(); var slave ModbusTcpSlave.CreateTcp(1, listener); slave.DataStore dataStore; slave.Listen();DataStoreFactory.CreateDefaultDataStore()会初始化四个数据区默认大小各 100 个。ModbusTcpSlave.CreateTcp的第一个参数是从站地址第二个是TcpListener。Listen()是阻塞调用通常放在独立线程里。这样上位机连上来读写数据就从dataStore里取方便验证逻辑。4. 避坑与排查NMODBUS 实战中容易翻车的五个点4.1 现象读寄存器返回异常码 0x02原因起始地址或数量超出从站支持范围。很多 PLC 的保持寄存器只开放前 100 个你从 200 开始读从站直接回非法数据地址。解决先确认从站手册里的寄存器映射表把地址换算成 0 基协议地址。不确定时从地址 0 读 1 个寄存器试起逐步加数量。4.2 现象连接成功但读写超时原因Modbus TCP 的单元标识填错或者目标端口不是 502。有些网关设备把 Modbus TCP 映射到非标端口但默认按 502 连。解决用TcpClient先测端口通不通再确认slaveId。如果设备文档写“站号 2”那slaveId就填 2不要填 1。4.3 现象多线程调用时数据错乱原因ModbusIpMaster实例不是线程安全的。多个线程同时调ReadHoldingRegisters事务标识符会冲突返回的数据可能串包。解决每个线程创建独立的TcpClient和ModbusIpMaster或者加锁串行化。我一般用lock包住读写操作简单有效。4.4 现象32 位浮点数解析出来是乱值原因Modbus 寄存器是 16 位的32 位浮点需要两个寄存器拼接且字节序有 ABCD、CDAB、BADC、DCBA 四种。NMODBUS 返回的是ushort[]不负责浮点转换。解决确认设备用的是哪种字节序用BitConverter手动拼。常见做法是先把两个ushort转成byte[]再按顺序组合后转float。4.5 现象从站模拟时上位机连不上原因TcpListener绑定的是IPAddress.Any但防火墙拦了 502 端口或者slave.Listen()没在独立线程里跑主线程被阻塞。解决检查防火墙入站规则放行 502 端口。Listen()用Task.Run包起来避免阻塞 UI 线程。注意NMODBUS 的从站模拟仅用于测试不要直接暴露在公网环境。生产环境建议加访问控制。5. 进阶技巧用 NMODBUS 做批量采集与异常重连5.1 批量读取的合并策略一条 Modbus TCP 报文最多读 125 个寄存器。如果业务需要读 500 个寄存器不要分 4 次从地址 0、125、250、375 读而是先确认这些寄存器在从站里是否连续。连续的话分 4 次读不连续的话按块合并减少请求次数。// 按 125 个一组分批读取避免单次超限 ushort totalStart 0; ushort totalCount 500; ushort batchSize 125; for (ushort offset 0; offset totalCount; offset batchSize) { ushort count (ushort)Math.Min(batchSize, totalCount - offset); ushort[] batch master.ReadHoldingRegisters(1, (ushort)(totalStart offset), count); // 处理 batch存入本地缓存 }Math.Min保证最后一批不超限。每批之间可以加Thread.Sleep(10)给从站喘息时间尤其是低端 PLC连续请求太快会丢包。5.2 断线重连的封装现场网络不稳定TcpClient断开后ModbusIpMaster不会自动重连。我一般封装一个ModbusClientWrapper在读写异常时重建连接。public class ModbusClientWrapper { private TcpClient _tcpClient; private ModbusIpMaster _master; private string _ip; private int _port; public ModbusClientWrapper(string ip, int port) { _ip ip; _port port; Connect(); } private void Connect() { _tcpClient?.Close(); _tcpClient new TcpClient(_ip, _port); _tcpClient.ReceiveTimeout 2000; _master ModbusIpMaster.CreateIp(_tcpClient); } public ushort[] ReadHoldingRegisters(byte slaveId, ushort start, ushort count) { try { return _master.ReadHoldingRegisters(slaveId, start, count); } catch (Exception) { // 发生异常时重连一次再试 Connect(); return _master.ReadHoldingRegisters(slaveId, start, count); } } }这个封装把重连逻辑收在ReadHoldingRegisters里调用方不用关心连接状态。注意重连只做一次如果第二次还失败就让异常抛出去避免死循环。5.3 验证方法用 Modbus Poll 对照写完代码后怎么确认读上来的值是对的我习惯用 Modbus Poll 这类工具同时连同一个从站对比两边读到的寄存器值。如果 Modbus Poll 读出来是 1234你的代码读出来也是 1234说明地址和解析没问题。如果 Modbus Poll 能读到你读不到检查slaveId和起始地址。另一个验证手段是用 NMODBUS 的从站模拟自己写值自己读闭环测试。比如在dataStore.HoldingRegisters[0]里写 999然后主站读地址 0看是不是 999。这个法子能排除 PLC 侧的问题快速定位是代码问题还是设备问题。从那以后我每次拿到新的 Modbus 设备都先用 NMODBUS 从站模拟跑一遍读写闭环确认代码逻辑没问题再接真实设备。这样现场调试时少了很多“到底是代码还是设备”的纠结。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

为什么iwe拒绝规定笔记结构?揭秘文本图管理工具的3条设计原则

为什么iwe拒绝规定笔记结构?揭秘文本图管理工具的3条设计原则

人工智能Agent 记忆MCP 服务CLI知识管理开发工具 【免费下载链接】iwe Markdown knowledge graph — LSP for your editor, CLI MCP memory for your AI agents 项目地址: https://gitcode.com/gh_mirrors/iw/iwe 点击查看 免费下载 🤔 用过不少笔记工…

2026/10/11 13:03:46 阅读更多 →
LeaScript:让私域陪练看得见,留得住,服务得上

LeaScript:让私域陪练看得见,留得住,服务得上

GitHub - ancientcc/LeaScript: LeaScript 是兰栖科技(Leagor)开源的动作脚本规范。它专为端侧离线视觉动作识别设计,底层支持 Mediapipe,全程无需联网,确保数据隐私安全。 GitHub 做私域健身的 UP 主,大…

2026/10/11 13:03:46 阅读更多 →
无人机视角交通目标检测:YOLOv8小目标训练与OBB旋转框实战指南

无人机视角交通目标检测:YOLOv8小目标训练与OBB旋转框实战指南

简介:这是一份面向智能交通、自动驾驶与智慧城市应用的无人机视角交通目标检测YOLO格式数据集。数据集包含956张训练航拍图与169张验证航拍图,覆盖公交、轿车、厢式货车、摩托车、船舶、商业车辆、工程车辆、特种车辆及区域特色车辆共9类陆海交通工具&am…

2026/10/11 13:03:46 阅读更多 →

最新新闻

无界队列会让 maximumPoolSize 失效,这句 Javadoc 很少有人引

无界队列会让 maximumPoolSize 失效,这句 Javadoc 很少有人引

➡️ 程序员曜灵 后端面试追问链 - 欢迎认识我 作者程序员曜灵,绿泡泡「我要拿offer」和小红书同名。 主业在一家大型央企做后端开发,Java 方向,参与过公司内部招聘面试。 这里在拆高频面试题的追问链,一题三层,每层给及格线答案和大多数人挂在哪。 工作日每天一篇,评论区点最…

2026/10/11 13:53:11 阅读更多 →
STEP7_HSPs.zip硬件支持包安装指南:解决S7 V5.X硬件目录缺失与模块识别问题

STEP7_HSPs.zip硬件支持包安装指南:解决S7 V5.X硬件目录缺失与模块识别问题

简介:这份资源是面向西门子S7系列PLC编程人员的STEP7 V5.X版本热修复服务包合集,适用于仍在使用5.1、5.2、5.3等经典版本、希望在不重装软件的前提下修复已知问题、提升编程环境稳定性的工程师与自动化技术人员。压缩包共342个文件,以339个hs…

2026/10/11 13:53:11 阅读更多 →
编程模型 API 哪家划算?从 OpenAI 与 Anthropic 的 Token 计费差异看账单为何差十倍

编程模型 API 哪家划算?从 OpenAI 与 Anthropic 的 Token 计费差异看账单为何差十倍

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 13:53:11 阅读更多 →
企业自建 MCP Server 实战:用 Python 打通 ERP 与数据库,TaoToken 统一 Key 接入

企业自建 MCP Server 实战:用 Python 打通 ERP 与数据库,TaoToken 统一 Key 接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 13:53:11 阅读更多 →
最优化决策模型实战:从线性规划建模到求解器落地

最优化决策模型实战:从线性规划建模到求解器落地

简介:这是一份面向经济管理类专业学生、教师及初学者的《经济管理中的计算机应用》第八章课件,聚焦最优化决策模型的理论与Excel求解实操。PPT内容系统完整,从最优化问题的定义、分类与数学模型讲起,覆盖线性规划、非线性规划、整…

2026/10/11 13:53:11 阅读更多 →
SAM边缘部署:基于ONNX与OpenVINO的C++推理实战

SAM边缘部署:基于ONNX与OpenVINO的C++推理实战

简介:面向需要将SAM分割模型部署到实际业务的计算机视觉开发者,这份基于ONNX与OpenVINO工具链的C实现教程,提供了从模型导出、格式转换到推理优化的完整落地路径。压缩包共32个文件,涵盖C源文件(.h/.cpp)、…

2026/10/11 13:52:11 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →