当前位置: 首页 > 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;…...

企业架构LNMP学习笔记16

基于IP的访问控制&#xff1a; 基于ngx_http_access_module模块&#xff0c;默认可使用。 语法是&#xff1a; deny ip 禁止IP访问 allow ip 允许IP访问 上面是允许的&#xff0c;下面是deny的。 老师建议写在server段中是比较合适的。 基于用户的访问控制&#xff1a; …...

redis实现消息队列

背景 消息队列&#xff08;Message Queue&#xff09;是一种常见的软件架构模式&#xff0c;用于在分布式系统中传递和处理异步消息。它解耦了发送消息的应用程序和接收消息的应用程序之间的直接依赖关系&#xff0c;使得消息的发送者和接收者可以独立地演化和扩展。 消息队列…...

JVM指令集

概述 JVM&#xff0c;Java Virtual Machine&#xff0c;Java虚拟机器&#xff0c;作为一台独立的机器&#xff0c;一般包括独立的指令集、独立的存储体系以及适合机器自身的运算方式&#xff0c;本章节主要是描述JVM指令的功能与作用。 JVM的每个指令的格式是【指令 操作数1操…...

如何用SSH克隆GitHub项目

诸神缄默不语-个人CSDN博文目录 使用场景&#xff1a;由于不可知的网络问题&#xff0c;无法用HTTPS克隆GitHub项目。 报错fatal: unable to access https://github.com/PolarisRisingWar/llm-throught-ages.git/: GnuTLS recv error (-110): The TLS connection was non-pro…...

sqlx库使用指南

sqlx库使用指南 在项目中我们通常可能会使用database/sql连接MySQL数据库。本文借助使用sqlx实现批量插入数据的例子&#xff0c;介绍了sqlx中可能被你忽视了的sqlx.In和DB.NamedExec方法。 sqlx介绍 在项目中我们通常可能会使用database/sql连接MySQL数据库。sqlx可以认为是Go…...

算法篇汇总

文章浏览 I https://leetcode.cn/problems/article-views-i/description/?envTypestudy-plan-v2&envId30-days-of-pandas&langpythondata 我的题解&#xff1a; import pandas as pddef article_views(views: pd.DataFrame) -> pd.DataFrame:dfviews[views[auth…...

typeScript 学习笔记(二)

类接口 TypeScript 入门教程 (xcatliu.com) 十四.类 ① 类 类&#xff1a;定义了一件事物的抽象特点&#xff0c;包含它的属性和方法对象&#xff1a;类的实例&#xff0c;通过new生成面向对象&#xff08;OOP&#xff09;的三大特性&#xff1a;封装、继承、多态封装&…...

redis集群架构详解

一、集群架构搭建 1、配置 在一台机器上模拟多台机器搭建redis集群&#xff0c;一个集群代表一台物理机 集群1路径&#xff1a; /usr/local/redis/redis-cluster/cluster1/9001/redis.conf/usr/local/redis/redis-cluster/cluster1/9004/redis.conf/usr/local/redis/redis-…...

nodejs设置镜像

1、npm镜像地址配置 -- 查看 npm 安装目录 npm root -g-- 查看 npm 配置信息 npm config list-- 查询当前镜像配置 npm get registry-- 或者仅修改 npm 命令镜像 -- 设置为淘宝镜像 npm config set registry https://registry.npmmirror.com -- 修改为官方镜像 npm config set…...

CSS中如何在table中隐藏表格中从第4个开始的多个 <tr> 元素

隐藏指定行 使用 CSS 的 nth-child 选择器来选择表格中的特定行&#xff0c;并隐藏它们。 以下是一个示例 CSS 规则&#xff0c;用于隐藏表格中的第 4 个和第 5 个行&#xff08;索引从 1 开始&#xff09;&#xff1a; table tr:nth-child(4), table tr:nth-child(5) {displ…...