目录

1:pom文件引入jar包

2:配置文件

3:踩坑-1

4:踩坑-2

5:后端二次验证

6:自定义背景图


给用户做的一个小程序,被某局安全验证后,说登录太简单,没有验证码等行为认证。于是想着给登录页加上一个滑块验证码(数字验证码还要输入,太麻烦了),于是开始问deepseek,列举了几个,看到有anji-captcha,就开始尝试搞了。


一开始问deepseek使用方法,给我列出的简直简单得不要不要的,还以为真的很简单,按照几个步骤开始搞了,结果根本用不了,网上去搜相关案例,全部清一色照搬anji.captcha开源文档,基本一摸一样,基本没看到有人写具体使用经验,全是寥寥草草照搬,痛苦至极,简单的后端代码,查了不知道多少资料,搞了一天多,真是痛苦加倍。


anji-captcha开源项目地址:https://github.com/anji-plus/captcha

anji-captcha开源文档地址:在线体验暂时下线 !!! | AJ-Captcha


1:pom文件引入jar包

<dependency><groupId>com.anji-plus</groupId><artifactId>spring-boot-starter-captcha</artifactId><version>1.3.0</version>
</dependency>

开源文档里面写着出了1.4.0,尝试着引入没成功,后面改回使用1.3.0


2:配置文件

#使用redis作为缓存,也可以使用local
aj.captcha.cache-type=redis# 缓存的阈值,达到这个值,清除缓存
aj.captcha.cache-number=5000# 定时清除过期缓存(单位秒),设置为0代表不执行
aj.captcha.timing-clear=120# 初始化验证码类型-滑块验证码
aj.captcha.type=blockPuzzle# 右下角水印文字,中文请使用unicode转码
aj.captcha.water-mark=# 校验滑动拼图允许误差偏移量12px
aj.captcha.slip-offset=12# 开启aes加密坐标
aj.captcha.aes-status=true# 滑动干扰项(0/1/2) 0不开启,2最强干扰
aj.captcha.interference-options=1

还有其他配置,具体可以看开源文档介绍,推荐去看一下,了解都有哪些是你需要的。

使用了redis作为缓存,所以项目接入redis,不会用的话自行去其他地方查,本文不做介绍。


3:踩坑-1

网上很多资料就到这里了,轻描淡写,说引入包,配置好基本参数,就可以基本使用了。

于是开始尝试使用,根据介绍aj.captcha的jar包里面默认提供有两个controller接口,给我们前端调用,分别是:【/captcha/get  获取验证码】【/captcha/check  校验验证码】。

于是开始前端调用,首先这里是登录页使用,所以这两个接口需要放权,不验证登录权限,具体自己根据自己的项目进行配置。

调用 /captcha/get,参数如下:

{"captchaType": "blockPuzzle",  //验证码类型,表示使用滑块验证码"clientUid": "唯一标识"  //客户端UI组件id,组件初始化时设置一次,UUID(非必传参数)
}

开始尝试用postman调用,不出意外,报错了,错误忘记啥了,大概意思是没有指定aj-captcha的redis缓存配置,这一步网上有资料,开源文档也有说明,不用多久就解决了。

开源文档的说明是:对于分布式多实例部署的应用,应用必须自己实现CaptchaCacheService,比如用Redis或者memcache,参考service/springboot/src/.../CaptchaCacheServiceRedisImpl.java
在resources目录新建META-INF.services文件夹,参考resource/META-INF/services中的写法。

一开始我觉得我不是分布式系统,就觉得不用加,所以报错了,后面加上去就好了。


启动类所在模块的resources文件夹下面新建一个文件,路径就是上面说的:resources/META-INF/services,services下新建一个文件,文件名为:com.anji.captcha.service.CaptchaCacheService

文件内容为:CaptchaCacheService接口的实现类位置,比如:

com.xxx.yyy.config.CaptchaCacheServiceRedisImpl

所以需要去com.xxx.yyy.config下面建一个名为CaptchaCacheServiceRedisImpl的类,并实现CaptchaCacheService接口。

import com.anji.captcha.service.CaptchaCacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService {private StringRedisTemplate stringRedisTemplate;@Overridepublic String type() {return "redis";}@Overridepublic void set(String key, String value, long expiresInSeconds) {stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);}@Overridepublic boolean exists(String key) {return stringRedisTemplate.hasKey(key);}@Overridepublic void delete(String key) {stringRedisTemplate.delete(key);}@Overridepublic String get(String key) {return stringRedisTemplate.opsForValue().get(key);}@Overridepublic Long increment(String key, long val) {if (!this.stringRedisTemplate.hasKey(key)) {return null;}return this.stringRedisTemplate.opsForValue().increment(key, val);}@Autowiredpublic void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}
}

重启后端服务后,再次去postman调用,可以了,正确返回结果了。

其中:secretKey 是aes密匙,后面用来加密坐标,验证滑动是否正确的。

originalImageBase64:滑块背景图,310 * 155的分辨率。

jigsawImageBase64:滑块缺口图,47 * 155的分辨率。

token:验证滑动坐标时,需要提交回后端,后端需要根据这个token去找缓存,取到对应缓存信息。

图片是aj-captcha默认自带的,它默认有几张图片。


获取验证码后,查看后端redis缓存,内容是这样的,记录着aes密匙和缺口坐标信息。缓存有效期120秒。


4:踩坑-2

到这里,我已经很兴奋了,觉得后端部分已经完成了80%了,于是开始调试第二个接口,也就是验证滑动位置接口【/captcha/check】,开始按照开源文档介绍传参。

{"captchaType": "blockPuzzle",  // 指定为滑块验证码"pointJson": "QxIVdlJoWUi04iM+65hTow==", // aes加密后坐标信息"token": "71dd26999e314f9abb0c635336976635" // token是前面的get接口返回的
}

 就这个aes加密后坐标信息,我也吃了不少苦头,aes加密我知道,但原文究竟应该是什么,什么样的格式,开源文档没有说明,硬生生网上查了很久资料才知道,问deepseek,回答模糊不清。可能也是我傻狗吧,其实就是一个json对象,里面是x和y的值。比如:{x:155,y:13},然后用JSON.stringify转成字符串,再加密就行了。

然后,加密模式是什么?ECB? CBC? 没有说明,只能一个个尝试,最后是ECB


搞好上面,觉得一切都差不多了,然后postman一调接口,返回说验证失败,位置不对。那正常,因为坐标我是随便写的,这里说一下,失败后,后端的缓存就没有了,也就是说,只能被验证一次。


然后我突然发现一个事情,验证接口竟然要传y坐标值???我直接懵逼了,滑块一直不都是横向右滑动的吗,横向滑动取到x坐标值,我哪来的y坐标值,然后扒看了【BlockPuzzleCaptchaServiceImpl】的check方法源码,确实有验证y坐标值,当场两眼一黑。

为了这个问题,我近乎疯狂,又问deepseek,又是一堆胡乱回答,已对它彻底失望,百度找答案,由于网上千篇一律都是照搬开源文档,没点自己个人经验的,根本找不到答案。于是开始尝试不传y坐标值,发现报错,或者随意传值,结果就是验证失败,差点放弃aj-captcha。最后就是自己想办法了。

最后想到几个方案:

1:多调几次接口发现,redis缓存里面的y坐标值永远都是5,那前端也直接写死5算了,但想想不太靠谱。

2:不使用aes加密,后端会返回缺口坐标给前端,里面包含了y坐标值,但这样搞就不安全了,获取验证码的同时直接把答案告诉你了,这明显不妥。

3:尝试自己新建一个类,继承【BlockPuzzleCaptchaServiceImpl】或实现其父类,重写check方法,发现后面会报错,这条路走不通。最后想到的办法是使用AOP切面,拦截【BlockPuzzleCaptchaServiceImpl】check方法。


新建一个名为【CustomizeCaptchaService】的类,如下:


import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.model.vo.PointVO;
import com.anji.captcha.service.impl.CaptchaServiceFactory;
import com.anji.captcha.util.AESUtil;
import com.anji.captcha.util.JsonUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;@Aspect
@Service
public class CustomizeCaptchaService {private final Logger logger = LoggerFactory.getLogger(getClass());private static String REDIS_SECOND_CAPTCHA_KEY = "RUNNING:CAPTCHA:second-%s";private static String REDIS_CAPTCHA_KEY = "RUNNING:CAPTCHA:%s";private static String cacheType = "redis";private static Long EXPIRESIN_THREE = 3 * 60L;@Value("${aj.captcha.slip-offset:10}")private Integer slipOffset;@Around("execution(* com.anji.captcha.service.impl.BlockPuzzleCaptchaServiceImpl.check(..))")public Object aroundCheckPoint(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();CaptchaVO captchaVO = (CaptchaVO) args[0];// ################################# 位置标记 #############################################// 原来方法里,这个位置是处理校验次数是否超过限制的,由于我不需要验证,这里没加,但这个位置先标记一下,后面再讲// ResponseModel r = super.check(captchaVO);// if(!validatedReq(r)){//     return r;// }// ################################# 位置标记 #############################################String codeKey = String.format(REDIS_CAPTCHA_KEY, captchaVO.getToken());if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_INVALID);}String s = CaptchaServiceFactory.getCache(cacheType).get(codeKey);CaptchaServiceFactory.getCache(cacheType).delete(codeKey);PointVO point = null;PointVO point1 = null;String pointJson = null;try {point = JsonUtil.parseObject(s, PointVO.class);//aes解密pointJson = AESUtil.aesDecrypt(captchaVO.getPointJson(), point.getSecretKey());point1 = JsonUtil.parseObject(pointJson, PointVO.class);} catch (Exception e) {logger.error("验证码坐标解析失败", e);return ResponseModel.errorMsg(e.getMessage());}if (point.x - slipOffset > point1.x || point1.x > point.x + slipOffset) {return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR);}//校验成功,将信息存入缓存String secretKey = point.getSecretKey();String value = null;try {value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(pointJson), secretKey);} catch (Exception e) {logger.error("AES加密失败", e);return ResponseModel.errorMsg(e.getMessage());}String secondKey = String.format(REDIS_SECOND_CAPTCHA_KEY, value);CaptchaServiceFactory.getCache(cacheType).set(secondKey, captchaVO.getToken(), EXPIRESIN_THREE);captchaVO.setResult(true);captchaVO.resetClientFlag();return ResponseModel.successData(captchaVO);}
}

类里打上了AOP类注解,并切面拦截【BlockPuzzleCaptchaServiceImpl】的check方法,自己重写此方法,其实我也是去原类方法里面复制出来,然后稍微改动一下。

改动点:1:去掉检查验证次数是否短时间频繁。2:去掉y坐标验证。

我不需要检查验证次数是否频繁,所以没搞这个,如果确实需要,那就有点麻烦了,因为这个验证方法是【BlockPuzzleCaptchaServiceImpl】的父类【AbstractCaptchaService】的check方法写的,【BlockPuzzleCaptchaServiceImpl】已经重写了此方法,看【BlockPuzzleCaptchaServiceImpl】的check方法源码就会知道,它先执super.check(captchaVO)调用了父类的验证方法,所以我们切面拦截后,是没法直接调用【AbstractCaptchaService】的check方法的。

最后又是花时间处理,真麻,想到的办法是,再建一个【AjCaptchaVerify】类继承【AbstractCaptchaService】。

import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.impl.AbstractCaptchaService;public class AjCaptchaVerify extends AbstractCaptchaService {public Boolean superCheck(CaptchaVO captchaVO) {ResponseModel r = super.check(captchaVO);if(!validatedReq(r)){return false;}return true;}@Overridepublic String captchaType() {return null;}
}

然后AOP切面方法里面,那段位置标记注释,可以改成:

// ################################# 位置标记 #############################################
ResponseModel verifyResponseModel = new AjCaptchaVerify().superCheck(captchaVO);
if (Objects.nonNull(verifyResponseModel)) {return verifyResponseModel;
}
// ################################# 位置标记 #############################################

这样就可以校验短时间内验证是否频繁。


真是多坑,踩得我怀疑人生。


5:后端二次验证

anji-captcha自带有后端二次验证,至于为什么要用后端二次验证,就以登录来说,用户选择短信登录,那么获取短信验证码的时候,给他来一个滑块的行为认证,必须滑对才能获取短信验证码,这是个正常操作了,很多系统都有。那按照之前讲的,使用【/captcha/check】验证滑动是否正确,这个是anji-captcha自带的验证接口,滑动完成,调用【/captcha/check】验证是否正确,正确的话,再调用【获取短信验证码接口】,为了防止越过行为认证,直接调取【获取短信验证码接口】,所以需要到这个后端二次验证了。当然,如果你把验证滑动行为认证和获取短信验证码集中在一个接口里面,那就不需要这个二次验证了。

这个后端二次验证文档同样没有说明,网上也没找着,还是得扒看源码了解。


扒开【BlockPuzzleCaptchaServiceImpl】的check方法,会发现最后验证成功后会设置一个缓存,用于二次认证使用。

它的缓存key是用aes加密过的,密匙还是用回【/captcha/get】返回的。

加密内容是:token---坐标点json字符串(解密过的)

于是到缓存里面可以看到验证成功后加密是这样的,值是token,后面没啥用,主要看key。


知道他的加密方式后,前端就可以根据这样加密出一串密文,也就是上面这个缓存的key,传给后端,后端二次验证方法:

// 先注入
@Autowired
private CaptchaService captchaService;// 具体使用
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaType('blockPuzzle');
captchaVO.setCaptchaVerification('前端加密后的密文(token---坐标点json字符串)');
ResponseModel verification = this.captchaService.verification(captchaVO);
if (Objects.isNull(verification) || !verification.isSuccess()) {// 认证失败
} else {// 认证通过
}

6:自定义背景图

如果不想要anji-captcha自带的滑块背景图,也可以自己配置。

当然,这个配置,也是一个坑,我狠狠的踩了。开源文档没有过多介绍这块,网上的更加零碎,还是摸着石头过河,整理网上零碎的信息,最后一点点确认出来了。


首先,背景图要求是310 * 155的分辨率,如果到了前端觉得显示模糊,可以自己选择*2或者*3加大分辨率,然后前端显示固定成310 * 155,不过前端验证的时候可能得/2或者/3了,具体没试,因为太大会影响滑块验证码图片加载的速度。


其次,配置文件配置背景图和缺口图路径:

aj.captcha.jigsaw=classpath:images/jigsaw

背景图和缺口图放在启动类所在模块

背景图路径【resources/images/jigsaw/original

缺口图路径【resources/images/slidingBlock

上面的/images/jigsaw这个路径随便写,但最后一个文件夹名称必须使用【original】和【slidingBlock】,因为anji-captcha源码里面就是写死了。


 然后original文件夹下面的背景图,放个几张进去,你看着来,三四张也行,五六张也行,反正是随机取的,图片文件名称也是随便自己命名。注意:这里说的背景图,是一张完整的背景图,没有被扣出缺口的。


然后slidingBlock文件夹下面放几张缺口图,这个就头疼了,完全不知道这个缺口图应该是怎么样,找了很久都没有具体说明,最后找到了一个网友的项目代码,他没有具体说明这个缺口图有什么要注意的,就说把项目下面的缺口图复制到自己项目就行了。我一看也是一脸疑惑,你的缺口图能适配我的背景吗,虽然很纳闷,但还是抱着心态试了一下,还真的成功。

看了一下源码,应该是根据给定的缺口图形状以及位置(是y坐标固定),去背景图里面随机x坐标扣出一块相同形状缺口图,然后最终形成了属于本次滑块验证码的缺口图。

这里放出来一下,大家直接保存到项目里面使用就行了。缺口图是:47 * 155的分辨率。

图片放出来被自动打上水印了,自行去掉或者去【点击这里获取】拿


到这里,后端部分就结束了,说了这么多没说到前端的,前端点击下面的另一篇文章看吧。

超详细 anji-captcha滑块验证uniapp微信小程序前端组件https://blog.csdn.net/new_public/article/details/149336921



码字不易,与你有利,勿忘点赞

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用AVPlayer播放FairPlay DRM视频的最佳实践

01DRM 介绍DRM&#xff0c;即数字版权管理&#xff08;Digital Rights Management&#xff09;&#xff0c;是指使用加密技术保护视频内容、通过专业技术安全地存储和传输密钥&#xff08;加密密钥和解密密钥&#xff09;、并允许内容生产商设置商业规则&#xff0c;限制内容观…

《机器学习数学基础》补充资料:拉格朗日乘子法

瑞士数学家欧拉&#xff08;Leonhard Euler&#xff0c;1707-1783&#xff09;的大名&#xff0c;如雷贯耳——欧拉&#xff0c;是按德文发音翻译。欧拉不仅是公认的十八世纪最伟大的数学家&#xff0c;还是目前史上最多产的数学家。所著的书籍及论文多达 886 部&#xff08;篇…

【PTA数据结构 | C语言版】二叉堆的朴素建堆操作

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n 个顺序存储的数据用朴素建堆操作调整为最小堆&#xff1b;最后顺次输出堆中元素以检验操作的正确性。 输入格式&#xff1a; 输入首先给出一个正整数 c&#xff08;≤1…

深入解析PyQt5信号与槽的高级玩法:解锁GUI开发新姿势

信号与槽机制是PyQt框架实现组件间通信的核心技术。掌握其高级用法能极大提升开发效率和代码灵活性。本文将通过六大核心模块&#xff0c;结合实战案例&#xff0c;全方位解析信号与槽的进阶使用技巧。自定义信号与槽的完全指南 1. 信号定义规范 class CustomWidget(QWidget):#…

gitee某个分支合并到gitlab目标分支

一、克隆Gitee仓库到本地 git clone https://gitee.com/用户名/仓库名.gitcd 仓库名二、添加 GitLab 仓库作为远程仓库 git remote add gitlab https://gitlab.com/用户名/仓库名.git三、查看所有远程仓库 git remote -v四、拉取 Gitee 上的目标分支 git fetch origin 分支名五…

PyQt5信号与槽(信号与槽的高级玩法)

信号与槽的高级玩法 高级自定义信号与槽 所谓高级自定义信号与槽&#xff0c;指的是我们可以以自己喜欢的方式定义信号与槽函 数&#xff0c;并传递参数。自定义信号的一般流程如下&#xff1a; &#xff08;1&#xff09;定义信号。 &#xff08;2&#xff09;定义槽函数。 &a…

第5天 | openGauss中一个用户可以访问多个数据库

接着昨天继续学习openGauss,今天是第五天了。今天学习内容是使用一个用户访问多个数据库。 老规矩&#xff0c;先登陆墨天轮为我准备的实训实验室 rootmodb:~# su - omm ommmodb:~$ gsql -r创建表空间music_tbs、数据库musicdb10 、用户user10 并赋予 sysadmin权限 omm# CREATE…

Vue3 Anime.js超级炫酷的网页动画库详解

简介 Anime.js 是一个轻量级的 JavaScript 动画库&#xff0c;它提供了简单而强大的 API 来创建各种复杂的动画效果。以下是 Anime.js 的主要使用方法和特性&#xff1a; 安装 npm install animejs 基本用法 <script setup> import { ref, onMounted } from "vu…

苦练Python第18天:Python异常处理锦囊

苦练Python第18天&#xff1a;Python异常处理锦囊 原文链接&#xff1a;https://dev.to/therahul_gupta/day-18100-exception-handling-with-try-except-in-python-3m5a 作者&#xff1a;Rahul Gupta 译者&#xff1a;倔强青铜三 前言 大家好&#xff0c;我是倔强青铜三。是一名…

JVM——如何对java的垃圾回收机制调优?

GC 调优的核心思路就是尽可能的使对象在年轻代被回收&#xff0c;减少对象进入老年代。 具体调优还是得看场景根据 GC 日志具体分析&#xff0c;常见的需要关注的指标是 Young GC 和 Full GC 触发频率、原因、晋升的速率、老年代内存占用量等等。 比如发现频繁会产生 Ful GC&am…

正则表达式使用示例

下面以 Vue&#xff08;前端&#xff09;和 Spring Boot&#xff08;后端&#xff09;为例&#xff0c;展示正则表达式在前后端交互中的应用&#xff0c;以邮箱格式验证为场景&#xff1a;1.前端<template><div class"register-container"><h3>用户…

云端微光,AI启航:低代码开发的智造未来

文章目录前言一、引言&#xff1a;技术浪潮中的个人视角初次体验腾讯云开发 Copilot1.1 低代码的时代机遇1.1.1 为什么低代码如此重要&#xff1f;1.2 AI 的引入&#xff1a;革新的力量1.1.2 Copilot 的亮点1.3 初学者的视角1.3.1 Copilot 带来的改变二、体验记录&#xff1a;云…

图片上传实现

图片上传change函数图片上传图片上传到服务器上传的图片在该页面中显示修改界面代码最终实现效果change函数 这里我们先用输入框控件来举例&#xff1a; 姓名&#xff1a;<input typetext classname>下面我们来写 js 语句&#xff0c;对控件进行绑事件来获取输入框内的…

【PTA数据结构 | C语言版】多叉堆的上下调整

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n 个已经满足 d 叉最小堆顺序约束的数据直接读入最小堆&#xff1b;随后将下一个读入的数据 x 插入堆&#xff1b;再执行删顶操作并输出删顶的元素&#xff1b;最后顺次输…

selenium后续!!

小项目案例:实现批量下载网页中的资源根据15.3.2小节中的返回网页内容可知,用户只有获取了网页中的图片url才可以将图片下载到*在使用selenium库渲染网页后,可直接通过正则表达式过滤出指定的网页图片&#xff0c;从而实现批量下载接下来以此为思路来实现一个小项目案例。项目任…

深度解析Linux文件I/O三级缓冲体系:用户缓冲区→标准I/O→内核页缓存

在Linux文件I/O操作中&#xff0c;缓冲区的管理是一个核心概念&#xff0c;主要涉及用户空间缓冲区和内核空间缓冲区。理解这两者的区别和工作原理对于高效的文件操作至关重要。 目录 一、什么是缓冲区 二、为什么要引入缓冲区机制 三、三级缓冲体系 1、三级缓冲体系全景图…

【每日算法】专题十三_队列 + 宽搜(bfs)

1. 算法思路 BFS 算法核心思路 BFS&#xff08;广度优先搜索&#xff09;使用 队列&#xff08;Queue&#xff09;按层级顺序遍历图或树的节点。以下是 C 实现的核心思路和代码模板&#xff1a; 算法框架 #include <queue> #include <vector> #include <un…

【动手实验】发送接收窗口对 TCP传输性能的影响

环境准备 服务器信息 两台腾讯云机器 t04&#xff08;172.19.0.4&#xff09;、t11&#xff08;172.19.0.11&#xff09;&#xff0c;系统为 Ubuntu 22.04&#xff0c;内核为 5.15.0-139-generic。默认 RT 在 0.16s 左右。 $ ping 172.19.0.4 PING 172.19.0.4 (172.19.0.4) …

28、鸿蒙Harmony Next开发:不依赖UI组件的全局气泡提示 (openPopup)和不依赖UI组件的全局菜单 (openMenu)、Toast

目录 不依赖UI组件的全局气泡提示 (openPopup) 弹出气泡 创建ComponentContent 绑定组件信息 设置弹出气泡样式 更新气泡样式 关闭气泡 在HAR包中使用全局气泡提示 不依赖UI组件的全局菜单 (openMenu) 弹出菜单 创建ComponentContent 绑定组件信息 设置弹出菜单样…

让老旧医疗设备“听懂”新语言:CAN转EtherCAT的医疗行业应用

在医疗影像设备的智能化升级中&#xff0c;通信协议的兼容性常成为工程师的“痛点”。例如&#xff0c;某医院的移动式X射线机采用CAN协议控制机械臂&#xff0c;而主控系统基于EtherCAT架构。两者协议差异导致数据延迟高达5ms&#xff0c;影像定位精度下降&#xff0c;甚至影响…