TinyUPnP:嵌入式设备轻量级UPnP端口映射实现

news2026/4/1 0:21:57
1. TinyUPnP面向嵌入式平台的轻量级UPnP IGD客户端实现TinyUPnP 是一个专为资源受限嵌入式系统设计的极简 UPnPUniversal Plug and PlayInternet Gateway DeviceIGD客户端库核心目标是在无用户干预前提下自动向家庭/企业路由器申请端口映射Port Mapping即通常所称的“UPnP端口转发”。该库并非通用UPnP协议栈而是聚焦于 IGD v2 规范中WANPPPConnection:1服务的AddPortMapping操作以最小代码体积典型编译后仅占用数KB Flash换取在 ESP8266 和 ESP32 平台上的可靠运行能力。其设计哲学是“够用即止”——不追求协议全兼容而是在主流家用路由器如华硕、TP-Link、Netgear、小米等支持 UPnP 的型号上实现高成功率的端口自动开通为物联网设备提供对外服务如Web服务器、MQTT Broker、视频流所需的网络穿透能力。1.1 工程定位与适用场景在嵌入式开发实践中设备常需暴露本地服务至公网传统方案依赖用户手动登录路由器后台配置端口转发规则这对非技术用户构成巨大障碍也违背了IoT设备“开箱即用”的设计原则。TinyUPnP 正是为此类痛点而生其典型应用场景包括智能硬件网关作为家庭自动化中心需将内部Home Assistant或Node-RED实例的80/443端口映射至公网供手机App远程访问。工业数据采集终端将Modbus TCP或OPC UA服务端口如502、4840映射出去便于云平台主动拉取数据。音视频流媒体设备为RTSP554、WebRTC信令3478等端口自动申请映射实现低延迟远程监控。P2P通信辅助节点在NAT穿越STUN/TURN失败时作为备用方案动态开通端口提升连接成功率。与通用UPnP库如libupnp、miniupnpc相比TinyUPnP 的工程价值在于其确定性它不尝试解析完整的UPnP设备描述XML而是基于对WANPPPConnection:1服务的硬编码路径假设直接构造SOAP请求。这牺牲了协议严谨性却换来在目标平台上的极致稳定性和可预测性——当你的固件需要在10万台设备上零故障运行时这种“有限但可靠”的设计远比“全面但脆弱”的方案更具工程价值。2. 协议原理与工作流程深度解析TinyUPnP 的运作严格遵循 UPnP IGD v2 规范 UPnP-arch-DeviceArchitecture-v2.0.pdf 其核心流程可分解为四个原子阶段每个阶段均对应明确的网络行为和状态机转换。2.1 阶段一SSDP发现Simple Service Discovery ProtocolTinyUPnP 启动后首先向 SSDP 多播地址239.255.255.250:1900发送M-SEARCH请求该请求包含关键头字段M-SEARCH * HTTP/1.1 HOST: 239.255.255.250:1900 MAN: ssdp:discover MX: 3 ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1其中STSearch Target字段指明搜索目标为 IGD 设备。路由器收到此请求后若启用了UPnP将在MXMaximum Wait指定的超时窗口内通常1-3秒单播回复一个HTTP/1.1 200 OK响应其关键头为LOCATION: http://192.168.1.1:5000/rootDesc.xml SERVER: Linux/3.14.29, UPnP/1.0, Portable SDK for UPnP devices/1.6.19 ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1 USN: uuid:upnp-InternetGatewayDevice-1234567890::urn:schemas-upnp-org:device:InternetGatewayDevice:1LOCATION头指向的 XML 文件rootDesc.xml即为IGD的根设备描述文档这是整个流程的起点。2.2 阶段二设备描述解析Root Description ParsingTinyUPnP 通过 HTTP GET 下载LOCATION指定的 XML 文件。该文件结构遵循 UPnP 设备描述规范其关键部分为device节点下的serviceList其中必然包含WANPPPConnection:1服务的定义service serviceTypeurn:schemas-upnp-org:service:WANPPPConnection:1/serviceType serviceIdurn:upnp-org:serviceId:WANPPPConn1/serviceId SCPDURL/WANPPPConnection.xml/SCPDURL controlURL/ctl/PPPConn/controlURL eventSubURL/evt/PPPConn/eventSubURL /serviceTinyUPnP 仅提取controlURL控制URL和SCPDURL服务描述URL。controlURL是后续 SOAP 请求的终点而SCPDURL理论上应被下载以获取AddPortMapping操作的精确参数定义但 TinyUPnP 采用工程化简化策略——跳过SCPDURL解析直接硬编码构造符合主流路由器预期的SOAP Body。这一决策的依据是绝大多数商用路由器对WANPPPConnection:1#AddPortMapping的参数签名高度一致硬编码可规避XML解析带来的内存开销和潜在解析错误。2.3 阶段三SOAP请求构造与发送TinyUPnP 使用controlURL构造最终的 HTTP POST 请求。请求头与正文严格遵循 SOAP 1.1 规范POST /ctl/PPPConn HTTP/1.1 Host: 192.168.1.1 Content-Type: text/xml; charsetutf-8 SOAPAction: urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping Content-Length: [body_length]SOAP Body 的结构如下已格式化以便阅读?xml version1.0? s:Envelope xmlns:shttp://schemas.xmlsoap.org/soap/envelope/ s:encodingStylehttp://schemas.xmlsoap.org/soap/encoding/ s:Body u:AddPortMapping xmlns:uurn:schemas-upnp-org:service:WANPPPConnection:1 NewRemoteHost/NewRemoteHost NewExternalPort8080/NewExternalPort NewProtocolTCP/NewProtocol NewInternalPort80/NewInternalPort NewInternalClient192.168.1.100/NewInternalClient NewEnabled1/NewEnabled NewPortMappingDescriptionWebServer/NewPortMappingDescription NewLeaseDuration0/NewLeaseDuration /u:AddPortMapping /s:Body /s:Envelope各参数含义及工程考量参数示例值说明工程要点NewRemoteHost空字符串指定外部主机IP为空表示允许所有IP访问空字符串是通配符符合安全基线NewExternalPort8080路由器WAN口开放的端口号必须为整数范围通常1-65535NewProtocolTCP协议类型TCP或UDP区分大小写必须全大写NewInternalPort80设备本地服务监听端口可与外部端口不同实现端口重映射NewInternalClient192.168.1.100设备在LAN内的IPv4地址必须为字符串格式TinyUPnP 提供ipAddressToString()辅助函数NewEnabled1规则启用状态1为启用0为禁用硬编码为1确保规则生效NewPortMappingDescriptionWebServer规则描述显示在路由器管理界面最大长度受路由器限制建议≤32字符NewLeaseDuration0租约时长秒0表示永久有效主流路由器支持0避免定时续租复杂度2.4 阶段四响应处理与状态反馈路由器返回的 SOAP 响应为标准 XML 格式?xml version1.0? s:Envelope xmlns:shttp://schemas.xmlsoap.org/soap/envelope/ s:encodingStylehttp://schemas.xmlsoap.org/soap/encoding/ s:Body u:AddPortMappingResponse xmlns:uurn:schemas-upnp-org:service:WANPPPConnection:1/ /s:Body /s:EnvelopeTinyUPnP 仅需检测响应状态码是否为200 OK且响应体中存在u:AddPortMappingResponse标签即可判定操作成功。失败情况如404、500、超时会触发重试逻辑。整个流程的时序图如下简化版[Device] --(M-SEARCH)-- [Router Multicast] [Router] --(200 OK LOCATION)-- [Device] [Device] --(GET rootDesc.xml)-- [Router] [Router] --(rootDesc.xml)-- [Device] [Device] --(POST SOAP to controlURL)-- [Router] [Router] --(200 OK SOAP Response)-- [Device]3. API接口详解与工程化使用指南TinyUPnP 的API设计极度精简仅暴露5个核心成员函数全部围绕端口映射的生命周期管理。其头文件TinyUPnP.h中的关键声明如下3.1 构造函数与初始化// 构造函数timeoutMs 为SSDP发现阶段的最大等待时间毫秒 // -1 表示阻塞等待但强烈建议使用有限超时如20000ms以避免死锁 TinyUPnP(int timeoutMs 20000);工程实践建议在setup()中创建实例时务必传入合理超时值。ESP32/ESP8266 在Wi-Fi连接不稳定时SSDP发现可能无限期挂起导致setup()无法完成。20秒超时是平衡成功率与响应性的经验值。3.2 端口映射配置// 添加一条端口映射规则到本地配置列表 // ruleIP: 设备在局域网内的IP地址WiFi.localIP() // rulePort: 内部服务监听端口如80 // ruleProtocol: 协议类型TCP 或 UDP字符串字面量 // ruleLeaseDuration: 租约时长秒0表示永久 // ruleFriendlyName: 规则描述用于路由器管理界面识别 void addPortMappingConfig(IPAddress ruleIP, uint16_t rulePort, const char* ruleProtocol, uint32_t ruleLeaseDuration, const char* ruleFriendlyName);关键参数说明ruleIP: 必须是IPAddress类型不可传入字符串。常见错误是误用WiFi.localIP().toString().c_str()这会导致悬垂指针。正确用法是直接传WiFi.localIP()。ruleProtocol: 必须为字符串字面量TCP或UDP不可动态生成。库内部使用strcmp()比较动态字符串易因内存管理问题导致匹配失败。ruleLeaseDuration: 主流路由器华硕、TP-Link对0支持良好部分老旧路由器如某些Linksys型号可能要求最小值如3600秒此时需根据实测调整。3.3 提交配置Commit// 将所有已配置的端口映射规则提交至路由器IGD // 返回值true 表示所有规则提交成功false 表示至少一条失败 bool commitPortMappings();执行时机应在 Wi-Fi 连接建立并获取到 IP 地址后调用。典型setup()结构void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASSWORD); // 等待Wi-Fi连接 while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nWiFi connected!); Serial.print(IP address: ); Serial.println(WiFi.localIP()); // 创建TinyUPnP实例20秒超时 tinyUPnP new TinyUPnP(20000); // 配置端口映射将本地80端口映射为WAN口8080 tinyUPnP-addPortMappingConfig(WiFi.localIP(), 80, TCP, 0, WebServer); tinyUPnP-addPortMappingConfig(WiFi.localIP(), 1883, TCP, 0, MQTTBroker); // 提交至路由器 bool success tinyUPnP-commitPortMappings(); if (success) { Serial.println(Port mappings committed successfully!); } else { Serial.println(Failed to commit port mappings. Check router UPnP status.); } }3.4 运行时维护// 定期检查并更新端口映射状态应对路由器重启、UPnP服务中断等场景 // intervalMs: 检查间隔毫秒如60000010分钟 // reconnectCallback: 可选的Wi-Fi重连回调函数指针当检测到Wi-Fi断开时调用 // 若为NULL则不尝试重连 void updatePortMappings(uint32_t intervalMs, void (*reconnectCallback)() NULL);工程化使用要点intervalMs应设为足够长如10分钟避免频繁轮询增加路由器负载。reconnectCallback是高级功能适用于需要高可用性的场景。例如void connectWiFi() { WiFi.begin(SSID, PASSWORD); while (WiFi.status() ! WL_CONNECTED) { delay(1000); Serial.print(.); } } void loop() { // 每10分钟检查一次UPnP状态断网时自动重连 tinyUPnP-updatePortMappings(600000, connectWiFi); delay(1000); // 避免loop空转 }3.5 调试与诊断// 打印本地配置的所有端口映射规则setup中addPortMappingConfig添加的 void printPortMappingConfig(); // 查询路由器当前所有UPnP端口映射并打印需路由器支持GetListOfPortMappings操作 void printAllPortMappings();调试技巧当commitPortMappings()返回false时首要步骤是启用调试日志。在TinyUPnP.h第16行将#define UPNP_DEBUG true修改为true然后观察串口输出。典型成功日志[UPnP] SSDP Search sent, waiting up to 20000 ms... [UPnP] Got response from 192.168.1.1:5000 [UPnP] Downloading rootDesc.xml... [UPnP] Found WANPPPConnection:1 service, controlURL/ctl/PPPConn [UPnP] Sending AddPortMapping for port 80... [UPnP] Success! Port 80 mapped.失败日志则会清晰指出卡在哪个环节如“Timeout waiting for SSDP response”或“HTTP error 404 on controlURL”极大加速故障定位。4. 与EasyDDNS集成构建完整远程访问方案TinyUPnP 解决了“端口如何开通”的问题但未解决“公网IP如何获知”的问题。家庭宽带的公网IP通常是动态分配的DHCP会随时间变化。因此一个完整的远程访问方案必须结合 DDNSDynamic DNS服务。TinyUPnP 与 EasyDDNS 库的集成是官方推荐的最佳实践。4.1 EasyDDNS 工作原理EasyDDNS 是一个轻量级DDNS客户端支持 No-IP、DynDNS、FreeDNS 等主流服务商。其核心逻辑是定期如每10分钟通过 HTTP GET 查询http://icanhazip.com或类似服务获取当前设备的公网IP。将获取到的IP与DDNS服务商提供的域名如mydevice.ddns.net进行绑定更新。更新成功后任何对mydevice.ddns.net的DNS查询都将解析到最新的公网IP。4.2 集成代码示例#include Arduino.h #include ESP8266WiFi.h // or WiFi.h for ESP32 #include TinyUPnP.h #include EasyDDNS.h TinyUPnP *tinyUPnP; EasyDDNS ddns; void setup() { Serial.begin(115200); WiFi.begin(SSID, PASSWORD); while (WiFi.status() ! WL_CONNECTED) delay(500); // 初始化EasyDDNS服务商、域名、用户名、密码 ddns.setProvider(EASYDDNS_NOIP); ddns.setHostname(mydevice.ddns.net); ddns.setUsername(your_username); ddns.setPassword(your_password); ddns.begin(); // 启动DDNS客户端 // 初始化TinyUPnP tinyUPnP new TinyUPnP(20000); tinyUPnP-addPortMappingConfig(WiFi.localIP(), 80, TCP, 0, WebServer); tinyUPnP-commitPortMappings(); } void loop() { // 每5分钟更新一次DDNSEasyDDNS内置 ddns.update(); // 每10分钟检查并更新UPnP端口映射 tinyUPnP-updatePortMappings(600000, connectWiFi); delay(1000); }关键优势此集成方案实现了真正的“零配置远程访问”。用户只需记住一个固定的域名mydevice.ddns.net无论家庭宽带IP如何变化都能通过http://mydevice.ddns.net:8080访问设备的Web服务。TinyUPnP 确保端口始终开通EasyDDNS 确保域名始终指向正确的IP二者协同构成了嵌入式设备对外服务的坚实基石。5. 实战部署与常见问题排查在真实项目部署中TinyUPnP 的成功率并非100%其表现高度依赖路由器的UPnP实现质量。以下是经过大量现场验证的部署指南与故障树。5.1 路由器端前置检查清单在烧录固件前务必在路由器管理界面完成以下配置启用UPnP路径通常为高级设置 NAT转发 UPnP确保开关为“开启”。禁用IGMP Snooping某些路由器如部分小米型号的IGMP Snooping功能会干扰SSDP多播导致发现失败。关闭此项可显著提升成功率。检查防火墙规则确认路由器防火墙未阻止UDP 1900端口SSDP和TCP 5000端口IGD控制端口。固件版本老旧固件可能存在UPnP Bug。建议升级至厂商最新稳定版。5.2 典型故障与解决方案故障现象根本原因解决方案commitPortMappings()返回false串口无任何UPnP日志Wi-Fi未连接或IP未获取在commit前添加Serial.println(WiFi.localIP())确认IP有效性日志显示Timeout waiting for SSDP response路由器UPnP关闭或IGMP Snooping干扰检查路由器UPnP设置关闭IGMP Snooping日志显示HTTP error 404 on controlURL路由器IGD服务URL与TinyUPnP硬编码不匹配查看rootDesc.xml中controlURL字段修改库源码中对应路径需重新编译日志显示SOAP request failed: 500路由器UPnP服务异常或参数不合法检查ruleProtocol是否为TCP/UDP大小写敏感ruleLeaseDuration是否为0或足够大端口映射成功但外网无法访问路由器WAN口防火墙拦截或ISP封锁端口尝试映射非常用端口如8080而非80或联系ISP确认端口策略5.3 性能与资源占用实测数据在 ESP32-WROVER 模块PSRAM启用上TinyUPnP 的资源占用如下Flash占用约 12 KB含所有依赖的HTTP客户端代码RAM占用运行时峰值约 3.2 KB主要为HTTP请求缓冲区和XML解析临时存储SSDP发现耗时平均 1.2 秒在10台设备共存的局域网中AddPortMapping耗时平均 850 ms从发送到收到响应这些数据表明TinyUPnP 完全满足 ESP32/ESP8266 的资源约束其性能瓶颈通常不在库本身而在于 Wi-Fi 模块的网络栈效率和路由器的UPnP服务响应速度。6. 源码关键逻辑剖析理解 TinyUPnP 的源码逻辑是进行深度定制和问题排查的基础。其核心实现在TinyUPnP.cpp中主要分为三个模块。6.1 SSDP发现模块discoverIGD()该函数是整个流程的入口其伪代码逻辑如下bool TinyUPnP::discoverIGD() { // 1. 创建UDP套接字绑定到端口1900 UDP udp; udp.begin(1900); // 2. 构造并发送M-SEARCH报文 String msearch M-SEARCH * HTTP/1.1\r\n HOST: 239.255.255.250:1900\r\n MAN: \ssdp:discover\\r\n MX: 3\r\n ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n\r\n; udp.beginPacket(IPAddress(239, 255, 255, 250), 1900); udp.write(msearch.c_str()); udp.endPacket(); // 3. 在timeoutMs内循环接收响应 unsigned long start millis(); while (millis() - start timeoutMs) { int len udp.parsePacket(); if (len 0) { // 4. 解析响应提取LOCATION头 String response udp.readString(); int locIndex response.indexOf(LOCATION:); if (locIndex ! -1) { locationUrl response.substring(locIndex 11).trim(); return true; // 发现成功 } } delay(10); } return false; // 超时未发现 }关键洞察该模块采用阻塞式UDP接收简单可靠。parsePacket()的返回值len是判断是否有新包到达的唯一依据避免了复杂的事件驱动模型。6.2 SOAP请求模块sendSOAPRequest()该函数封装了HTTP POST的细节其核心在于HTTPClient的使用bool TinyUPnP::sendSOAPRequest(const String url, const String soapBody) { HTTPClient http; http.begin(url); // url形如 http://192.168.1.1/ctl/PPPConn // 设置SOAP专用头 http.addHeader(Content-Type, text/xml; charset\utf-8\); http.addHeader(SOAPAction, \urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping\); // 发送POST请求 int httpCode http.POST(soapBody); // 检查响应 if (httpCode HTTP_CODE_OK) { String payload http.getString(); // 检查payload中是否包含u:AddPortMappingResponse if (payload.indexOf(u:AddPortMappingResponse) ! -1) { return true; } } http.end(); return false; }关键洞察TinyUPnP 不依赖复杂的XML解析库而是使用String::indexOf()进行简单的子串匹配来验证SOAP响应。这是一种典型的嵌入式“够用就好”哲学——牺牲了对XML结构的严格校验换来了极小的代码体积和极高的运行效率。6.3 状态机管理TinyUPnP 内部维护一个简单的状态机其状态流转如下IDLE - DISCOVERING - DISCOVERED - CONFIGURING - COMMITTED - MAINTAINING每个状态对应不同的loop()行为。例如在MAINTAINING状态下updatePortMappings()会周期性调用sendSOAPRequest()来验证映射是否依然有效。这种状态机设计使得库的行为完全可预测便于在大型项目中进行集成和调试。TinyUPnP 的价值不在于其技术的前沿性而在于其对嵌入式工程现实的深刻理解在资源、时间、可靠性三者的三角约束下选择最务实的解法。它不试图成为另一个 libupnp而是成为你固件中那个沉默而可靠的“端口管家”在你专注于核心业务逻辑时悄然为你打通通往世界的那扇门。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2470176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…