简介面向工业自动化与上位机开发者的OPC UA通信实战源码包聚焦PLC与PC机之间的数据交互完整展示了基于OPC UA开放标准的服务器、客户端、信息模型与安全机制可解决不同厂商设备互联与数据交换的难题。压缩包共285个文件整体仅926KB以C/C源代码.c/.h/.cc/.cpp为主辅以Python脚本、CMake构建配置、Shell部署脚本、Markdown/RST说明文档及Dockerfile便于在Windows、Linux等系统及容器环境中编译运行整体结构清晰适合按模块查阅。该压缩包目前已有265人浏览学习。通过实例代码可理解OPC UA的身份验证与授权、节点数据模型的层次化组织、订阅推送机制以及二进制编码解码流程资源自带服务器端与客户端实现、配置文件和示例应用有助于快速搭建联调环境并为实际项目中的协议对接与二次开发提供直接参考。1. 为什么说 OPC UA 是 PLC 与 PC 通信的“后悔药”做过 PLC 和上位机通信的人都有这种经历PLC 厂商各有一套私有协议三菱用 MC 协议西门子用 S7 协议欧姆龙用 FINS协议之间不通用。以前项目上要接三台不同品牌的 PLC就得写三套驱动每套驱动还得处理端口、报文格式、字节序这些细节调试起来非常痛苦。OPC UA 出现之后这个问题算是被从根上解决了PLC 和 PC 之间通过统一的信息模型和地址空间通信上位机只跟 OPC UA 服务器打交道不用关心对面是哪个品牌的 PLC。这篇内容围绕“OPC UA 通信用于 PLC 与 PC 机通信实例源码”展开适合两类人一类是刚接触 PLC 上位机开发的工程师想搞明白怎么用 C# 快速读写出 PLC 数据另一类是已经在用 OPC DA 或者 Modbus TCP 的熟手想知道换到 OPC UA 之后证书、密码、安全策略这些参数到底怎么配。全文会从协议选型讲到可运行的代码再落到几个最让人想翻车的报错上照着做基本能跑通一条完整的链路。2. OPC UA 通信前必须想清楚的事协议栈、安全策略和选型2.1 OPC UA 和传统通信方案的差异在哪老一代的 OPC DA 基于 Windows 的 COM/DCOM 技术最典型的问题就是 DCOM 配置极其难搞。跨防火墙、跨域、换一台电脑都要重新配置权限而且只能在 Windows 上用。OPC UA 重新设计了通信层不依赖 DCOM底层可以跑在 TCP 上也可以走 HTTP 和 HTTPS跨平台能力完全是另一个量级。更重要的是OPC UA 不只是一个传输协议它把数据模型也标准化了服务器端把数据组织成一个地址空间客户端通过浏览节点就能找到设备的数据而不是像 Modbus 那样靠寄存器地址硬记。从 PLC 通信的角度看OPC UA 带来的实际好处主要有这几个地址空间自描述客户端可以浏览节点不需要提前约定一张寄存器表。数据带类型和时间戳读出来的数据是带语义的不是裸字节。支持订阅机制数据变化时服务器主动推送不用高频轮询。安全机制内置支持匿名、用户名密码、证书三种认证方式。这些特性对做设备数据采集、MES 对接、跨车间组网非常有利。很多 PLC 厂商比如西门子 S7-1500、S7-1200三菱的 FX5U 和 iQ-F 系列还有 CodeSys 内核的 PLC 和软 PLC已经把 OPC UA 服务器直接做进了固件。也就是说PLC 通电后启用 OPC UA 功能PC 端不需要单独装驱动直接用 OPC UA 客户端就能连上。2.2 安全策略要怎么选匿名、用户名密码还是证书OPC UA 的安全设置是新手最容易犯迷糊的地方。服务器端和客户端之间需要协商安全策略Security Policy和安全模式Security Mode。常见的安全策略有三种None、Basic256Sha256 和 Basic256。安全模式也有三种None、Sign、SignAndEncrypt。常见的选型思路是这样的车间局域网内调试数据不敏感图省事就用 None连接速度快配置最少。有跨区域或者数据敏感需求时至少用 Basic256Sha256 和用户名密码认证。如果公司安全规范要求双向认证那就要配置证书客户端和服务器互相导入信任证书。需要特别注意的是如果服务器配置了“必须使用加密连接”而客户端用匿名加 None 去连服务器会直接拒绝并返回 Bad_SecurityModeInsufficient 一类的错误。反过来如果服务器是 None 策略客户端却强制要求加密也会连不上。两边参数必须对齐。2.3 C# 方案选型官方库还是写好的 Helper 类.NET 平台上最常用的 OPC UA 开发库是 OPC Foundation 官方的 OPC UA .NET Standard 库GitHub 上的 opcfoundation/UA-.NETStandard 就是它。用这个库开发的好处是功能完整、和官方规范同步坏处是接口层级偏底层第一次接触的人面对几百个类会不知道该从哪里入手。实际做项目时很多工程师会选择用别人封装好的 Helper 类比如 OPCUAHelper。这类封装通常把连接、读节点、写节点、订阅这几个高频操作收拢成简单的方法代码量少很多特别适合快速出原型。搜“c# opcuahelper 读取opcua数据”能看到的案例基本都是这个思路。不过要提醒一句Helper 类一般只覆盖常见场景遇到证书异常、安全策略不匹配这些问题还是得回到底层库去查日志和参数。我在项目里的做法是原型阶段用 Helper 类验证通信链路确认节点能读到数据之后再根据项目需求决定要不要换官方库重写。如果只是做数据采集和展示Helper 完全够用如果要做复杂的订阅、历史数据读取或者自定义信息模型那还是直接用官方库更稳妥。3. 搭建 PLC 与 PC 的 OPC UA 通信环境PLC 端和 PC 端的配置步骤3.1 PLC 端开启 OPC UA 服务器功能不同品牌的 PLC 开启 OPC UA 的方式不一样但思路是共通的找到 PLC 设置里的“OPC UA”选项启用服务器功能设置好端口号必要时配置用户账号和密码。以西门子 S7-1200/S7-1500 为例在 TIA Portal 里选中 CPU进入“设备视图 - 属性 - OPC UA”勾选“激活 OPC UA”默认端口是 4840。如果 PLC 程序运行在虚地址空间里还要注意设置“服务器接口”参数否则客户端可能浏览不到数据节点。三菱 FX5U 的做法也类似在 GX Works3 里启用 CPU 的内置 OPC UA 服务器功能设置端口后PLC 固件会自动把软元件映射为 OPC UA 节点变量。这类设置完成后需要用 PLC 编程软件把配置下载到 PLC 并重启确保 OPC UA 服务真正启动。S7-1200 CPU 属性中的 OPC UA 关键参数 - 激活 OPC UA勾选 - 端口号4840 - 服务器接口选择 PLC 程序所在的虚地址空间 - 安全策略按需选择调试期可先选 None - 认证方式匿名 / 用户名密码 / 证书参数说明端口号默认 4840这是 OPC UA 的标准端口PC 端连接时填的地址就是opc.tcp://PLC的IP地址:4840。服务器接口这个参数容易被忽略如果 PLC 程序里有多个工业以太网接口一定要确认选了正确的那一个否则客户端连上了但看不到数据。设置完成后可以在 PC 上用 OPC UA 客户端工具后面会提到 UA Expert测试连接。如果连接被拒绝优先检查 PLC 端的“激活 OPC UA”是否真的生效以及 PLC 固件版本是否支持 OPC UA 功能老版本固件可能需要先升级。3.2 PC 端用 KEPServerEX 做 OPC UA 网关如果 PLC 本身不带 OPC UA 服务器或者品牌太老不支持常见的做法是在 PC 端装一个 OPC UA 网关软件把 PLC 的私有协议转换成 OPC UA。KEPServerEX 是这类软件里用得比较多的一个配置思路是所有网关软件的通用模板。KEPServerEX 配置 OPC UA 的大致步骤安装 KEPServerEX添加驱动通道通道类型选对应的 PLC 驱动比如 Siemens TCP/IP Ethernet 或者 Mitsubishi FX。在通道下创建设备填写 PLC 的 IP 地址、端口号、CPU 类型这些参数。在设备下创建标签Tag标签地址对应 PLC 的数据区比如 DB 块地址、M 寄存器地址。确认 KEPServerEX 的 OPC UA 服务已经启动默认监听端口 49320。# 查看 KEPServerEX 网关是否在监听 OPC UA 端口 netstat -ano | findstr 49320逻辑说明和参数说明端口 49320 是 KEPServerEX 默认的 OPC UA 端口netstat用来确认服务没有意外停止。如果端口没在监听去 Windows 服务里检查 KEPServerEX 相关的服务状态。设备地址格式和 PLC 的编程软件里的地址对应比如三菱 PLC 的 D100 在 KEPServerEX 里可能写成D100或M100具体看驱动文档。这里有个容易踩的坑KEPServerEX 的标签名称和 OPC UA 节点 ID 不是一回事。PLC 数据映射到 OPC UA 地址空间后节点 ID 通常是由 KEPServerEX 生成的一串路径客户端读取时要通过浏览节点来找实际路径不能指望标签名直接变成可用节点 ID。3.3 PC 端证书信任配置OPC UA 通信如果启用了证书认证PC 端第一次连接服务器时客户端会生成一个应用实例证书并要求服务器端信任这个证书。反过来服务器也会有自己的证书客户端需要决定是否信任它。这个环节在整个通信调试里最容易卡住因为很多报错信息看起来像是“连不上”实际是“证书不信任”。在 Windows 环境下OPC UA 客户端的证书通常会被放到用户或系统的证书存储区里应用证书的目录一般类似C:\Users\用户名\AppData\OA\OPC Foundation\CertificateStores\MachineDefault\certs服务器证书和客户端证书要交叉导入“信任列表”。有些人会直接把证书复制到对方的 Trusted 目录里省去交互确认的步骤。这里强调一下证书过期、证书没有私钥、证书的 SAN主题备用名称里没有匹配的 IP 或主机名都会导致连接失败。报错信息五花八门但日志里基本都会有一个异常链指向证书问题。4. C# OPCUAHelper 连接需要密码的服务器从连接、读到订阅4.1 引入 OPC UA 客户端库并初始化连接参数C# 项目里连接 OPC UA 服务器最常见的做法是创建一个控制台应用或者 WinForm 应用然后通过 NuGet 安装 OPC Foundation 的客户端库。包名是OPCFoundation.NetStandard.Opc.Ua.Client和OPCFoundation.NetStandard.Opc.Ua.Core安装后会自动把依赖的 OPC UA 核心库拉进来。# 在项目目录下执行 dotnet add package OPCFoundation.NetStandard.Opc.Ua.Client dotnet add package OPCFoundation.NetStandard.Opc.Ua.Core参数说明这两个包是最常用的 OPC UA .NET Standard 客户端组件Unity 和配置包一般不需要单独安装因为依赖会被自动解析。安装完成后再看一下项目目标框架建议用 .NET 6 及以上老版本 .NET Framework 4.6.1 虽然也能跑但在证书处理方面会有一些额外限制。初始化代码第一步是创建配置对象这一步做的事情包括配置应用名称、证书目录和安全策略列表using Opc.Ua; var config new ApplicationConfiguration { ApplicationName PLC_PC_Client, ApplicationType ApplicationType.Client, SecurityConfiguration new SecurityConfiguration { ApplicationCertificate new CertificateIdentifier { StoreType Directory, StorePath C:\OPCUA\pki\own, SubjectName CNPLC_PC_Client }, TrustedPeerCertificates new CertificateTrustList { StoreType Directory, StorePath C:\OPCUA\pki\trusted }, RejectedCertificateStore new CertificateTrustList { StoreType Directory, StorePath C:\OPCUA\pki\rejected } }, TransportConfigurations new TransportConfigurationCollection(), TransportQuotas new TransportQuotas { OperationTimeout 10000 }, ClientConfiguration new ClientConfiguration { DefaultSessionTimeout 60000 } }; await config.Validate(ApplicationType.Client);逻辑说明和参数说明这段配置的核心是证书路径。StorePath目录会在首次运行时由 SDK 自动创建SubjectName对应客户端证书的主题如果服务器要求验证客户端证书这个主题名会被记录在服务器的证书列表里。trusted目录用来放服务器信任的证书rejected目录则放着握手失败时被拒绝的证书排错时可以通过看 rejected 目录里的文件来判断是不是证书信任问题。OperationTimeout的单位是毫秒设置太短可能导致大数据读取超时局域网内一般 10 秒够用。4.2 使用用户名密码建立会话连接需要密码的 OPC UA 服务器服务器端启用用户名密码认证之后客户端不能光靠匿名连接了必须把用户名密码带在会话创建请求里。OPC UA 的用户身份令牌UserIdentityToken有几种类型最常见的两种是UserNameIdentityToken和AnonymousIdentityToken。当服务器配置了“需密码”时客户端创建会话时必须显式提供UserNameIdentityToken。using Opc.Ua; using Opc.Ua.Client; var certificate await config.SecurityConfiguration.ApplicationCertificate.Load(); if (certificate null) { certificate await CertificateFactory.CreateCertificate(config, 2048); await config.SecurityConfiguration.ApplicationCertificate.Save(certificate); } config.ApplicationCertificate.Certificate certificate; var endpoint CoreClientUtils.SelectEndpoint(opc.tcp://192.168.1.10:4840, useSecurity: true); var endpointDescription new EndpointDescription { EndpointUrl endpoint.EndpointUrl, Server endpoint.Server, SecurityMode MessageSecurityMode.SignAndEncrypt, SecurityPolicyUri SecurityPolicies.Basic256Sha256 }; var session await Session.Create( config, new ConfiguredEndpoint(null, endpointDescription), false, PLC_Session, 10000, new UserIdentity(new UserNameIdentityToken { UserName admin, Password plc12345 }), null);逻辑说明和参数说明这里SelectEndpoint的作用是从服务器的 endpoint 列表里挑一个符合要求的安全策略。如果服务器只支持Basic256Sha256而客户端代码里写死了SecurityPolicies.None握手就会失败。useSecurity: true表示要求加密和签名服务器要是没启用安全连接这行会直接报异常。UserIdentity里封装了用户名密码令牌密码在实际项目里不要写死在代码里可以从配置文件读取或者用加密的密钥管理方案。连接成功后如果只是想读一个节点的值简单的方式是调用ReadValueAsyncvar nodeId new NodeId(ns4;sProgram:Data.D100); var value await session.ReadValueAsync(nodeId); Console.WriteLine($D100 当前值: {value});4.3 读取 OPC UA 数据和写 PLC 点位读取单个节点只是最基础的用法实际场景里经常需要一次读一批节点。OPC UA 的ReadRequest支持在同一个请求里携带多个节点 ID服务端会一次性返回所有节点的值这样就避免了逐个读取带来的多次网络往返。var nodesToRead new ReadValueIdCollection { new ReadValueId { NodeId new NodeId(ns4;sProgram:Data.D100), AttributeId Attributes.Value }, new ReadValueId { NodeId new NodeId(ns4;sProgram:Data.D101), AttributeId Attributes.Value }, new ReadValueId { NodeId new NodeId(ns4;sProgram:Data.M0), AttributeId Attributes.Value } }; var request new ReadRequest { NodesToRead nodesToRead, TimestampsToReturn TimestampsToReturn.Both }; var response await session.ReadAsync(request); foreach (var result in response.Results) { Console.WriteLine($节点状态: {result.StatusCode}, 值: {result.Value}); }逻辑说明和参数说明TimestampsToReturn.Both表示同时返回源时间戳和服务器时间戳做数据溯源时会用到。每个节点的返回结果都带一个StatusCode如果节点路径写错或者节点没有访问权限StatusCode会返回BadNodeIdUnknown或BadNotYetExisting。批量读取时建议把节点数量控制在几百个以内一次请求节点太多会触发服务器的请求大小限制。写操作和读操作结构差不多但要注意写 PLC 点位是有权限控制的。如果服务器只给当前用户分配了读权限执行WriteAsync时会返回BadUserAccessDenied。另外PLC 的数据类型必须和 OPC UA 变量的数据类型匹配比如把float值写到一个Int16节点上服务器会返回BadTypeMismatch。var writeValue new WriteValue { NodeId new NodeId(ns4;sProgram:Data.D200), AttributeId Attributes.Value, Value new DataValue(new Variant((short)1234)) }; var writeResponse await session.WriteAsync(new WriteRequest { NodesToWrite new WriteValueCollection { writeValue } }); Console.WriteLine($写入结果: {writeResponse.Results[0].StatusCode});4.4 数据订阅让服务器主动推送 PLC 变量变化轮询模式的缺点是实时性差、占用带宽而且当 PLC 的扫描周期远小于轮询周期时中间的变化很可能会丢。OPC UA 订阅Subscription机制就是为这个场景设计的客户端创建订阅后服务器检测到数据变化会按设定的发布间隔把数据推送过来。var subscription new Subscription(session) { PublishingInterval 500, KeepAliveCount 10, LifetimeCount 100, MaxNotificationsPerPublish 1000 }; session.AddSubscription(subscription); subscription.Create(); var monitoredItem new MonitoredItem { StartNodeId new NodeId(ns4;sProgram:Data.D100), SamplingInterval 200, QueueSize 10, DiscardOldest true }; monitoredItem.Notification (sender, e) { foreach (var value in e.NotificationValue.MonitoredItems) { Console.WriteLine($数据变化: {value.Value}); } }; subscription.AddItem(monitoredItem); subscription.ApplyChanges();逻辑说明和参数说明PublishingInterval是订阅的发布周期单位是毫秒这里的 500 表示服务器每 500 毫秒向客户端推送一次数据。SamplingInterval是服务器采样底层数据的时间间隔最好小于发布周期否则数据变化可能被采样周期漏掉。QueueSize是数据变化队列长度当发布延迟时队列可以帮助缓存后续变化设为 0 会默认用服务器端的最小值。KeepAliveCount和LifetimeCount影响断线监听的灵敏度KeepAlive 超时后会话还没恢复订阅就会被服务端回收。订阅通知里拿到的MonitoredItems自带ClientHandle属性可以通过它判断这条通知对应的是哪个监控节点不用再逐条反查节点 ID。项目里监控几十个点位时建议把节点 ID 和ClientHandle的关系维护在一个字典里回调处理会清爽很多。5. 常见问题排查证书、安全模式和地址空间的坑5.1 现象报错 “ApplicationCertificate cannot be found.”连接直接失败原因这个错误看起来是“找不到应用证书”本质上是 SDK 在配置里指定的证书目录里没有找到有效证书又不能自动生成新证书于是客户端直接退出了握手。通常发生在第一次运行客户端、StorePath目录为空或者运行账号没有对该目录的写权限时。解决先检查StorePath目录是否存在且目录名正确。SDK 一般会在首次运行配置时自动生成证书但如果你把SubjectName配置错误或者Load()没有触发创建逻辑就会出现这个报错。我的习惯是显式做一次证书初始化在调用Load之后如果返回 null就调用CertificateFactory.CreateCertificate生成一个然后保存到 pki/own 目录。生成之后去目录里确认多了两个文件一个证书文件、一个私钥文件。如果文件在但程序还是报错就需要给运行用户开启目录的读写权限。另外ApplicationConfiguration.Validate会校验证书信息证书过期也会在后续连接时被服务器拒绝出现 BadCertificateTimeInvalid。5.2 现象用户密码正确但服务器返回 BadSecurityModeInsufficient原因用户名密码是用来做身份认证的而安全模式的作用是保护通信内容。服务器端如果强制安全模式为 SignAndEncrypt而客户端配置里的SecurityMode是 None 或者 Sign服务器就会拒绝因为两者协商不到同一个安全级别。很多案例里用户密码没问题报错却出现在安全策略这一步容易被误判成账号密码错误。解决先确认服务器端支持哪些安全策略组合再用客户端显式指定同一个策略。调试时可以用 UA Expert 这类工具连接服务器在连接界面里会列出所有可用的 endpoint 和对应的安全模式。代码里不要偷懒省掉useSecurity参数建议把SecurityMode和SecurityPolicyUri都写成明确的常量而不是用默认值。另外要检查服务器证书上的 SAN 字段里是否含 PLC 或 PC 的 IP如果证书只签了主机名你用 IP 去连也可能出现安全策略协商失败。5.3 现象订阅建立后长时间收不到数据断开重连又能收到一次原因这是典型的订阅参数配置不当。当PublishingInterval大于SamplingInterval时数据变化虽然被采样了但要等到下一个发布周期才推送。更隐蔽的问题是QueueSize太小而DiscardOldest为 false当新数据到达且队列已满时新数据会被丢弃造成持续漏报。解决调小SamplingInterval保证采样比发布快。PublishingInterval按 PLC 项目的实时性要求来定一般取 100500 毫秒足够。QueueSize设置成大于发布周期内可能产生的变化次数比如 10 或 20。如果 PLC 点位变化极快比如高速计数值就不要再依赖订阅推送了建议在 PLC 内部做累计后再给 OPC UA 服务器或者用轮询把变化拉回来。还有一种情况是会话断开了OPC UA 断线后订阅会自动清掉重连后必须重新创建订阅代码里要做好断线重连并重建订阅的逻辑。5.4 现象使用 OPCUAHelper 2.2.1 连接加密 OPC UA 服务器时证书被服务器拒绝日志显示 BadCertificateUntrusted原因服务器端不信任客户端提供的证书。OPC UA 的证书信任是双向的服务器有自己的受信证书列表客户端证书不在这个列表里就会被丢弃。使用 Helper 类时很多人忽略了证书导入这一步只填了服务器地址和密码结果就卡在证书握手环节。解决把客户端第一次运行时生成的应用证书导出导入到服务器的受信证书列表里。具体路径要看 OPC UA 服务器端实现了哪些受信目录有些服务器是 .pem 文件形式直接放到受信任文件夹。KEPServerEX 的话在管理界面里有一个“Trusted Server Certificates”列表把客户端证书加进去。反过来如果服务器证书不被客户端信任客户端会收到 BadCertificateUntrusted 或 BadCertificateChainIncomplete需要在客户端受信列表里添加服务器证书。用 Helper 类时注意看 Helper 的配置项里是否有CertificateStorePath和TrustedServerStorePath这两个参数留空会导致 SDK 使用默认路径容易出现“代码没问题但证书对不上”的情况。5.5 现象使用 KEPServerEX 做网关时OPC UA 客户端能看到通道和标签但读不到数据返回 BadWaitingForInitialData原因KEPServerEX 或者类似网关软件中的标签还没有收到 PLC 的实际数据。常见原因有三种PLC 类型和地址配置错误标签地址超出了 PLC 数据区范围或者 PLC 的通信参数如波特率、数据位、停止位不匹配。解决先到 KEPServerEX 的“Quick Client”工具中查看标签状态如果标签状态列显示错误码直接看错误码含义。重点排查设备地址的格式比如西门子 DB 块地址要写成 DB1.DBD10 而不是 DBD10三菱 FX 系列软元件要区分 D 和 M 是不同数据区。确认地址无误后在 KEPServerEX 里手动写入一个测试值如果写入成功但读取还是 WaitingForInitialData就确认 PLC 侧是否配置了通信允许时间区域有些 PLC 默认禁止外部写入。最后再检查 OPC UA 客户端浏览到的节点路径是否跟标签一一对应因为网关软件生成的节点路径里通常会包含通道名和设备名。6. 进阶用法用 UA Expert 验证链路再把重连和安全做成基础设施调试 OPC UA 项目时我习惯在写代码之前先用 UA Expert 把链路验证一遍。UA Expert 是 OPC Foundation 出的免费调试工具能连接服务器、浏览地址空间、读取节点、建立订阅界面直观。把服务器地址填进去选择安全策略和安全模式如果 UA Expert 能正常读写那问题就基本定位在客户端代码这边。这个工具还能导出地址空间的节点结构方便你对照着写节点 ID不猜不蒙。代码层面项目上线后最值得做的两件事是断线重连和日志采集。PLC 经常会因为断电、网络抖动而断线如果没有重连机制PC 端程序只能重启。OPC UA 客户端重连的常规做法是监听SessionReconnectHandler在收到KeepAlive异常后尝试重新调用Session.Reconnect或者重新创建会话。重连之后要检查订阅状态如果订阅已经失效需要按原来的监控配置重新创建订阅。日志方面OPC UA SDK 自带的Utils.Tracing可以记录明细日志建议把日志级别设为Trace至少保留一周的日志量。碰到难查的证书问题时这份日志就是救命稻草里面会明确标记是证书不受信、证书过期还是安全策略不匹配。最后分享一个自己的习惯所有参数包括服务器地址、端口、用户名、密码、证书路径一律放到配置文件里代码里不写一个硬编码的 IP。这样设备 IP 变了、密码轮换了不用重新编译程序。OPC UA 这套技术本身不难难的是参数杂、证书坑多把配置和日志这两个基础打牢后续维护成本能降一大半。希望帮到你。本文还有配套的精品资源点击获取