
简介这份资源面向工业自动化与物联网方向的C#开发者提供一套基于.NET Core的OPC UA开发环境覆盖OPC UA规范1.03版适合希望快速上手或验证客户端与服务端通信的初中级工程师。压缩包共195个文件以179个dll动态库为核心辅以4个exe可执行程序、4个config配置文件和4个xml说明文件整体约15.06MB其中Opc.Ua.Core.dll与BouncyCastle.Crypto.dll承担协议栈与加密通信职责config文件则对应示例程序的运行参数。资源内含SampleClient、BoilerClient等客户端示例以及BoilerServer、ReferenceServer等服务端示例可分别演示数据读写、变量订阅、方法调用与锅炉模拟场景帮助读者理解OPC UA的信息模型、安全模型与传输层机制。目前已有557人学习下载可作为跨平台OPC UA应用开发的参考起点。1. 从一台西门子 PLC 说起C# 在 .NET Core 上跑通 OPC UA 客户端到底难在哪车间里一台 S7-1500 已经开了 OPC UA 服务端上位机却还在用老掉牙的 WinCC 或者某个只认 Windows 的 DLL 去读数据。你想用 C# 写一个跨平台的上位机跑在 .NET Core 上最好还能丢进 Linux 工控机里长期运行——这个念头一起坑就来了。标题里的 opc_C#OPCUA_.netcore_opcua客户端_覆盖1.03版_OPCUA说的就是这么一件事用 C# 在 .NET Core 环境下实现一个 OPC UA 客户端覆盖到 1.03 版协议规范能连 PLC、能读点位、能订阅变化。它解决的是「把设备数据稳定拿进自己的程序」这个最朴素也最要命的需求适合做上位机、SCADA、MES 数据采集、设备状态监控的工程师。难点不在协议本身而在 .NET Core 的生态里选哪个库、证书怎么过、会话怎么保活、断线怎么重连——这些才是真正让人翻车的地方。2. 选型先定生死.NET Core 下能用的 OPC UA 客户端库怎么挑2.1 为什么不是随便找个 NuGet 包就能用OPC UA 不是 HTTP它是一套完整的二进制协议栈包含传输层、安全通道、会话、订阅、方法调用等一整套规范。在 .NET Core 上能用的客户端库其实就那么几个方向一是 OPC Foundation 官方那套基于 .NET Standard 的栈二是社区维护的轻量封装三是某些商业库的免费额度版。官方栈最完整覆盖到 1.03 甚至更高版本的规范但 API 偏底层证书和会话管理要自己写不少代码社区封装上手快但遇到复杂类型或者安全策略协商时容易卡住。我一般会先确认三件事目标 PLC 或服务端支持哪些安全策略None、Basic256Sha256、Aes128Sha256RsaOaep 等、是否需要订阅而不是轮询、是否要跨平台跑在 Linux 上。如果这三点里有一项是硬需求官方栈基本是唯一稳妥选择。选型时别只看 GitHub 星数要看它最近一次提交是不是还在跟进 .NET 6/7/8 的兼容性很多包在 .NET Framework 下能用一到 .NET Core 就报平台不支持。2.2 用 NuGet 把最小客户端骨架搭起来先建一个 .NET Core 控制台项目把官方栈的核心包引进来。下面这条命令是搭骨架的第一步注意包名和版本要跟你实际用的 .NET 版本匹配别硬抄。dotnet new console -n OpcUaClientDemo cd OpcUaClientDemo dotnet add package OPCFoundation.NetStandard.Opc.Ua.Client dotnet add package OPCFoundation.NetStandard.Opc.Ua.Configuration这两条包引用分别负责客户端会话和配置加载。装完之后项目文件里会出现对应的 PackageReference。这里有个细节如果你只引 Client 不引 Configuration编译时不会报错但运行到加载应用配置那一步会直接抛异常提示找不到配置类型。所以两个一起加别省。2.3 连接之前先把应用配置和证书理清楚OPC UA 的安全通道建立依赖应用实例证书。在 .NET Core 上证书默认会生成到用户目录下的一个 pki 文件夹里。第一次运行时如果目录不存在程序会自己建但在 Linux 上要注意运行账户对那个目录有没有写权限否则会卡在证书生成那一步日志里只给一句很模糊的「无法创建应用实例证书」。var config new ApplicationConfiguration() { ApplicationName OpcUaClientDemo, ApplicationType ApplicationType.Client, SecurityConfiguration new SecurityConfiguration { AutoAcceptUntrustedCertificates true, // 调试阶段临时用生产环境必须关掉 ApplicationCertificate new CertificateIdentifier { StoreType CertificateStoreType.Directory, StorePath pki/own, SubjectName CNOpcUaClientDemo }, TrustedIssuerCertificates new CertificateTrustList { StoreType CertificateStoreType.Directory, StorePath pki/issuer }, TrustedPeerCertificates new CertificateTrustList { StoreType CertificateStoreType.Directory, StorePath pki/trusted }, RejectedCertificateStore new CertificateTrustList { StoreType CertificateStoreType.Directory, StorePath pki/rejected } }, TransportConfigurations new TransportConfigurationCollection(), TransportQuotas new TransportQuotas { OperationTimeout 15000 }, ClientConfiguration new ClientConfiguration { DefaultSessionTimeout 60000 } }; await config.Validate(ApplicationType.Client);这段代码做了三件事定义应用类型为客户端、指定证书存储路径、设置操作超时和会话超时。AutoAcceptUntrustedCertificates在调试时能省掉手动信任服务端证书的麻烦但上线前一定要改成 false否则等于把安全通道的校验关了。OperationTimeout默认值偏短遇到网络抖动大的现场15 秒是个比较稳的起点。DefaultSessionTimeout决定会话多久没心跳就断后面做保活时要跟这个值对齐。3. 连上只是开始会话、订阅与数据读取的落地写法3.1 建立会话并拿到第一个点位值会话建立是 OPC UA 客户端最核心的一步。下面这段代码把配置、端点选择和会话创建串起来注意端点描述里的安全策略要跟服务端实际开启的一致否则会报「找不到匹配的端点」。var endpointDescription CoreClientUtils.SelectEndpoint(opc.tcp://192.168.1.10:4840, useSecurity: false); var endpointConfiguration EndpointConfiguration.Create(config); var endpoint new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration); var session await Session.Create( config, endpoint, updateBeforeConnect: false, sessionName: DemoSession, sessionTimeout: 60000, identity: new UserIdentity(new AnonymousIdentityToken()), preferredLocales: null ); var readValue session.ReadValue(ns3;s\DB1\.\Temperature\); Console.WriteLine($温度值: {readValue.Value});SelectEndpoint的第二个参数useSecurity设为 false 表示走无安全策略适合内网调试生产环境要改成 true 并传入证书。Session.Create里的identity决定用什么身份连服务端匿名、用户名密码、证书三种方式按现场配置选。ReadValue里的节点 ID 写法是ns命名空间索引;s节点标识不同 PLC 的命名空间索引不一样西门子一般是 3但别死记用 UaExpert 先连上看一眼最稳。3.2 用订阅替代轮询把变化数据推回来轮询读点位在点位数少时还行一旦上百个点网络和 PLC 都扛不住。OPC UA 的订阅机制才是正路。下面这段代码创建一个订阅把几个点位加进去并注册回调。var subscription new Subscription(session.DefaultSubscription) { PublishingInterval 1000, // 服务端推送周期单位毫秒 KeepAliveCount 10, // 多少次发布无变化后发心跳 LifetimeCount 30, // 多少次发布无订阅者响应后删除订阅 MaxNotificationsPerPublish 1000, PublishingEnabled true, Priority 0 }; session.AddSubscription(subscription); await subscription.CreateAsync(); var monitoredItem new MonitoredItem(subscription.DefaultItem) { DisplayName Temperature, StartNodeId ns3;s\DB1\.\Temperature\, AttributeId Attributes.Value, SamplingInterval 500, // 采样周期要小于等于发布周期 QueueSize 10, DiscardOldest true }; monitoredItem.Notification (item, args) { if (args.NotificationValue is MonitoredItemNotification notification) { Console.WriteLine($变化: {notification.Value.Value} 时间: {notification.Value.SourceTimestamp}); } }; subscription.AddItem(monitoredItem); await subscription.ApplyChangesAsync();PublishingInterval是服务端往客户端推的周期SamplingInterval是服务端去 PLC 采样的周期后者必须小于等于前者否则数据会丢。QueueSize决定采样快于发布时缓存多少个值DiscardOldest为 true 表示缓存满时丢最旧的。回调里拿到的SourceTimestamp是 PLC 侧的时间戳不是客户端收到的时间做时序分析时要用这个。订阅创建后如果一直收不到通知先检查PublishingEnabled是不是 true再看服务端有没有把这个节点标记为可订阅。3.3 断线重连与会话保活别让程序悄悄死掉工业现场网络抖动是常态会话断了不重连程序看起来还在跑数据早就不更新了。下面这段是重连逻辑的核心骨架。session.KeepAlive async (s, e) { if (e.Status ! null ServiceResult.IsBad(e.Status)) { Console.WriteLine($会话异常: {e.Status}); try { await session.ReconnectAsync(); Console.WriteLine(重连成功); } catch (Exception ex) { Console.WriteLine($重连失败: {ex.Message}); // 这里可以加退避重试别用死循环硬刷 } } };KeepAlive事件在每次心跳时触发e.Status为坏值时说明会话已经不可用。ReconnectAsync会尝试用原参数重建会话但订阅不会自动恢复重连成功后要重新创建订阅或者调用订阅的RecreateAsync。我一般会在重连成功后加一个状态标志让上层逻辑知道数据可能断了一段别拿旧值当实时值用。退避重试建议用 1 秒、2 秒、5 秒、10 秒这样的间隔别一失败就疯狂重连会把服务端拖垮。4. 避坑与排查那些让 OPC UA 客户端翻车的细节4.1 证书报错「BadCertificateUntrusted」怎么处理现象是连接时直接抛异常提示服务端证书不受信任。原因是客户端第一次连某个服务端时服务端证书不在信任列表里。解决方式有两种调试阶段把AutoAcceptUntrustedCertificates设为 true让客户端自动接受生产环境则要把服务端证书导出放进客户端的pki/trusted目录然后重启客户端。注意别把服务端证书丢进pki/issuer那个目录是放签发者证书的放错了不生效。4.2 订阅收不到数据但读点位正常现象是ReadValue能拿到值订阅回调一直不触发。常见原因是SamplingInterval设得比PublishingInterval大服务端采样比发布还慢自然推不出变化。另一个原因是节点的AttributeId没设成Attributes.Value默认可能是别的属性。还有一种情况是服务端对订阅数量有限制超过上限后新订阅被静默拒绝这时要看服务端日志或者减少订阅数。4.3 在 Linux 上跑报「PlatformNotSupportedException」现象是同一个项目在 Windows 上正常丢到 Linux 上启动就崩。原因是某些 OPC UA 库依赖 Windows 特有的证书存储或者加密 API。解决方式是确认用的包是不是纯 .NET Standard 实现证书存储类型改成Directory而不是X509Store。另外 Linux 上要装好libssl相关依赖否则安全通道协商会失败报的错往往很隐晦只说握手失败。4.4 会话频繁断开但网络看着正常现象是会话每隔几分钟就断一次重连后过一会又断。原因通常是DefaultSessionTimeout设得太短而客户端没有及时发心跳。OPC UA 会话靠客户端定期发Publish请求维持如果订阅的PublishingInterval比会话超时还大服务端会认为客户端失联。解决方式是让PublishingInterval小于DefaultSessionTimeout的三分之一并确保KeepAlive事件里正确处理坏状态。4.5 节点 ID 写错却报「BadNodeIdUnknown」现象是读某个点位时报节点不存在但用 UaExpert 能看到这个点。原因是命名空间索引在不同会话里可能不一样尤其是服务端重启后。解决方式是用session.NamespaceUris拿到实际的命名空间数组按 URI 去匹配索引而不是硬编码ns3。西门子 PLC 的命名空间 URI 一般是http://www.siemens.com/s7opcua这类匹配到之后再拼节点标识。5. 把客户端做成能长期跑的服务几个我踩过坑才定下来的习惯先说一个进阶用法把 OPC UA 客户端封装成一个后台服务用IHostedService托管配合CancellationToken做优雅退出。这样丢进 Linux 的 systemd 里就能开机自启不用挂着控制台窗口。封装时把会话、订阅、重连逻辑收在一个类里对外只暴露「连接状态」和「数据变化」两个事件上层业务不碰协议细节。验证客户端稳不稳我一般会做三件事一是拔网线三十秒再插回去看能不能自动重连并恢复订阅二是把服务端重启一次看客户端会不会崩三是连续跑七十二小时看内存有没有持续上涨。内存涨通常是订阅回调里做了耗时操作或者事件没解绑把回调里的逻辑丢到队列里异步处理别在回调里直接写数据库。参数上有一张我常用的对照表现场调的时候直接改这几个值参数调试值生产建议值说明OperationTimeout1500010000~20000网络差取大内网取小DefaultSessionTimeout60000120000要大于 PublishingInterval 的三倍PublishingInterval1000500~2000按数据变化频率调SamplingInterval500等于或略小于 PublishingInterval别大于发布周期QueueSize105~20变化快就加大KeepAliveCount1010~30太大断线发现慢最后说个习惯每次改完连接参数先用 UaExpert 连一遍确认服务端侧没问题再跑自己的客户端。这样能把「服务端配置问题」和「客户端代码问题」分开省掉大量来回猜的时间。OPC UA 这东西协议本身不玄学玄学的是现场网络和证书把这两样摸清楚剩下的就是写代码的功夫。希望帮到你。本文还有配套的精品资源点击获取