当前位置: 首页 > news >正文

蓝牙Mesh专有DFU

蓝牙Mesh专有DFU

    • Mesh专有DFU协议介绍
      • 特征
      • DFU模式和类型
      • 角色
      • 并发传输
      • 混合设备的网络
      • 传输速率
      • 后台操作
      • 传输分区
      • 内存映射
      • 安全
      • DFU固件ID
        • Application firmware ID
        • SoftDevice firmware ID
        • Bootloader firmware ID
      • 设备页面
        • 格式
        • 内容

Mesh专有DFU协议介绍

设备固件更新(Device Firmware Update, DFU)是对蓝牙mesh设备的固件进行更新的过程。

Nordic的mesh DFU协议采用了专有的nRF OpenMesh项目,并在专有的OpenMesh协议上运行。OpenMesh协议是一种基于广播的协议,类似于蓝牙mesh,但它不支持寻址、确认消息传递或加密。

特征

专有的mesh DFU协议优化为尽可能有效地更新网络中的所有设备。尽管它与nRF5 SDK Bootloader和DFU模块共享一些工具和代码模块,但在协议和操作方面存在一些差异,以便尽可能轻松地更新大量设备。
在这里插入图片描述

DFU模式和类型

Mesh网络支持两种DFU类型,一种是后台模式,新固件在应用运行过程中通过后台传输,同时在传输完成的时候会通知应用程序,应用程序可以在新固件准备好之后改写flash。另一种是Bootloader模式,用于应用程序无法运行时使用BootLoader进行传输,此模式主要用于在应用程序出现故障时的回退机制。

在每一种DFU模式下,都可以分别对BootLoader,Application和SoftDevice进行DFU传输。这三种固件都必须单独传输,并且每个都有自己的标识符,每个都在内存映射中有自己的区域。用于Mesh SoftDevice和Bootloader的nRF5 SDK可以独立更新。如果想在Bootloader程序中进行小的更改,这可以节省时间和内存。

角色

当设备作为DFU传输的发起者时,使用源角色。源设备控制发送报文的时间间隔。它还响应DFU数据请求包。源角色由nRF Util工具控制。
目的角色用于设备升级时使用。专有Mesh DFU模块接收新固件并在传输完成时通知应用程序。在这个角色中,专有的Mesh DFU模块也重传它接收到的DFU数据包。
中继角色用于重传从其他设备接收到的DFU数据包。在此角色下,设备不将接收到的报文保存到flash中。

并发传输

与nRF5 SDK DFU相反,整个蓝牙mesh网络可以通过并发传输同时更新。
蓝牙Mesh网络可以包含数百个设备,逐个更新所有设备需要花费大量时间。为了解决这个问题,专有的mesh DFU协议允许mesh网络设备将它们接收到的数据转发给它们的邻居。被动mesh设备和接收传输的设备都将转发所有数据包,从而确保DFU传输到网络中的所有设备。这种方法比将整个DFU传输单独传递到每个设备要快得多。

接收到DFU传输的设备将对它接收到的每条数据报文执行以下步骤:
1、验证数据包尚未被接收。
2、在flash中以传输数据的适当偏移量存储数据包的有效载荷。
3、标记接收到的数据包。
4、以指数间隔重新传输数据包的预定次数。
对传输内容不直接感兴趣的设备将只执行步骤1、3和4,以确保网络中更远的目标设备仍然接收到数据包。

混合设备的网络

通常,蓝牙mesh网络包含具有多个不同角色和固件的设备。在执行DFU传输时,能够区分这些设备是很重要的。如果灯开关设备的固件更新到灯泡上,很可能灯泡停止工作。这对于一对一DFU传输是不同的,就像nRF5 SDK DFU协议所执行的那样,发送方能够识别目标设备并向其发送正确的固件。

专有mesh DFU协议通过允许每种设备类型除了应用程序版本号外,还拥有自己的应用程序ID来处理混合设备网络。当设备收到即将到来的DFU传输的通知时,它可以将传输的应用程序ID与自己固件的应用程序ID进行比较。如果应用id匹配,并且传入的传输版本较高,则设备通常接受该传输。如果一个设备收到即将到来的DFU传输通知与自己的应用程序ID不同,它可以选择作为该传输的中继设备,或者忽略它。

传输速率

与nRF5 SDK DFU协议相比,专有mesh DFU传输速度相当慢。例如,传输100 kB的固件映像需要大约一个小时。专有的mesh DFU协议依赖于冗余来确保可靠的通信,因此与nRF5 SDK DFU相比,传播相同数量的数据需要更长的时间。DFU数据以16字节的块定期发送,每个包都有一些冗余传输,以确保所有设备都能接收到它。报文间隔由传输源设备控制。默认情况下,蓝牙mesh DFU工具每500毫秒发出一个新数据包(使传输速率为16 B/500 ms = 32 B/s),但该数字应根据蓝牙mesh网络属性进行调整。
需要考虑的一些网络特性有:
网络密度:在彼此radio范围内的设备数量对数据包接收速率有很大影响。在彼此radio距离内的设备数量越多,会导致更多的数据包冲突,从而降低总吞吐量。
网络跨度:网络中的每一跳都有一定的丢包风险,并造成流量的延迟。网络中的跳数越高,某些设备错过传输的风险就越高。
网络拓扑结构:虽然高节点密度可能会对传输成功率产生负面影响,但到达目标节点的路径太少可能会导致数据包丢失。目标节点越是依赖于多个中继设备才能成功传输,那么在传输过程中的某个时刻丢失目标节点的可能性就越高。
外部噪音:当部署在嘈杂的环境中时,专有的mesh DFU性能更差(像所有无线技术一样)。

因为这些特征对于所有的部署都是不同的,所以不可能制定一个适用于所有网络的通用规则。为了最大限度地提高DFU性能:
① 调优单个部署;
② 如果可能的话,将DFU转移安排在预期的低流量期间,噪音最少。

后台操作

由于专有mesh DFU协议的传输速率相对较慢,传输最终可能需要一个多小时,这对于许多应用程序来说是不可接受的停机时间。为了解决这个问题,专有的网格DFU实现了一个后台模式。

后台模式允许应用程序在DFU传输过程中继续正常运行。后台模式是默认的操作模式,除非设备上没有有效的应用程序,在这种情况下,它会回落到引导加载程序(BootLoader)。

传输分区

接收后台DFU传输的设备必须将传入的包存储在flash区域中未使用的分区,以避免在运行时覆盖自己。在此过程中,DFU传输数据被存储在分区中,一旦完成,应用程序可以告诉引导加载程序将分区内容复制到应用程序区域,从而有效地完成更新。尽管引导加载程序会尽快通知应用程序完成传输,但应用程序可以在任何时候自由地复制分区,甚至根本不复制。同一时间,每种传输类型只能有一个分区,并且分区不能重叠。完成分区DFU传输后将删除同一传输类型的现有扇区。

分区必须放置在一个flash区域中,该区域必须足够大,以便在传输过程中容纳整个传入的应用程序,并且不能与新应用程序或旧应用程序重叠。为了确保最大的空间来接收传输,无论是作为分区还是作为完整的应用程序,通常建议将分区的开始位置放在设备应用程序部分的正中间。如果在传输完成后,传输内容不能用于分区或应用程序,则设备必须回到引导加载程序,并在引导加载程序模式下执行传输。

内存映射

专有mesh DFU协议使用与nRF5 SDK的Bootloader和DFU模块相同的闪存映射,仅有细微差别。与将MBR参数存储放在Bootloader和Bootloader设置(在专有mesh DFU中称为设备页面)之间不同,专有mesh DFU协议中的MBR参数存储放在Bootloader和应用程序区之间。

内存映射图:
在这里插入图片描述
内存映射包含以下主要固件元素:
SoftDevice
Application
Bootloader

这些与前面提到的传输类型相对应。每个固件元素都可以通过DFU传输单独更新。应用程序使用引导加载程序来执行接收和中继算法步骤,即使在后台模式下工作也是如此。在初始化蓝牙mesh框架时,DFU模块初始化引导加载程序中的命令处理程序模块,该模块与应用程序一起运行。

为了能够与应用程序一起运行,BootLoader程序保留设备上最后768字节的RAM。在所有蓝牙mesh项目文件和链接器脚本中都会考虑到这个保留的RAM。未能保留这些字节将导致应用程序启动时BootLoader程序出现意外行为。

安全

与nRF5 SDK类似,专有mesh DFU不加密DFU传输的数据。

在蓝牙mesh的情况下,这是一个从OpenMesh协议继承的限制,这意味着在任何情况下都不应该将安全敏感数据(如密钥)作为DFU传输的一部分发送。然而,专有mesh DFU确实具有椭圆曲线数字签名(ECDSA)用于验证传输。虽然签名是可选的,但强烈建议对所有传输签名。在创建DFU传输时,使用私有签名密钥执行签名,并且可以使用匹配的公共签名密钥对所有蓝牙mesh设备进行预编程,以对固件进行身份验证。如果蓝牙mesh设备具有公共签名密钥,则在完成传输之前,它将始终要求签名通过。签名算法通过创建传输元数据和固件的SHA256哈希来执行。

哈希分解:
在这里插入图片描述
固件ID (F)的大小取决于传输的类型。

ECDSA使用NIST P-256曲线(secp256r1)创建签名:

signature = ecc_sign(curve=P-256, private_key, hash)

在目标设备上使用匹配的公钥验证签名:

authenticated = ecc_verify(curve=P-256, public_key, hash, signature)

由于哈希需要整个固件数据,所以直到整个传输完成后才会检查签名。这种设计为目标设备上的拒绝服务攻击创造了可能性,因为攻击者可能会发起错误的传输。这种错误的传输可能与正常的传输难以区分,直到最后的签名检查,此时目标设备可能已经花费了大量资源来接收传输。然而,这个攻击向量不允许攻击者在目标设备上执行任何代码,因为当签名检查失败时,目标设备将删除所有关于传输的信息。

DFU固件ID

所有专有mesh DFU传输都由固件ID标识。固件ID的结构取决于传输类型:
Application firmware ID
SoftDevice firmware ID
Bootloader firmware ID
为了决定是否接受传入的传输,所有蓝牙mesh设备都在其设备页面中携带其当前固件id。

Application firmware ID

应用程序固件ID标识应用程序。

每个应用程序都有一个ID和版本号。通常,设备应该接受DFU应用程序传输与自己的固件相同应用程序ID,并且版本号更高。版本号为32位数字,版本方案由用户自定义。

当处于引导加载程序模式时,预构建的引导加载程序强制执行严格递增的版本号。这可以防止恶意设备将固件降级到以前的版本,这种攻击可以用来重新引入固件中的旧弱点。当处于后台模式时,不强制执行此规则,但强烈建议使用此规则,因为没有其他原生方法可以防止恶意降级。

要为每个应用程序创建唯一的固件ID,应用程序ID包含一个32位的Company ID字段,用于标识设备供应商。公司ID字段可以包含:
①蓝牙SIG分配的公司ID (0xFFFF或更低),或
②随机选择的大于0xFFFF的标识符。
随机选择的标识符不能保证唯一,但它允许没有分配公司ID的供应商保持高概率的唯一性。
在这里插入图片描述

SoftDevice firmware ID

Nordic为每个SoftDevice版本分配一个唯一的16位SoftDevice标识符,可以从固件中读出。

设备上的SoftDevice固件ID意味着匹配这个数字(尽管这不是正确操作所明确要求的)。由于SoftDevice id代表的是发布id,而不是不断增加的版本号,因此是否接受传入SoftDevice DFU传输的策略是用户可定义的。

当处于引导加载模式时,如果它收到一个FWID信标,其中的应用程序ID代表当前应用程序的更高版本,但具有不同的SoftDevice固件ID,则引导加载程序开始请求SoftDevice更新。在后台模式下,升级策略由用户自定义。
在这里插入图片描述
由于引导加载程序实现的限制,只有当新的SoftDevice能够适应定义的SoftDevice区域时,才能接受SoftDevice DFU传输。

Bootloader firmware ID

Bootloader程序固件ID由一个8位的引导程序ID字段和一个8位的引导程序版本字段组成。就像应用程序一样,引导加载程序可以有不同的配置,每种配置都可以有不同的版本。

在引导加载程序模式下,当引导加载程序ID字段与其当前引导加载程序ID相同并且引导加载程序版本字段大于当前引导加载程序版本时,引导加载程序接受传入的引导加载程序DFU传输。

在后台模式下,升级策略由用户自定义。
在这里插入图片描述

设备页面

所有运行专有mesh DFU Bootloader程序的设备都需要在flash中保存设备页面。设备页定义设备配置,并充当Bootloader程序的操作参数。

设备页面必须在主机上生成,并在部署前在每个设备上闪现。生成设备页面的device_page_generator.py脚本可以在tools/dfu/中找到。

格式

设备页面是设备最后一个flash页面的单页flash管理区域。

内容

设备页包含引导加载程序参与DFU传输所需的所有信息:
① Flash区域:每种传输类型(SoftDevice、bootloader和application)在flash中都有一个指定的区域。这些区域在设备页面中定义,并且必须能够包含其类型中最大可能的固件块。

② 固件ID:每种传输类型(SoftDevice、bootloader和application)都有一个指定的固件ID。这些固件id用于决定是否接受传入的DFU传输,并在每次DFU传输完成后更新。

③ 公共签名密钥:用于验证DFU传输签名。

④ 状态标志:指示每个固件块(SoftDevice、bootloader和application)有效性的标志。

⑤ 分区传输:设备上的每个分区传输都有一个专门的结构来描述它。此条目类型是在每次分区传输成功后由引导加载程序生成的。每种传输类型只能有一个分区。

⑥ 固件签名:每个固件的签名(如果存在)。

下表列出了可能的条目,包括必需的和可选的。在这里插入图片描述

签名公钥:用于签名验证的公钥。
在这里插入图片描述
固件ID:当前固件ID。固件ID条目是三个不同的专有mesh固件ID的连接。
在这里插入图片描述
标志:每个固件的当前状态。
在这里插入图片描述
SoftDevice 区域:SoftDevice 固件可以写入的Flash区域。
在这里插入图片描述
Bootloader 区域:Bootloader 固件可以写入的Flash区域。
在这里插入图片描述
Application 区域:Application 固件可以写入的Flash区域。
在这里插入图片描述
SoftDevice 签名:当前SoftDevice的签名。
在这里插入图片描述
Bootloader 签名:当前Bootloader的签名。
在这里插入图片描述
Application 签名:当前Application的签名。
在这里插入图片描述
SoftDevice 分区:SoftDevice传输分区信息。填充(Padding)是一个固定的数据字段,其值始终为0。
在这里插入图片描述
Bootloader 分区:Bootloader 传输分区信息。填充(Padding)是一个固定的数据字段,其值始终为0。
在这里插入图片描述
Application 分区:Application 传输分区信息。
在这里插入图片描述

相关文章:

蓝牙Mesh专有DFU

蓝牙Mesh专有DFU Mesh专有DFU协议介绍特征DFU模式和类型角色并发传输混合设备的网络传输速率后台操作传输分区内存映射安全DFU固件IDApplication firmware IDSoftDevice firmware IDBootloader firmware ID 设备页面格式内容 Mesh专有DFU协议介绍 设备固件更新(Device Firmwar…...

浅谈综合管廊智慧运维管理平台应用研究

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要:为提升综合管廊运维管理水平,实现管理的数字化转型,采用综合监测系统、BIMGIS 可视化系统、智能机器人巡检、结构安全监测等技术,搭建实时监控、应急管理、数据分析等多功能…...

Httpservletrequest与Httpservletresponse

目录 一、Httpservletrequest 1.1什么是Httpservletrequest 1.2Httpservletrequest中的方法 二、Httpservletresponse 1.1什么是Httpservletresponse 1.2Httpservletresponse的方法 一、Httpservletrequest 1.1什么是Httpservletrequest HttpServletRequest(…...

文件上传之图片码混淆绕过(upload的16,17关)

目录 1.upload16关 1.上传gif loadup17关(文件内容检查,图片二次渲染) 1.上传gif(同上面步骤相同) 2.条件竞争 1.upload16关 1.上传gif imagecreatefromxxxx函数把图片内容打散,,但是不会…...

Jetsonnano B01 笔记5:IIC通信

今日继续我的Jetsonnano学习之路,今日学习的是IIC通信,并尝试使用Jetson读取MPU6050陀螺仪数据。文章提供源码。文章主要是搬运的官方PDF说明,这里结合自己实际操作作笔记。 目录 IIC通信: IIC硬件连线: 安装IIC库文…...

【网络爬虫笔记】爬虫Robots协议语法详解

Robots协议是指一个被称为Robots Exclusion Protocol的协议。该协议的主要功能是向网络蜘蛛、机器人等搜索引擎爬虫提供一个标准的访问控制机制,告诉它们哪些页面可以被抓取,哪些页面不可以被抓取。本文将进行爬虫Robots协议语法详解,同时提供…...

MATLAB 2022b 中设置关闭 MATLAB 之前进行询问

在 MATLAB 2022b 中可以进行设置,在关闭 MATLAB 之前进行询问,防止意外关闭 MATLAB。如图:...

在SpringBoot框架下,接口有读个实现类,在不改变任何源码的情况下,SpringBoot怎么知道给接口注入哪个实现类的依赖呢?

在Spring Boot框架下,当一个接口有多个实现类时,Spring Boot 默认情况下不知道要注入哪个实现类的依赖。因此,你需要使用一些方法来明确告诉Spring Boot应该注入哪个实现类的依赖。 以下是一些常用的方法: 1.使用Qualifier注解&a…...

探索数据库管理的利器 - PHPMyAdmin

有一个项目,后端由博主独自负责,最近需要将项目交接给另一位同事。在项目初期,博主直接在数据库中使用工具创建了相关表格,并在完成后利用PhpMyAdmin生成了一份数据字典,供团队使用。然而,在随后的开发过程…...

大数据技术原理与应用学习笔记第1章

黄金组合访问地址:http://dblab.xmu.edu.cn/post/7553/ 1.《大数据技术原理与应用》教材 官网:http://dblab.xmu.edu.cn/post/bigdata/ 2.大数据软件安装和编程实践指南 官网林子雨编著《大数据技术原理与应用》教材配套大数据软件安装和编程实践指…...

算法从未放弃你,放弃你的只有你自己

在人生的旅程中,我们常常会遇到各种挫折和困难。有些人在面对困境时,会选择放弃,将责任归咎于命运或外部环境。然而,算法教给我们一个重要的道理:永远不要放弃 当我们遇到问题或挑战时,算法可以帮助我们找到…...

[Linux 基础] linux基础指令(1)

文章目录 1、Linux下基本指令1.ls指令2.pwd指令3.cd指令4.touch指令5.mkdir指令6.rmdir指令 && rm指令7.man指令8.cp指令9.mv指令10.cat指令11.more指令12.less指令 Linux学习笔记从今天开始不断更新了。第一篇我们从基础指令开始学起。 1、Linux下基本指令 好多人都说…...

ESP32蓝牙主从站模式:主站发送,从站接收,同时附加简单通信协议

主站发送:WXAiBj,六个字符 蓝牙模式是一个字符一个字符发送 主站和从站设置通信协议 使得六个字符一句话完整接收,同时打印出接收完成信息 硬件电路连接如下: 主从站为两个ESP32,只使用了其中的蓝牙功能 代码如下: 主站: //主机模式 #include <Arduino.h> …...

Redis布隆过滤亿级大数据

场景描述 小程序用户的openid作为最主要的业务查询字段&#xff0c;在做了缓存设计之后仍有非常高频的查询&#xff0c;通过埋点简单统计约在每日1000w次。 其中&#xff1a;由于有新增用户原因&#xff0c;导致请求的openid根本不存在MySQL数据库中&#xff0c;这部分统计约占…...

车联网仿真工具Veins学习1

准备条件 假如你是一个小白&#xff0c;先找到相关的参考资料&#xff08;已根据上一篇博客安装好Veins&#xff09;&#xff0c;主要是官方文档和相关的博客&#xff0c;官方提供了一个example&#xff0c;我找到的资料如下&#xff1a; Frequently Asked Questions (FAQ) O…...

封闭岛屿数量 -- 二维矩阵的dfs算法

1254. 统计封闭岛屿的数目 这道题和 岛屿数量 – 二维矩阵的dfs算法 类似&#xff0c;区别在于不算边缘部分的岛屿&#xff0c;那其实很简单&#xff0c;把上⼀题中那些靠边的岛屿排除掉&#xff0c;剩下的就是「封闭岛屿」了。 关于岛屿的相似题目&#xff1a; 岛屿数量 –…...

C语言_指针(1)

文章目录 前言一、指针数组1.1利用指针数组模拟出二维数组 二、数组指针2.1数组名是数组首元素的地址2.2 二维数组传参2.3 一级指针传参2.4 二级指针传参 三. 函数指针四 . typedef 重命名 前言 指针数组是由指针组成的数组。它的每个元素都是一个指针&#xff0c;可以指向任何…...

建站系列(一)--- 网站基本常识

目录 相关系列文章前言一、因特网二、网站三、服务器四、IP五、域名六、DNS七、Hosts文件八、端口号九、URL十、静态网站十一、动态网站 相关系列文章 建站系列&#xff08;一&#xff09;— 网站基本常识 建站系列&#xff08;二&#xff09;— 域名、IP地址、URL、端口详解 …...

Codeforces Round 895 (Div. 3) A ~ F

Dashboard - Codeforces Round 895 (Div. 3) - Codeforces A 问多少次能使a 和 b相等&#xff0c;就是abs(a - b) / 2除c向上取整&#xff0c;也就是abs(a - b)除2c向上取整。 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #de…...

【前端知识】Axios——请求拦截器模板

Axios——请求拦截器模板 Axios是一个基于Promise的HTTP客户端&#xff0c;用于发送HTTP请求。它可以在浏览器和Node.js环境中使用&#xff0c;并且提供了许多强大的功能&#xff0c;例如拦截请求和响应、转换请求和响应数据、取消请求等。 Axios具有简单易用的API&#xff0c;…...

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

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

XML Group端口详解

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

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...