OPC UA全称是OPC统一架构,是工业自动化领域用于设备间数据交互的标准化协议,相比传统的OPC DA,它具备跨平台、安全性高、信息模型丰富等优势,在C#开发中常用于工业上位机与下位机、设备之间的数据通信。理解OPC UA的底层原理,能帮助我们更高效地实现相关功能,排查通信过程中的问题。

OPC UA底层核心原理
信息模型
OPC UA采用面向对象的信息模型,所有可访问的资源都被抽象为节点,节点分为对象节点、变量节点、方法节点等类型,每个节点都有唯一的节点ID,节点之间通过引用关系构成层级化的地址空间。客户端通过浏览地址空间可以获取所有可用的资源信息,这种设计让OPC UA可以描述非常复杂的工业设备结构。
通信与安全机制
OPC UA支持多种传输协议,最常用的是基于TCP的二进制编码传输,底层基于TCP套接字建立连接。安全方面支持多种安全策略,包括无安全、Basic256Sha256、Aes256_Sha256_RsaPss等,同时支持用户名密码、证书等多种身份验证方式,通信过程中可以对数据进行签名和加密,满足工业场景的安全需求。
核心服务集
OPC UA定义了丰富的服务集,包括发现服务、会话服务、节点服务、视图服务、查询服务、方法服务等。客户端与服务器之间的所有交互都通过调用这些服务完成,比如建立会话需要调用CreateSession服务,读取变量值需要调用Read服务,订阅数据变化需要调用CreateSubscription和CreateMonitoredItems服务。
C#中使用OPC UA的准备工作
在C#中开发OPC UA应用,推荐使用官方提供的OPC UA Foundation SDK,也就是Opc.Ua.Fx包,可以通过NuGet直接安装。安装完成后,就可以基于SDK提供的类库实现客户端和服务器的开发。
C#实现OPC UA服务器
以下是使用C#创建一个简单OPC UA服务器的示例代码,服务器会暴露一个可写的变量节点,供客户端访问:
using Opc.Ua;
using Opc.Ua.Server;
using System;
namespace OpcUaServerDemo
{
class Program
{
static void Main(string[] args)
{
// 配置服务器参数
var applicationConfiguration = new ApplicationConfiguration
{
ApplicationName = "CSharpOpcUaServer",
ApplicationUri = "urn:localhost:CSharpOpcUaServer",
ProductUri = "urn:ipipp.com:CSharpOpcUaServer",
ServerConfiguration = new ServerConfiguration
{
// 服务器监听地址,使用本地地址
BaseAddresses = new StringCollection { "opc.tcp://127.0.0.1:4840" },
SecurityPolicies = new ServerSecurityPolicyCollection
{
new ServerSecurityPolicy
{
SecurityMode = MessageSecurityMode.None,
SecurityPolicyUri = SecurityPolicies.None
}
}
}
};
// 验证配置
applicationConfiguration.Validate(applicationConfiguration.ApplicationName);
applicationConfiguration.CertificateValidator = new CertificateValidator();
applicationConfiguration.CertificateValidator.CertificateValidation += (sender, eventArgs) =>
{
eventArgs.Accept = true;
};
// 创建服务器实例
var server = new StandardServer();
// 启动服务器
server.Start(applicationConfiguration);
Console.WriteLine("OPC UA服务器已启动,监听地址:opc.tcp://127.0.0.1:4840");
Console.WriteLine("按任意键退出...");
Console.ReadKey();
// 停止服务器
server.Stop();
}
}
// 自定义节点管理器,用于添加自定义变量节点
public class CustomNodeManager : CustomNodeManager2
{
private BaseDataVariableState m_customVariable;
public CustomNodeManager(IServerInternal server, ApplicationConfiguration configuration)
: base(server, configuration, "urn:ipipp.com:CSharpOpcUaServer")
{
// 创建自定义变量节点
m_customVariable = new BaseDataVariableState(null)
{
NodeId = new NodeId("CustomVariable", NamespaceIndex),
BrowseName = new QualifiedName("CustomVariable", NamespaceIndex),
DisplayName = new LocalizedText("自定义变量"),
DataType = DataTypes.Double,
ValueRank = ValueRanks.Scalar,
Value = 0.0,
AccessLevel = AccessLevels.CurrentReadOrWrite,
UserAccessLevel = AccessLevels.CurrentReadOrWrite,
WriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description,
UserWriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description
};
// 将节点添加到地址空间
AddPredefinedNode(SystemContext, m_customVariable);
}
}
}
C#实现OPC UA客户端
以下是C# OPC UA客户端的示例代码,实现连接服务器、读取变量、写入变量、订阅变量变化的功能:
using Opc.Ua;
using Opc.Ua.Client;
using System;
namespace OpcUaClientDemo
{
class Program
{
static void Main(string[] args)
{
// 配置客户端参数
var applicationConfiguration = new ApplicationConfiguration
{
ApplicationName = "CSharpOpcUaClient",
ApplicationUri = "urn:localhost:CSharpOpcUaClient",
ProductUri = "urn:ipipp.com:CSharpOpcUaClient",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier(),
TrustedPeerCertificates = new CertificateTrustList()
}
};
// 创建会话配置
var endpointDescription = new EndpointDescription
{
EndpointUrl = "opc.tcp://127.0.0.1:4840",
SecurityMode = MessageSecurityMode.None,
SecurityPolicyUri = SecurityPolicies.None
};
var endpointConfiguration = EndpointConfiguration.Create(applicationConfiguration);
var endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
// 创建会话
using (var session = Session.Create(applicationConfiguration, endpoint, false, "CSharpClientSession", 60000, null, null))
{
Console.WriteLine("已成功连接到OPC UA服务器");
// 读取变量值
var nodeId = new NodeId("CustomVariable", 2); // 命名空间索引需要根据实际服务器情况调整
DataValue dataValue = session.ReadValue(nodeId);
Console.WriteLine($"读取到的变量值:{dataValue.Value}");
// 写入变量值
var writeValue = new WriteValue
{
NodeId = nodeId,
AttributeId = Attributes.Value,
Value = new DataValue(123.45)
};
var writeResults = session.Write(null, new WriteValueCollection { writeValue });
if (writeResults[0].IsGood())
{
Console.WriteLine("变量写入成功");
}
// 订阅变量变化
var subscription = new Subscription(session.DefaultSubscription)
{
DisplayName = "CustomSubscription",
PublishingInterval = 1000, // 发布间隔1秒
};
session.AddSubscription(subscription);
subscription.Create();
var monitoredItem = new MonitoredItem(subscription.DefaultItem)
{
DisplayName = "MonitorCustomVariable",
StartNodeId = nodeId
};
monitoredItem.Notification += (sender, e) =>
{
var item = sender as MonitoredItem;
if (item != null)
{
foreach (var notification in e.NotificationValue as MonitoredItemNotificationCollection)
{
Console.WriteLine($"变量值变化:{notification.Value.Value}");
}
}
};
subscription.AddItem(monitoredItem);
subscription.ApplyChanges();
Console.WriteLine("开始订阅变量变化,按任意键退出...");
Console.ReadKey();
}
}
}
}
常见问题与注意事项
- 连接失败时需要检查服务器地址、端口是否正确,防火墙是否开放对应端口,安全策略是否匹配。
- 节点ID的命名空间索引需要和服务器端的配置对应,否则会出现节点找不到的错误。
- 生产环境中建议开启安全策略,配置正确的证书,避免明文传输数据。
- 订阅功能需要注意发布间隔和监控项的采样间隔设置,避免过高的资源占用。
C#OPC_UAOPC_UA_clientOPC_UA_server修改时间:2026-07-14 08:09:21