《Rust权威指南》学习笔记(五)
高级特性
1.在Rust中,unsafe是一种允许绕过Rust的安全性保证的机制,用于执行一些Rust默认情况下不允许的操作。unsafe存在的原因是:unsafe 允许执行某些可能被 Rust 的安全性检查阻止的操作,从而可以进行性能优化,如手动内存管理和优化特定的底层操作;在系统级编程中,通常需要直接操作硬件或底层资源,这些操作超出了 Rust 的安全模型的范围,unsafe 提供了一种方式来实现这些操作;在与不安全的代码库(例如 C 库)进行集成时,unsafe 使得可以调用这些库函数,虽然需要保证这些调用的安全性;允许开发者在受控的、已知的环境中使用不安全的操作,同时通过安全的 API 封装这些操作,确保整个程序的安全性。unsafe并没有关闭借用检查或停用其他安全检查,任何内存相关的错误必须留在unsafe块里,应尽量隔离unsafe代码,最好将其封装在安全的抽象里面,提供安全的API。unsafe块允许执行以下的操作:1解引用原始指针(裸指针):原始指针可以在其他地方创建但只能在unsafe块中使用,原始指针分为可变的*mut T和不可变的*const T(不可变意味着指针在解引用后不能直接对其赋值),此处*是类型名不是解引用,与引用不同,原始指针允许同时存在指向同个位置位置的可变指针来忽略借用规则,原始指针无法保证能指向合理的内存,且允许为null,原始指针不实现任何的自动清理。示例如下:

通过原始指针可以实现与C语言进行接口,构建借用检查器无法理解的安全抽象;2调用unsafe函数或方法:不安全的函数和方法调用是指在 Rust 中标记为 unsafe 的函数或方法,它们可能执行绕过 Rust 安全检查的操作,因此调用时必须在 unsafe 块中进行,以确保开发者意识到潜在风险并手动保证安全性。函数包含unsafe块代码并不意味着需要将整个函数标记为unsafe,将unsafe代码包裹在安全函数中是一个常见的抽象。如下图所示:

可以使用extern关键字调用其他语言的函数或从其他语言调用Rust函数,供其他语言调用的Rust函数需要在fn前面加上extern关键字,还要添加#[no_mangle]注解,避免Rust在编译时改变函数名称,如下图所示:

3访问或修改一个静态变量:在Rust中,静态变量(static变量)是具有'static生命周期的全局变量,这意味着它们在程序的整个运行期间都存在,静态变量可以在程序的任何地方访问,无论是主函数、子函数,还是多个线程之间。静态变量在程序的内存中占有固定位置,通常用于存储不变的数据或需要跨越多个函数或线程访问的数据。Rust中全局变量只能是静态变量,全局的数据还可以是常量const,他俩的区别是:const在编译时求值、不占用内存且总是不可变;static在运行时分配内存,具有'static生命周期,可用static mut使其可变。访问不可变静态变量是安全的,访问和修改可变静态变量是不安全的,不可变静态变量可以在程序的任何地方安全地访问,可变的静态变量 (static mut) 只能在unsafe块中访问。静态变量的名称通常使用全大写字母,如果静态变量的名称由多个单词组成,通常使用下划线_进行分隔。如下图所示:

4实现不安全的trait:当某个trait存在至少一个方法拥有编译器无法校验的不安全因素时,就称这个trait是不安全的,对于不安全的trait需在其前面加上unsafe声明,不安全的trait只能在unsafe块中实现,如下图所示:

2.Self和self的区别:在Rust中,Self代表当前类型,用于类型级别的操作,通常出现在实现构造函数、trait方法以及关联类型时,它表示与当前实现的类型相同的类型,可以在返回类型或关联类型中使用。例如,在impl块中,Self就等同于实现该impl块的具体类型。self则是方法的第一个参数,用于表示当前实例。它在实例方法中使用,用于访问和修改该实例的数据。根据需要,self可以通过值传递(self)、不可变引用(&self)、或可变引用(&mut 0self)的方式传递。
3.关联类型则是在trait中定义的一种占位符类型,具体类型由实现该trait的类型在实现过程中指定。如下图所示:

这种设计可以使trait的接口更简洁,因为使用者不需要在每次调用时都指定类型。关联类型尤其适用于涉及多个相关类型的trait,实现者可以为这些类型设置特定的关系,从而增强trait的表达力和易用性。泛型允许为不同类型多次实现同一个trait,如下图所示:

而关联类型在trait中占位且只能由具体实现者指定具体类型,因此在大多数情况下,一个关联类型只能为一个trait提供唯一的实现。
4.Rust中类型的三种变体(Variance):1协变(covariant):某类型只能用其“子类型”替代。例如,&'static T可以替代&'a T。这是因为'static生命周期比'a长,所以&'static T可以安全地用在需要&'a T的地方;2不变(invariant):类型必须严格提供指定的类型,不能有任何变动。例如,&mut T对于T来说是不变的,意味着必须严格匹配T的类型,不能使用其子类型或超类型;3逆变(contravariant):函数对参数的要求越低,参数可以发挥的作用越大。逆变指的是在某些上下文中,父类型可以替代子类型。逆变通常在函数参数中体现,如果一个函数能够接受一个更一般的类型作为参数,那么这个函数在子类中仍然有效。
5.可以在使用泛型参数时为泛型指定一个默认的具体类型,如标准库中Add定义,其中Rhs=Self表示默认的另一个操作数的类型与函数调用者类型相同:

这种技术通常用于运算符重载,可以在具体实现时不使用默认类型,而重新提供一个类型,如下图:

6.当结构体自身以及结构体实现的接口中都含有同名函数,默认调用的是结构体中的函数,想要调用接口中的函数,可使用下图中的方式:

其中&person指明了是哪个具体类型实现的接口中的函数,但如果这些函数中并没有传入&self,这样调用就会报错了,因为编译器无法知道该调用哪个类型对应的接口实现中的函数,此时可以使用完全限定语法来调用这些同名方法,格式为:<Type as Trait>::function(receiver_if_method,next_arg,…);,如下图所示:

7.可以使用supertrait来要求trait附带其它trait的功能,但需要被依赖的trait也被实现,否则会报错,那个被间接依赖的trait就是当前trait的supertrait,类似于其他语言中继承的概念,如下图所示:
8.可以使用type关键字创建类型别名,如下图所示:

有一个!的特殊类型,称为Never类型,他没有任何值,它在不返回值的函数中充当返回类,如无限循环、抛出错误、或其他导致函数退出的场景,不返回值的函数又称作发散函数。
9.孤儿规则(Orphan Rule)规定了一个trait可以被实现的前提是,trait或实现中的至少一个必须在当前crate中定义,以避免不同crate之间的冲突。但可以使用newtype模式在外部类型上实现外部trait,即利用元组结构体创建一个新的类型,如下图所示,fmt::Display和Vec<String>都是外部的:

Newtype模式通过封装现有类型创建新的类型,优点包括增加类型安全、封装数据、以及扩展功能。它通过定义一个包含现有类型的结构体来实现,这样可以控制对底层数据的访问,同时允许为新类型实现额外的功能或trait。动态大小(Dynamic Size)指的是在编译时无法确定大小的类型,这些类型的大小在程序运行时确定,而不是在编译时,如str类型。Sized trait是一个标记trait,用于指示某个类型的大小在编译时是已知的。默认情况下,Rust中的所有类型都实现了Sized trait,除非它们明确被声明为动态大小类型(DST)。T:?Sized是一个trait bound,用来表示T可以是定长的类型(Sized)或者不定长的类型(?Sized)。这通常用于泛型函数或结构体中,使得它们能够处理不定长类型。宽指针(FatPointer)是Rust中一种包含额外元数据的指针类型,用于引用不定长(?Sized)类型。宽指针不仅包含数据的内存地址,还包括额外的信息,如长度或虚表指针。常见的宽指针包括引用切片(&[T]或&str)和特征对象(&dynTrait)。对于切片,宽指针存储了指向数据的指针和长度;对于特征对象,宽指针存储了数据指针和指向虚表的指针,以便在运行时调用方法。
10.函数指针:可以将函数作为参数传递给其他函数,函数在传递过程中会被强制转换为fn类型,fn类型就是函数指针,如下图所示:

11.Rust中的宏主要分为两类:声明宏和过程宏。声明宏是最常见的一类宏,通过使用 macro_rules!进行定义。它们类似于函数,但操作的是代码而非数据,通常用于生成重复的代码模式。如下图:

过程宏允许通过编写函数来生成代码,它们比声明宏更为灵活和强大,能够进行复杂的代码生成和分析。过程宏可以分为三种类型:自定义派生宏允许为结构体或枚举自动生成代码,通常用于自动实现某些 trait,例如 Debug、Clone等。自定义派生宏通过 #[derive] 属性使用,例如#[derive(Debug)];属性宏允许将宏应用于函数、模块或其他 Rust 项目元素,以实现元编程。它们通过 #[attribute_name] 语法使用; 函数宏类似于声明宏,但它们的输入形式可以更复杂,函数宏可以接受括号内的任意输入,并生成对应的代码。
12.#[repr(transparent)] 是一个用于结构体的属性,它确保该结构体在内存中的布局与其单个字段的类型完全一致。这意味着在 FFI(Foreign Function Interface,外部函数接口)或与 C 语言交互时,Rust编译器不会对该结构体的布局进行任何调整,从而使其与字段的内存布局保持透明。repr(C)和repr(Rust)是Rust中用于控制数据结构内存布局的属性。repr(C)告诉编译器按C语言的内存布局规则排列结构体字段,使其在与C或其他语言的互操作中保持一致。这包括字段的顺序和对齐方式,确保Rust结构体在FFI中可以安全地传递和接收。而repr(Rust)是Rust的默认布局方式,编译器会根据优化的需要对字段进行排列,可能会重新排序字段以减少内存占用或提高访问速度,但这会导致布局不确定,因此不适合FFI。选择repr(C)提供了跨语言的兼容性,而repr(Rust)提供了在Rust内部更好的性能优化。repr(packed)用于紧凑排列结构体字段,忽略默认的内存对齐要求,减少结构体的内存占用,但可能导致未对齐的内存访问问题。repr(align(N)) 是 Rust 中的一个属性,用于将结构体的内存对齐设定为N字节,确保结构体在内存中的地址是 N 的倍数,有助于优化性能。下图示例展示了repr(C)和repr(Rust)的区别:

其他
1.Rust的编译过程始于源代码的解析和词法分析,编译器将源代码分解为标记(tokens),并构建抽象语法树(AST),这反映了代码的语法结构。接着,编译器进行语义分析,主要包括类型检查和借用检查,确保代码遵循Rust的严格类型系统和所有权规则。语义分析阶段还包括将AST转换为中间表示(HIR和MIR),以便进一步优化。在优化阶段,编译器对中间表示(MIR)执行各种优化,如常量折叠和循环展开,以提高代码效率。然后,编译器将优化后的MIR转换为LLVM的中间表示(LLVM IR),并利用LLVM的强大优化功能进一步改进代码,包括寄存器分配和指令合并等。最终,LLVM IR被编译为特定目标架构的机器码,这个阶段生成与硬件相关的二进制指令。编译过程的最后一步是链接,将不同的编译单元合并为最终的可执行文件或库文件。如果启用了增量编译,编译器会缓存中间结果以加快后续编译速度。整体上,Rust的编译过程通过严格的类型检查和多阶段优化,确保生成高效且安全的可执行代码。
2.不同于一些面向语句的编程语言,在Rust里,大多数控制结构(如if、match、loop等)都是表达式,这意味着它们可以返回一个值并直接用于赋值或作为其他表达式的一部分。分号表达式;返回值永远是自身的单元类型:(),分号表达式只有在块表达式的最后一行才会进行求值,其他时候只作为连接符存在。块表达式的值为块中最后一个表达式的值(块中最后一行不加;的表达式),块表达式只对其最后一行表达式进行求值。
3.在Rust中,编译期计算、常量函数和常量泛型是提高代码效率和灵活性的关键概念。它们允许在编译时执行计算,减少运行时开销,并使得代码在编译时就能确定许多值。这对性能至关重要,尤其是在系统编程和嵌入式编程中。常量函数(const fn)是可以在编译时执行的函数,与普通函数不同,const fn可以用于定义常量或在常量表达式中使用,编译器在编译过程中执行这些函数,并将其结果作为常量值嵌入到最终生成的代码中。例如:
const fn square(x: i32) -> i32 { x * x }
const SQUARE_OF_FIVE: i32 = square(5);
square就是一个常量函数,编译器在编译时就计算了SQUARE_OF_FIVE的值,并将其作为常量嵌入到程序中。常量函数还支持嵌套定义,如下图所示:

常量泛型是Rust提供的一种功能,允许在定义泛型类型或函数时使用常量值。通常情况下,泛型参数是类型参数,而常量泛型使得你可以使用整数或其他常量值作为泛型参数。例如:
struct Array<T, const N: usize> { elements: [T; N], }
fn main() {
let arr: Array<i32, 5> = Array { elements: [1, 2, 3, 4, 5] };
}
N是一个常量泛型参数,代表数组的长度。编译器在编译时会处理这个常量,并根据它生成相应的代码。这允许编译时确定数组的大小,而不需要运行时动态分配。
4.位置表达式是指那些能够表示数据的内存位置或地址的表达式,它们通常位于赋值操作的左侧,因为这些表达式可以被赋值,也就是能够“放置”一个值。位置表达式并不直接代表某个具体的值,而是代表某个可以存储值的内存位置。值表达式是指那些直接产生一个值的表达式,与位置表达式不同,值表达式表示的是某个具体的值,而不是一个可以存储值的内存位置。值表达式通常出现在右侧的表达式中,是表达式计算后的结果。常见的位置表达式包括:静态变量的初始化(如static mut LEVELS:u32=0;)、解引用表达式(如*rxpr)、数组索引表达式(形如expr[expr])、字段表达式(形如expr.field)、加上括号的位置表达式(形如(expr))。当位置表达式出现在值表达式的位置时就会发生copy或者所有权的转移。
5.[T]是一种抽象概念,用于描述元素的集合类型,而&[T]是实际使用中的借用类型,允许访问这组元素,切片在处理动态数组或需要获得部分数组的情况下非常有用。
6.NonNull<T>是Rust标准库中的一种指针类型,用于表示一个永远非空的原始指针。这种指针类型确保指针值永远不会是空指针,从而避免了空指针解引用带来的安全问题。但仍可能指向无效内存,因此通常需要在unsafe块中使用。
7.在Rust中,结构体分为具名结构体、元组结构体和单元结构体。具名结构体使用字段名称和类型进行定义,增强代码可读性,适合表示复杂数据结构。例如,struct Person { name: String, age: u32 }。元组结构体类似于元组定义,没有字段名,只有类型和顺序,用于简单封装或类型区分,适合无需复杂命名的简单结构,如struct Color(u8, u8, u8)。单元结构体不包含任何字段,不占用任何内存空间,类似于空元组(),通常用于类型标记或占位符,表示一种类型存在,例如struct Marker。这三种结构体形式满足不同需求,从复杂数据建模到简单类型标识,提供了灵活、多样的定义方式。
8.函数项类型和函数指针类型的主要区别在于语法表示、内存表示、自动转换、捕获环境和性能等方面。前者直接使用函数签名,是编译时确定的值,可以自动转换为后者,并能捕获闭包环境,通常更高效;后者使用fn关键字,是运行时确定的指针,不能自动转换为前者,也不能捕获环境,但可能有一些性能开销。因此,函数项类型更适合编译时确定的函数,函数指针类型更适合运行时确定的函数。
9.在Rust的结构体方法中,使用不同的参数类型会产生不同的效果。如果使用 self 作为参数,则意味着方法会获取结构体的所有权,并将其移动到方法内部,这意味着在方法调用之后,原始的结构体实例将不再可用。相反,如果使用 &self 作为参数,则方法只会借用结构体的引用,而不会获取其所有权,这样可以确保在方法调用之后,原始的结构体实例仍然可以使用。选择合适的参数类型取决于具体的使用场景,如果需要修改结构体的状态,应该使用 self,而如果只需要读取结构体的数据,则应该使用 &self。
相关文章:
《Rust权威指南》学习笔记(五)
高级特性 1.在Rust中,unsafe是一种允许绕过Rust的安全性保证的机制,用于执行一些Rust默认情况下不允许的操作。unsafe存在的原因是:unsafe 允许执行某些可能被 Rust 的安全性检查阻止的操作,从而可以进行性能优化,如手…...
GitHub的简单操作
引言 今天开始就要开始做项目了,上午是要把git搭好。搭的过程中遇到好多好多的问题。下面就说一下git的简单操作流程。我们是使用的GitHub,下面也就以这个为例了 一、GitHub账号的登录注册 https://github.com/ 通过这个网址可以来到GitHub首页 点击中间绿色的S…...
「Mac畅玩鸿蒙与硬件54」UI互动应用篇31 - 滑动解锁屏幕功能
本篇教程将实现滑动解锁屏幕功能,通过 Slider 组件实现滑动操作,学习事件监听、状态更新和交互逻辑的实现方法。 关键词 滑动解锁UI交互状态管理动态更新事件监听 一、功能说明 滑动解锁屏幕功能包含以下功能: 滑动解锁区域:用…...
SMMU软件指南之系统架构考虑
安全之安全(security)博客目录导读 目录 5.1 I/O 一致性 5.2 客户端设备 5.2.1 地址大小 5.2.2 缓存 5.3 PCIe 注意事项 5.3.1 点对点通信 5.3.2 No_snoop 5.3.3 ATS 5.4 StreamID 分配 5.5 MSI 本博客介绍与 SMMU 相关的一些系统架构注意事项。 5.1 I/O 一致性 如…...
使用高云小蜜蜂GW1N-2实现MIPI到LVDS(DVP)转换案例分享
作者:Hello,Panda 大家晚上好,熊猫君又来了。 今天要分享的是一个简单的MIPI到LVDS(DVP)接口转换的案例。目的就是要把低成本FPGA的应用潜力充分利用起来。 一、应用背景 这个案例的应用背景是:现在还在…...
「C++笔记」unordered_map:哈希化的无序映射函数(键值对)
unordered_map 是 C 中一个经过哈希函数(Hash)处理的映射(map)容器。 本文中的map和set是差不多的,unordered_map与unordered_set也是对应的。所以不再单独写一篇了。 这里的内容建议看完本文之后再回过头来看 二者虽然…...
Linux 安装jdk
1、官网下载jdk https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html2、以tar包为例,在window或者Linux解压都可以,这里直接在win解压了,上传到服务器 3、在/usr/local/ 创建jdk目录,将jdk上传到…...
asp.net core 发布到iis后,一直500.19,IIS设置没问题,安装了sdk,文件夹权限都有,还是报错
原因就是没有安装ASP.NET Core 9.0 Runtime (v9.0.0) - Windows Hosting Bundle,我是只安装了.net core的sdk,下面介绍下sdk和hosting bundle的关系 在 .NET Core 和 ASP.NET Core 的开发中,SDK(Software Development Kit&#x…...
【Go】运行自己的第一个Go程序
运行自己的第一个Go程序 一、Go语言的安装Go环境安装查看是否安装成功配置GOPROXY(代理) 二、Goland安装三、Goland破解四、新建项目 开一篇专栏记录学习Go的过程,一门新语言从hello world开始,这篇文章详细讲解Go语言环境搭建及hello world实现 一、Go语…...
qt qss文件的使用
qt样式的修改方式 一 通过ui界面的改变样式表来直接修改显示效果。 不推荐,其他人不好修改,不够直观,不易维护。 二 通过setStyleSheet接口修改。 一般,界面很少的时候可以使用。一旦界面多起来,代码部分就显得杂乱…...
【管道——二分+区间合并】
题目 思路 区间合并 1、按照左端点排序2、遍历窗口,若窗口非法,继续遍历;否则执行33、若是第一个窗口,设定合并结果初值,判断结果左端点是否造成“起点过大”,是,FALSE退出;否则执行…...
宽带、光猫、路由器、WiFi、光纤之间的关系
1、宽带(Broadband) 1.1 宽带的定义宽带指的是一种高速互联网接入技术,通常包括ADSL、光纤、4G/5G等不同类型的接入方式。宽带的关键特点是能够提供较高的数据传输速率,使得用户可以享受到稳定的上网体验。 1.2 宽带的作用宽带是…...
如何排查 Apache Doris 中 “Failed to commit txn“ 导入失败问题?
今天来聊聊 Doris 数据导入那些事儿。你是不是在数据导入的时候遇到各种状况,让人头疼不已?别担心,这篇文章给你答案! 在 Doris 的版本里,< 2.0.3 的时候,数据迁移存在一些已知的问题,比如可…...
回归预测 | MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测
回归预测 | MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-GRU卷积门控循环单元多输入单输出回归预测 数据准备&#x…...
HCIA-Access V2.5_7_3_XG(S)原理_关键技术
为什么需要测距 因为上行链路只有一根纤,而且每一个ONU到OLT的距离是不一样的,虽然上行通过TDMA技术,让每一个ONU在不同的时间段发送数据,但是仍然有可能在同一时刻到达分光器,产生数据冲突。 有测距的信元传输 所以为了避免碰撞冲突,通过ONU在注册的时候就会启动测距…...
leetcode hot 100 不同路径
62. 不同路径 已解答 中等 相关标签 相关企业 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )…...
智慧工地解决方案 1
建设背景与挑战 工地施工现场环境复杂,人员管理难度大,多工种交叉作业导致管理混乱,事故频发。传统管理方式难以实现科学、有效、集中式的管理,特别是在环境复杂、地点分散的情况下,监管困难,取证复杂。施…...
LeetCode -Hot100 - 53. 最大子数组和
前言 本专栏主要通过“LeetCode 热题100”,来捡起自己本科阶段的算法知识与技巧。语言主要使用c/java。如果同样正在练习LeetCode 热题100的朋友欢迎关注或订阅本专栏。有疑问欢迎留言交流~ 题目描述 题目链接 示例 1: 输入:nums [-2,1…...
php 多进程那点事,用 swoole 如何解决呢 ?
在 PHP 中,多进程的处理通常会遇到一些挑战,比如资源共享、进程间通信、性能优化等。Swoole 是一个高性能的协程和多进程框架,旨在为 PHP 提供异步、并发、协程等功能,解决了传统 PHP 环境中的多进程管理问题。通过使用 Swoole&am…...
探索AI在地质科研绘图中的应用:ChatGPT与Midjourney绘图流程与效果对比
文章目录 个人感受一、AI绘图流程1.1 Midjourney(1)环境配置(2)生成prompt(3)完善prompt(4)开始绘图(5)后处理 1.2 ChatGPT不合理的出图结果解决方案 二、主题…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
