news 2026/4/29 17:29:17

用C语言和open62541库,5分钟搞定一个OPC UA服务端与客户端(附完整源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用C语言和open62541库,5分钟搞定一个OPC UA服务端与客户端(附完整源码)

5分钟实战:用C语言和open62541构建OPC UA双向通信系统

在工业物联网项目中,快速验证通信协议可行性是开发初期的重要环节。今天我们将使用轻量级的open62541库,从零搭建一个完整的OPC UA通信演示系统。这个实战教程特别适合需要在嵌入式设备或工业控制系统中集成OPC UA功能的开发者。

1. 环境准备与库配置

1.1 开发环境搭建

首先确保系统已安装基础编译工具链。对于Linux系统(以Ubuntu为例),执行以下命令安装依赖:

sudo apt update sudo apt install -y build-essential cmake git

Windows用户需要安装MinGW或Visual Studio,并确保CMake已加入系统PATH。open62541支持跨平台编译,本文示例将同时涵盖两种操作系统下的配置差异。

1.2 open62541库获取

推荐使用v1.3稳定版本,通过Git克隆仓库:

git clone --branch 1.3 https://github.com/open62541/open62541.git cd open62541 git submodule update --init --recursive

编译安装库文件:

mkdir build && cd build cmake -DUA_ENABLE_AMALGAMATION=ON .. make -j$(nproc) sudo make install

关键编译选项说明:

选项作用推荐值
UA_ENABLE_AMALGAMATION生成单个合并文件ON
UA_ENABLE_SUBSCRIPTIONS启用订阅功能按需
UA_ENABLE_ENCRYPTION启用加密支持测试阶段OFF

提示:Windows用户使用MinGW时需添加-G "MinGW Makefiles"参数

2. 服务端开发实战

2.1 最小化服务端实现

创建server.c文件,实现一个包含两个传感器节点的服务端:

#include <open62541/server.h> #include <signal.h> UA_Boolean running = true; void signalHandler(int sig) { running = false; } UA_StatusCode addVariableNode(UA_Server *server, char* nodeIdStr, UA_Int32 initialValue) { UA_VariableAttributes attr = UA_VariableAttributes_default; attr.displayName = UA_LOCALIZEDTEXT("en-US", nodeIdStr); attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; UA_Variant_setScalar(&attr.value, &initialValue, &UA_TYPES[UA_TYPES_INT32]); UA_NodeId nodeId = UA_NODEID_STRING(1, nodeIdStr); UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, nodeIdStr); return UA_Server_addVariableNode(server, nodeId, parentNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), browseName, UA_NODEID_NULL, attr, NULL, NULL); } int main() { signal(SIGINT, signalHandler); UA_Server *server = UA_Server_new(); UA_ServerConfig_setDefault(UA_Server_getConfig(server)); // 添加示例节点 addVariableNode(server, "Temperature", 25); addVariableNode(server, "Pressure", 1013); UA_Server_run(server, &running); UA_Server_delete(server); return 0; }

2.2 编译与运行服务端

Linux下的编译命令:

gcc -o opcua_server server.c -lopen62541 -pthread

Windows(MinGW)编译需额外链接网络库:

gcc -o opcua_server server.c -lopen62541 -lws2_32 -liphlpapi

启动服务端后,默认监听端口为4840,可通过netstat -tulnp验证服务是否正常运行。

3. 客户端开发实战

3.1 客户端核心功能实现

创建client.c文件,实现节点读写和浏览功能:

#include <open62541/client.h> #include <stdio.h> void readNode(UA_Client *client, UA_NodeId nodeId) { UA_Variant value; UA_StatusCode retval = UA_Client_readValueAttribute(client, nodeId, &value); if(retval == UA_STATUSCODE_GOOD && value.type == &UA_TYPES[UA_TYPES_INT32]) { printf("Node Value: %d\n", *(UA_Int32*)value.data); } UA_Variant_clear(&value); } void writeNode(UA_Client *client, UA_NodeId nodeId, UA_Int32 newValue) { UA_Variant value; UA_Variant_setScalar(&value, &newValue, &UA_TYPES[UA_TYPES_INT32]); UA_StatusCode retval = UA_Client_writeValueAttribute(client, nodeId, &value); if(retval == UA_STATUSCODE_GOOD) { printf("Write successful\n"); } } void browseNodes(UA_Client *client, UA_NodeId nodeId) { UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.nodesToBrowse = UA_BrowseDescription_new(); bReq.nodesToBrowseSize = 1; bReq.nodesToBrowse[0].nodeId = nodeId; bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq); for(size_t i=0; i<bResp.resultsSize; ++i) { UA_ReferenceDescription *ref = &bResp.results[i].references[0]; printf("Found Node: %.*s\n", (int)ref->browseName.name.length, ref->browseName.name.data); } UA_BrowseResponse_clear(&bResp); } int main() { UA_Client *client = UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840"); if(retval != UA_STATUSCODE_GOOD) { UA_Client_delete(client); return retval; } // 示例操作 UA_NodeId tempNode = UA_NODEID_STRING(1, "Temperature"); readNode(client, tempNode); writeNode(client, tempNode, 30); browseNodes(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)); UA_Client_disconnect(client); UA_Client_delete(client); return 0; }

3.2 客户端编译与测试

编译命令与服务端类似,注意添加相同的链接库。测试时先确保服务端正在运行,然后执行客户端程序观察输出结果。

4. 高级功能扩展

4.1 多节点批量操作

改进客户端代码,实现高效的多节点批量读写:

void batchReadNodes(UA_Client *client, UA_NodeId *nodeIds, size_t count) { UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = (UA_ReadValueId*)UA_Array_new(count, &UA_TYPES[UA_TYPES_READVALUEID]); request.nodesToReadSize = count; for(size_t i=0; i<count; i++) { request.nodesToRead[i].nodeId = nodeIds[i]; request.nodesToRead[i].attributeId = UA_ATTRIBUTEID_VALUE; } UA_ReadResponse response = UA_Client_Service_read(client, request); for(size_t i=0; i<response.resultsSize; i++) { if(response.results[i].hasValue && response.results[i].value.type == &UA_TYPES[UA_TYPES_INT32]) { printf("Node[%zu] Value: %d\n", i, *(UA_Int32*)response.results[i].value.data); } } UA_ReadResponse_clear(&response); UA_Array_delete(request.nodesToRead, count, &UA_TYPES[UA_TYPES_READVALUEID]); }

4.2 异常处理与日志记录

增强系统健壮性的关键处理逻辑:

UA_Logger logger = { .log = [](UA_Logger *logger, UA_LogLevel level, UA_LogCategory category, const char *msg, va_list args) { vprintf(msg, args); printf("\n"); } }; // 在客户端初始化时配置 UA_ClientConfig *config = UA_Client_getConfig(client); config->logger = logger; // 典型错误处理模式 UA_StatusCode retval = some_operation(); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_CLIENT, "Operation failed: %s", UA_StatusCode_name(retval)); // 恢复或重试逻辑 }

5. 工程化建议

5.1 CMake工程组织

推荐的项目结构:

opcua_demo/ ├── CMakeLists.txt ├── client/ │ ├── client.c │ └── CMakeLists.txt └── server/ ├── server.c └── CMakeLists.txt

顶层CMakeLists.txt示例:

cmake_minimum_required(VERSION 3.5) project(opcua_demo LANGUAGES C) find_package(open62541 REQUIRED) add_subdirectory(server) add_subdirectory(client)

服务端子项目配置:

add_executable(opcua_server server.c) target_link_libraries(opcua_server open62541::open62541) if(UNIX) target_link_libraries(opcua_server pthread) endif()

5.2 跨平台注意事项

不同平台的差异处理:

  • 网络超时设置

    #ifdef _WIN32 config->tcpConnectTimeout = 5000; // Windows下毫秒 #else config->tcpConnectTimeout = 5; // Linux下秒 #endif
  • 线程安全

    UA_ServerConfig config = UA_ServerConfig_standard; config.verification.allowMultiThreading = UA_TRUE;

在实际项目中,建议将这些差异封装到平台适配层中。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 17:24:41

终极模组管理方案:XXMI启动器一站式管理六大热门二次元游戏

终极模组管理方案&#xff1a;XXMI启动器一站式管理六大热门二次元游戏 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 你是否厌倦了为每个游戏单独配置模组的繁琐过程&#xff…

作者头像 李华
网站建设 2026/4/29 17:20:06

如何快速搭建跨平台Iwara客户端:完整Flutter开发指南

如何快速搭建跨平台Iwara客户端&#xff1a;完整Flutter开发指南 【免费下载链接】iwrqk Unofficial Iwara Flutter Client 项目地址: https://gitcode.com/gh_mirrors/iw/iwrqk 还在为手机端浏览Iwara社区而烦恼吗&#xff1f;官方移动端体验不佳&#xff0c;加载缓慢&…

作者头像 李华
网站建设 2026/4/29 17:15:24

RIR-Generator:如何用图像法生成高精度房间脉冲响应?

RIR-Generator&#xff1a;如何用图像法生成高精度房间脉冲响应&#xff1f; 【免费下载链接】RIR-Generator Generating room impulse responses 项目地址: https://gitcode.com/gh_mirrors/ri/RIR-Generator 在音频信号处理领域&#xff0c;房间脉冲响应&#xff08;R…

作者头像 李华