使用Opc.Ua库创建OPC服务器:服务器无法被发现问题排查
OPC UA服务器创建失败排查(仅能看到localhost:4840连接)
我尝试使用OPC基金会的库创建OPC UA服务器,实现代码如下,但服务器未成功创建,仅能看到localhost:4840的连接,请问哪里操作有误?
实现代码
public class OPCServer { public void StartServer() { ApplicationInstance application = new ApplicationInstance(); application.ApplicationName = "MyServer"; application.ApplicationType = ApplicationType.Server; // 加载应用配置 ApplicationConfiguration config = new ApplicationConfiguration() { ApplicationName = "MyServer", ApplicationType = ApplicationType.Server, ApplicationUri = "urn:localhost:MyServer", ServerConfiguration = new ServerConfiguration { BaseAddresses = new StringCollection { "opc.tcp://localhost:4840/MyServer" }, SecurityPolicies = { new ServerSecurityPolicy() { SecurityMode = MessageSecurityMode.None, SecurityPolicyUri = SecurityPolicies.None, } }, }, SecurityConfiguration = new SecurityConfiguration { ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = "MyServer" }, TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications" }, RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates" }, AutoAcceptUntrustedCertificates = true, AddAppCertToTrustedStore = true }, TransportConfigurations = new TransportConfigurationCollection(), TransportQuotas = new TransportQuotas { OperationTimeout = 15000 }, ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }, TraceConfiguration = new TraceConfiguration() }; config.Validate(ApplicationType.Server).GetAwaiter().GetResult(); if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates) { config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); }; } application.ApplicationConfiguration = config; // 检查应用证书 bool haveAppCertificate = application.CheckApplicationInstanceCertificate(false, 0).Result; if (!haveAppCertificate) { throw new Exception("应用实例证书无效!"); } // 启动服务器 application.Start(new MySampleServer()).Wait(); Console.ReadLine(); } } public class MySampleServer : StandardServer { private BaseDataVariableState m_myVariable; protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) { List<INodeManager> nodeManagers = new List<INodeManager> { new MyNodeManager(server, configuration) }; return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray()); } protected override ServerProperties LoadServerProperties() { ServerProperties properties = new ServerProperties { ManufacturerName = "My Manufacturer", ProductName = "My Sample Server", ProductUri = "http://my.sample.server.com/", SoftwareVersion = Utils.GetAssemblySoftwareVersion(), BuildNumber = Utils.GetAssemblyBuildNumber(), BuildDate = Utils.GetAssemblyTimestamp() }; return properties; } } public class MyNodeManager : CustomNodeManager2 { private FolderState myFolder; private BaseDataVariableState myVariable; public MyNodeManager(IServerInternal server, ApplicationConfiguration config) : base(server, config, Namespaces.OpcUa) { SystemContext.NodeIdFactory = this; myFolder = new FolderState(null) { SymbolicName = "MyFolder", NodeId = new NodeId("MyFolder", NamespaceIndex), BrowseName = new QualifiedName("MyFolder", NamespaceIndex), DisplayName = new LocalizedText("MyFolder"), TypeDefinitionId = ObjectTypeIds.FolderType, EventNotifier = EventNotifiers.None }; // 创建变量 myVariable = new BaseDataVariableState(myFolder) { SymbolicName = "MyVariable", NodeId = new NodeId("MyVariable", NamespaceIndex), BrowseName = new QualifiedName("MyVariable", NamespaceIndex), DisplayName = new LocalizedText("MyVariable"), DataType = DataTypeIds.Int32, ValueRank = ValueRanks.Scalar, AccessLevel = AccessLevels.CurrentReadOrWrite, UserAccessLevel = AccessLevels.CurrentReadOrWrite, Historizing = false, Value = 123, TypeDefinitionId = VariableTypeIds.BaseDataVariableType }; myFolder.AddChild(myVariable); } public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences) { base.CreateAddressSpace(externalReferences); AddPredefinedNode(SystemContext, myFolder); } }
可能的错误点及修复方案
1. 自定义节点未挂载到根对象文件夹
当前代码仅将myFolder添加到地址空间,但没有将其挂载到OPC UA服务器的根ObjectsFolder,导致客户端无法浏览到自定义节点。
修复:在MyNodeManager的CreateAddressSpace方法中添加根节点引用:
public override void CreateAddressSpace(IDictionary<NodeId, IList<IReference>> externalReferences) { base.CreateAddressSpace(externalReferences); AddPredefinedNode(SystemContext, myFolder); // 获取根ObjectsFolder并添加引用 var objectsFolder = FindPredefinedNode(ObjectIds.ObjectsFolder, typeof(FolderState)) as FolderState; if (objectsFolder != null) { // 建立从根文件夹到自定义文件夹的组织引用 objectsFolder.AddReference(ReferenceTypes.Organizes, true, myFolder.NodeId); // 建立反向引用 myFolder.AddReference(ReferenceTypes.Organizes, false, ObjectIds.ObjectsFolder); } }
2. 命名空间配置问题
MyNodeManager构造函数中使用Namespaces.OpcUa作为命名空间索引,这是OPC UA的标准命名空间。如果你的自定义节点属于自定义命名空间,需要先注册命名空间URI。
修复:在MySampleServer的CreateMasterNodeManager中添加自定义命名空间:
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) { // 注册自定义命名空间URI server.NamespaceUris.Add("http://your-company.com/MySampleServer"); int customNamespaceIndex = server.NamespaceUris.Count - 1; List<INodeManager> nodeManagers = new List<INodeManager> { new MyNodeManager(server, configuration, customNamespaceIndex) // 修改构造函数传入自定义索引 }; return new MasterNodeManager(server, configuration, null, nodeManagers.ToArray()); }
同时修改MyNodeManager的构造函数:
public MyNodeManager(IServerInternal server, ApplicationConfiguration config, int namespaceIndex) : base(server, config, namespaceIndex) { // ... 原有代码 ... }
3. 异步调用同步阻塞导致死锁
代码中使用.GetAwaiter().GetResult()和.Wait()同步阻塞异步方法,可能导致线程死锁,尤其是在有同步上下文的环境中(如WPF/WinForms)。
修复:将StartServer改为异步方法,使用await替代同步阻塞:
public async Task StartServer() { ApplicationInstance application = new ApplicationInstance(); application.ApplicationName = "MyServer"; application.ApplicationType = ApplicationType.Server; // ... 原有配置代码 ... // 异步验证配置 await config.Validate(ApplicationType.Server); if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates) { config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); }; } application.ApplicationConfiguration = config; // 异步检查证书 bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0); if (!haveAppCertificate) { throw new Exception("应用实例证书无效!"); } // 异步启动服务器 await application.Start(new MySampleServer()); Console.ReadLine(); }
4. 证书生成/加载异常
虽然设置了自动接受证书,但可能证书未正确生成或存储路径解析失败(%CommonApplicationData%在某些环境下可能无法自动解析)。
修复:
- 手动替换证书路径为绝对路径,比如
@"C:\ProgramData\OPC Foundation\CertificateStores\MachineDefault" - 强制生成证书,将
CheckApplicationInstanceCertificate的第一个参数改为true:
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(true, 2048);
5. 端口占用或地址配置问题
确保端口4840未被其他程序占用,或者尝试更换BaseAddresses中的端口,比如opc.tcp://localhost:4841/MyServer。
内容的提问来源于stack exchange,提问作者Josef
相关产品推荐
相关产品推荐

