【小沐学Rust】Rust实现TCP网络通信

news2024/11/14 6:19:21

文章目录

  • 1、简介
  • 2、安装
    • 2.1 安装Rust
    • 2.2 VsCode安装Rust插件
  • 3、快速入门
    • 3.1 命令行构建
    • 3.2 Cargo构建
    • 3.3 Cargo添加依赖
  • 4、基本语法
    • 4.1 main 的函数
    • 4.2 代码缩进
    • 4.3 todo! 宏
    • 4.4 println! 宏
    • 4.5 变量的使用
    • 4.6 元组
    • 4.7 结构
    • 4.8 枚举
    • 4.9 函数
  • 5、TCP通信
    • 5.1 测试一
      • 5.1.1 TCP服务端
      • 5.1.2 TCP客户端
    • 5.2 测试二
      • 5.2.1 TCP服务端
      • 5.2.2 TCP客户端
  • 结语

1、简介

Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。
Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。
在这里插入图片描述

  • Rust的用途
    开发人员可在网络软件(如 Web 服务器、邮件服务器和 Web 浏览器)领域使用 Rust。 Rust 的身影还会出现在编译器和解释器、虚拟化和软件容器、数据库、操作系统和加密中。 你还可以利用 Rust 编译适用于嵌入式设备的游戏、命令行程序、Web 程序集程序和应用程序。

  • Rust的内存机制
    Rust 是现有系统软件语言(如 C 和 C++)的一种安全替代语言。 与 C 和 C++ 一样,Rust 没有大型运行时或垃圾回收器,这几乎与所有其他现代语言形成了鲜明对比。 但是,与 C 和 C++ 不同的是,Rust 保证了内存安全。 Rust 可以避免很多与在 C 和 C++ 中遇到的内存使用错误相关的 bug。

  • Rust的特点

    • 类型安全:编译器可确保不会将任何操作应用于错误类型的变量。
    • 内存安全:Rust 指针(称为“引用”)始终引用有效的内存。
    • 无数据争用:Rust 的 borrow 检查器通过确保程序的多个部分不能同时更改同一值来保证线程安全。
    • 零成本抽象:Rust 允许使用高级别概念,例如迭代、接口和函数编程,将性能成本控制在最低,甚至不会产生成本。 这些抽象的性能与手工编写的底层代码一样出色。
    • 最小运行时:Rust 具有极小的可选运行时。 为了有效地管理内存,此语言也不具有垃圾回收器。 在这一点上,Rust 非常类似于 C 和 C++ 之类的语言。
    • 面向裸机:Rust 可以用于嵌入式和“裸机”编程,因此适合用于编写操作系统内核或设备驱动程序。
  • Rust的编译器
    虽然可以直接使用 Rust 编译器 (rustc) 来生成箱,但大多数项目都使用 Rust 生成工具和名为 Cargo 的依赖项管理器。
    Cargo 可以为你做许多事情,包括:

    • 使用 cargo new 命令创建新的项目模板。
    • 使用 cargo build 编译项目。
    • 使用 cargo run 命令编译并运行项目。
    • 使用 cargo test 命令测试项目。
    • 使用 cargo check 命令检查项目类型。
    • 使用 cargo doc 命令编译项目的文档。
    • 使用 cargo publish 命令将库发布到 crates.io。
    • 通过将箱的名称添加到 Cargo.toml 文件来将依赖箱添加到项目。
  • Rust的在线开发环境:
    操场是 Rust 开发的 IDE,可在 https://play.rust-lang.org/ Internet 上访问。 任何人都可以访问操场。 你可以编写代码,然后在相同的环境中编译和运行代码。 以下屏幕截图显示了操场环境。 在工具栏的最右侧,“CONFIG”菜单的选项可以设置环境的首选项。

https://play.rust-lang.org/ 

浏览器运行如下:
在这里插入图片描述

2、安装

2.1 安装Rust

https://www.rust-lang.org/zh-CN/tools/install

在Windows上要使用 Rust,请下载安装器,然后运行该程序并遵循屏幕上的指示。当看到相应提示时,您可能需要安装 Microsoft C++ 生成工具。如果您不在 Windows 上,参看 “其他安装方式”。
在这里插入图片描述
下载rustup-init.exe,鼠标直接双击运行之。
(1)选择安装方式
在这里插入图片描述
(2)rust下载中
在这里插入图片描述
(3)rust安装完成
在这里插入图片描述
(4)查看rust版本号

rustc -V
# rustc --version

在这里插入图片描述

cargo --version

在这里插入图片描述

2.2 VsCode安装Rust插件

(1)rust-analyzer:它会实时编译和分析你的 Rust 代码,提示代码中的错误,并对类型进行标注。你也可以使用官方的 rust 插件取代。
rust-analyzer是官方维护的rls(rust语言服务器)2.0版本,已有VSCode插件。

Rust:这是官方开发的;
rust-analyzer:这是社区开发的;

在这里插入图片描述
(2)rust syntax:为代码提供语法高亮。
在这里插入图片描述
(3)Dependi:帮助你分析当前项目的依赖是否是最新的版本。
在这里插入图片描述
(4)Even Better TOML:Rust 使用 toml 做项目的配置管理。
在这里插入图片描述

(5)Tabnine:基于 AI 的自动补全,可以帮助你更快地撰写代码。
在这里插入图片描述

3、快速入门

3.1 命令行构建

  • (1)创建一个新目录
    对于 Windows 命令提示符,请运行以下命令:
mkdir hello-yxy
cd hello-yxy

在这里插入图片描述

  • (2)编写你的第一个 Rust 程序

创建一个名为 main.rs 的新文件,并使用编辑器将以下代码写入其中:

fn main() {
	println!("Hello, yxy!");
}

在 Windows 中可以运行以下命令:

rustc main.rs
.\main.exe

在这里插入图片描述

3.2 Cargo构建

-(3)使用 Cargo 创建项目
让我们使用 Cargo 编写并运行相同的程序。
并运行以下命令:

cargo new hello-cargo-yxy

命令如下:
在这里插入图片描述
此命令生成名为 hello-cargo 的新目录(其中包含 src 子目录),并自动添加两个文件:

hello-cargo/
     Cargo.toml
     src/
         main.rs

在这里插入图片描述
Cargo.toml 是 Rust 的清单文件。 这个文件可用于保存项目及依赖项的元数据。
Src 子目录中的 main.rs 文件可用于编写应用程序代码。

-(4)使用 Cargo 生成并运行你的程序
使用 cargo run 命令:

cd hello-cargo-yxy
cargo run

在这里插入图片描述

3.3 Cargo添加依赖

  • 新建项目

Cargo 创建一个新项目:

cargo new hello-rust

这会生成一个名为 hello-rust 的新目录,其中包含以下文件:

hello-rust
|- Cargo.toml
|- src
  |- main.rs

Cargo.toml 为 Rust 的清单文件。其中包含了项目的元数据和依赖库。
src/main.rs 为编写应用代码的地方。

  • 代码编译
cargo run
  • 添加依赖
    为应用添加依赖。您可以在 crates.io,即 Rust 包的仓库中找到所有类别的库。在 Rust 中,我们通常把包称作“crates”。

Ferris-Says是一个Rust编程语言编写的库,它以Rust的标志性吉祥物Ferris为特色,帮助你在命令行中打印出带有Ferris元素的文本。不仅如此,该项目还提供了一个名为fsays的二进制工具,使用户可以方便地从标准输入或文件读取文本,并以独特的ASCII艺术风格呈现。

在 Cargo.toml 文件中添加以下信息(从 crate 页面上获取):

[dependencies]
ferris-says = "0.3.1"

Cargo 就会安装该依赖。

cargo build

在这里插入图片描述
运行此命令会创建一个新文件 Cargo.lock,该文件记录了本地所用依赖库的精确版本。

打开 main.rs,添加示意代码:

use ferris_says::say; // from the previous step
use std::io::{stdout, BufWriter};

fn main() {
    let stdout = stdout();
    let message = String::from("Hello fellow Rustaceans!");
    let width = message.chars().count();

    let mut writer = BufWriter::new(stdout.lock());
    say(&message, width, &mut writer).unwrap();
}

运行代码:

cargo run

在这里插入图片描述

4、基本语法

4.1 main 的函数

每个 Rust 程序都必须有一个名为 main 的函数。 main 函数中的代码始终是 Rust 程序中运行的第一个代码。 我们可以从 main 函数内部或从其他函数内部调用其他函数。

fn main() {
    println!("Hello, world!");
}

4.2 代码缩进

在函数体中,大多数代码语句以分号 ; 结尾。
起始代码语句从左边距缩进四个空格。

4.3 todo! 宏

Rust 中的宏类似于采用可变数量的输入参数的函数。 todo! 宏用于标识 Rust 程序中未完成的代码。

fn main() {
    // Display the message "Hello, world!"
    todo!("Display the message by using the println!() macro");
}

在这里插入图片描述

4.4 println! 宏

println! 宏需要一个或多个输入参数,这些参数会显示在屏幕或标准输出中。

fn main() {
    // Our main function does one task: show a message
    // println! displays the input "Hello, world!" to the screen
    println!("Hello, world!");
}

在这里插入图片描述

  • {} 参数的值替换
    println! 宏将文本字符串中的每个大括号 {} 实例替换为列表中下一个参数的值。
fn main() {
    // Call println! with three arguments: a string, a value, a value
    println!("The first letter of the English alphabet is {} and the last letter is {}.", 'A', 'Z');
}

在这里插入图片描述

4.5 变量的使用

在 Rust 中,变量用关键字 let 声明。
每个变量都有一个唯一的名称。
在 Rust 中,变量绑定默认不可变。

// Declare a variable
// let a_number;
let a_number = 10;
 
// Declare a second variable and bind the value
let a_word = "Ten";
    
// Bind a value to the first variable
a_number = 10;

println!("The number is {}.", a_number);
println!("The word is {}.", a_word);

若要更改值,必须先使用 mut 关键字将变量绑定设为可变。

// The `mut` keyword lets the variable be changed
let mut a_number = 10; 
println!("The number is {}.", a_number);

// Change the value of an immutable variable
a_number = 15;
println!("Now the number is {}.", a_number);

将 number 变量创建为 32 位整数。

let number: u32 = 14;
println!("The number is {}.", number);

Rust 附带一些用于表达数字、文本和真实信息的内置基元数据类型。
内置数据类型:

整数数字
浮点数
布尔型
字符
  • (1)数字:整数和浮点值。
let number_64 = 4.0;      // compiler infers the value to use the default type f64
let number_32: f32 = 5.0; // type f32 specified via annotation
  • (2)布尔值:True 或 false
// Declare variable to store result of "greater than" test, Is 1 > 4? -- false
let is_bigger = 1 > 4;
println!("Is 1 > 4? {}", is_bigger);  
  • (3)文本:字符和字符串

字符:

let uppercase_s = 'S';
let lowercase_f = 'f';
let smiley_face = '😃';

字符串:

// Specify the data type "char"
let character_1: char = 'S';
let character_2: char = 'f';
   
// Compiler interprets a single item in quotations as the "char" data type
let smiley_face = '😃';

// Compiler interprets a series of items in quotations as a "str" data type and creates a "&str" reference
let string_1 = "miley ";

// Specify the data type "str" with the reference syntax "&str"
let string_2: &str = "ace";

println!("{} is a {}{}{}{}.", smiley_face, character_1, string_1, character_2, string_2);

4.6 元组

元组是集中到一个复合值中的不同类型值的分组。 元组中的各个值称为元素。 这些值指定为括在括号中的逗号分隔列表 (, , …)。

// Declare a tuple of three elements
let tuple_e = ('E', 5i32, true);

// Use tuple indexing and show the values of the elements in the tuple
println!("Is '{}' the {}th letter of the alphabet? {}", tuple_e.0, tuple_e.1, tuple_e.2);

4.7 结构

结构是多个其他类型的组合体。 结构中的元素称为字段。 与元组一样,结构中的字段可以具有不同的数据类型。 结构类型的一个显著好处是,可以命名每个字段,以便清楚展示相应值的含义。

// Classic struct with named fields
struct Student { name: String, level: u8, remote: bool }

// Tuple struct with data types only
struct Grades(char, char, char, char, f32);

// Unit struct
struct Unit;

实例化结构:

// Instantiate classic struct, specify fields in random order, or in specified order
let user_1 = Student { name: String::from("Constance Sharma"), remote: true, level: 2 };
let user_2 = Student { name: String::from("Dyson Tan"), level: 5, remote: false };

// Instantiate tuple structs, pass values in same order as types defined
let mark_1 = Grades('A', 'A', 'B', 'A', 3.75);
let mark_2 = Grades('B', 'A', 'A', 'C', 3.25);

println!("{}, level {}. Remote: {}. Grades: {}, {}, {}, {}. Average: {}", 
         user_1.name, user_1.level, user_1.remote, mark_1.0, mark_1.1, mark_1.2, mark_1.3, mark_1.4);
println!("{}, level {}. Remote: {}. Grades: {}, {}, {}, {}. Average: {}", 
         user_2.name, user_2.level, user_2.remote, mark_2.0, mark_2.1, mark_2.2, mark_2.3, mark_2.4);

4.8 枚举

与结构一样,枚举变体可以具有命名字段,但也可以具有没有名称的字段或根本没有字段。 与结构类型一样,枚举类型也采用大写形式。

enum WebEvent {
    // An enum variant can be like a unit struct without fields or data types
    WELoad,
    // An enum variant can be like a tuple struct with data types but no named fields
    WEKeys(String, char),
    // An enum variant can be like a classic struct with named fields and their data types
    WEClick { x: i64, y: i64 }
}

4.9 函数

  • (1)定义函数
    Rust 中的函数定义以 fn 关键字开头。
fn main() {
    println!("Hello, world!");
    goodbye();
}

fn goodbye() {
    println!("Goodbye.");
}

在这里插入图片描述

  • (2)传递输入参数
    如果函数具有输入参数,请命名每个参数并在函数声明的开头指定数据类型。
fn goodbye(message: &str) {
    println!("\n{}", message);
}

fn main() {
    let formal = "Formal: Goodbye.";
    let casual = "Casual: See you later!";
    goodbye(formal);
    goodbye(casual);
}

在这里插入图片描述

  • (3)返回值

当函数返回某个值时,请在函数参数列表后面和函数体的左大括号前面添加语法 -> 。

fn divide_by_5(num: u32) -> u32 {
    num / 5
}

fn main() {
    let num = 25;
    println!("{} divided by 5 = {}", num, divide_by_5(num));
}

在这里插入图片描述

5、TCP通信

5.1 测试一

5.1.1 TCP服务端

use std::net::TcpListener;

fn main() {
    // 监听地址: 127.0.0.1:9090
    let listener = TcpListener::bind("127.0.0.1:9090").unwrap();

    for stream in listener.incoming() {
        let stream = stream.unwrap();

        println!("Connection established!");
    }
}

在这里插入图片描述

5.1.2 TCP客户端

fn main() {
    use std::net::TcpStream;

    if let Ok(stream) = TcpStream::connect("127.0.0.1:9090") {
        println!("Connected to the server!");
    } else {
        println!("Couldn't connect to server...");
    }
}

在这里插入图片描述

5.2 测试二

5.2.1 TCP服务端

//
//@dev server用于监听
//
use std::net::{TcpListener, TcpStream}; 
use std::thread;
//std::thread库的引入,对输入的每一个流创建一个线程
use std::time;
use std::io::{self, Read, Write};
//引入io库,为了处理错误

fn handle_client(mut stream: TcpStream) -> io::Result<()> {
    //该函数用来处理client(就是这个流),流的格式或者说他的类型就是TcpStream
    let mut buf = [0; 512];
    //创建一个叫buf的数组,内容为0,长度为512
    loop {
        //该循环表示server端永久提供服务,因为默认服务器为永不关闭的
        let bytes_read = stream.read(&mut buf)?;
        //从流里面读内容,读到buf中
        if bytes_read == 0 {
            return Ok(());
            //如果读到的为空(即0),则说明已经结束了
        }
        stream.write(&buf[..bytes_read])?;
        //否则把它写回去
        thread::sleep(time::Duration::from_secs(1));
        //调用sleep函数实现服务的间隔,间隔1s
        let s = String::from_utf8(buf.to_vec());
        println!("receive data from client: {:?}", &s);
    }
}

fn main() -> io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080")?;
    //定义一个listener,bind函数里面填写的是监听的的ip与端口号,?是一种简写,等价于except,unwrap
    let mut thread_vec: Vec<thread::JoinHandle<()>> = Vec::new();
    //创建一个容器,用来放线程的句柄
    for stream in listener.incoming() {
        println!("new client is connected.");
        let stream = stream.expect("failed");
        //转换一下stream流,出现问题,提示“失败”,没有问题,继续下面的操作
        let handle = thread::spawn(move || {
            handle_client(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
        });
        //对输入的每一个流来创建一个线程,利用必包进行一个处理
        thread_vec.push(handle);
        //把handle加到容器里面
    }

    for handle in thread_vec {
        //此循环为了等待线程的结束
        handle.join().unwrap();
        //等待结束的具体实现
    }
    Ok(())
}

在这里插入图片描述

5.2.2 TCP客户端

//
//@dev server端进行监听,在client端发起链接
//
use std::io::{self, prelude::*, BufReader, Write};
use std::net::TcpStream;
use std::str;

fn main() -> io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:8080")?;
    //创建变量stream,直接连接sever端
    for _ in 0..10 {
        println!("please input:");
        let mut input = String::new();
        //定义一个String类型的输入
        io::stdin().read_line(&mut input).expect("Failed to read!");
        //从标准输入读入一行,读入input里面,如果有问题的话,提示“读取失败”
        stream.write(input.as_bytes()).expect("Failed to write!");
        //把input读取的内容,转换成bytes后,写到stream流里面去,如果写入失败,提示“写入失败”

        let mut reader = BufReader::new(&stream);
        //从stream流创建一个读,目的是要从我们的server端读,
        let mut buffer: Vec<u8> = Vec::new();
        //用Vector创建一个buffer变量 
        reader.read_until(b'\n', &mut buffer).expect("Failed to read into buffer");
        //一直读到换行为止(b'\n'中的b表示字节),读到buffer里面
        println!("read from server: {}", str::from_utf8(&buffer).unwrap());
        //把读取到buffer中的内容打印出来
        println!("");
        //再来一个换行,美化输出
    }
    Ok(())
}

在这里插入图片描述

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

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

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

相关文章

【HTML】模拟二级菜单【附源代码】

模拟二级菜单 HTML部分&#xff1a; <!DOCTYPE html>: 声明文档类型为HTML5。<html>: HTML文档的根元素。<head>: 包含文档的元数据&#xff0c;如字符集、标题和样式。 <meta charset"utf-8">: 设置文档的字符编码为UTF-8。<title>:…

企业级web应用服务器之Tomcat

Tomcat介绍 Tomcat 由 Apache 软件基金会下属的 Jakarta 项目开发。它实现了 Java Servlet 和 JavaServer Pages&#xff08;JSP&#xff09;等 Java EE 技术规范&#xff0c;为基于 Java 的 Web 应用程序提供运行环境。 主要特点 轻量级 Tomcat 是一个相对轻量级的服务器&am…

浏览器解析流程

思考 不知道大家是否思考过这几个问题&#xff1a; 当我们在浏览器中输入url后&#xff0c;到底发生了什么&#xff1f; h5&#xff0c;css&#xff0c;js代码执行的顺序是什么&#xff1f;什么情况下会阻塞页面&#xff1f;又有什么办法可以提高页面响应速度呢&#xff1f; 如…

再说圆的面积

在微积分-圆的面积和周长(1)介绍微积分方法求解圆的面积&#xff0c;本文使用蒙特卡洛方法求解圆面积。 取&#xff08;0&#xff0c;1&#xff09;* (0,1)区间&#xff0c;也就是单位圆第一象限的端点区间对应的正方形区间。下面是计算机给出的结果&#xff1a;很显然第一象限…

基于vue3和audio封装的简易音频播放器

样式如图所示 <template><div class"audio-player"><div class"player_top" flex-ac flex-justify-between ><div class"fileName genericTitle" fs-28 l-height-32 height-64 pr-42 flex-ac><span class"t…

UE管理内容 —— FBX Morph Target Pipeline

目录 Naming Setting Up Morph Targets Importing Morph Targets Morph Target 是特定网格体的顶点位置的快照&#xff0c;该网格体在某种程度上已经变形&#xff1b;例如&#xff0c;可以使用一个角色模型&#xff0c;对其面部进行重塑以创建一个面部表情&#xff0c;然后将…

Linux 流式DMA映射(DMA Streaming Mapping)

流式DMA相关的接口为dma_map_sg(),dma_unmap_sg(),dma_map_single(),dma_unmap_single()。流式DMA一般用于已经分配好的内存&#xff0c;然后再对其进行DMA操作&#xff0c;而不是提前申请好一块cache一致性的内存给DMA用。例如从协议栈里发下来的一个包&#xff0c;想通过网卡…

day41| 01背包问题一 01背包问题二(滚动数组篇)416. 分割等和子集 1049.最后一块石头的重量II 494. 目标和 474. 一和零

文章目录 背景介绍01背包问题一思路方法一方法二01背包问题二(滚动数组篇)思路方法一方法二416. 分割等和子集思路方法一1049.最后一块石头的重量II思路方法一494. 目标和思路方法方法二 回溯法474. 一和零思路方法总结由于笔试的时候会判重,而这里面的代码都是我自己写的,…

不同场景下的负载均衡器

负载均衡主要用于分配来自互联网或局域网的请求或任务负载到多个服务器中。 这样做可以避免任何单个服务器的过载&#xff0c;提高响应速度&#xff0c;增加系统的整体处理能力&#xff0c;并确保系统的高可用性和可靠性。 负载均衡器大概可以分为 3 类&#xff0c;包括&#…

Argo/BGC-Argo数据下载

BGC-Argo官方网站 网址&#xff1a; https://biogeochemical-argo.org/data-access.php 信息&#xff1a;提供BGC-Argo位置及剖面预览 数据ftp下载&#xff1a;ftp://ftp.ifremer.fr/ifremer/argo 或者 ftp://usgodae.org/pub/outgoing/argo 问题&#xff1a;dac文件按照数据中…

Image Stride(内存图像行跨度)

When a video image is stored in memory, the memory buffer might contain extra padding bytes after each row of pixels. The padding bytes affect how the image is store in memory, but do not affect how the image is displayed. 当视频图像存储在内存时&#xff0…

EVE-NG安装部署使用

EVE-NG安装部署使用 一、EVE的虚拟化安装1、下载EVE-NG(社区版)2、导入虚拟机-配置-登录二、EVE中设备的连接sercureCRT连接wireshark连接一、EVE的虚拟化安装 1、下载EVE-NG(社区版) 官网下载地址(科学上网): https://www.eve-ng.net/index.php/download/ 中文网下载…

基于python django的图书数据分析系统,包括图书推荐和可视化大屏分析,带有后台

研究背景 随着数字化技术的发展&#xff0c;图书管理与数据分析在图书馆和在线图书销售平台中变得越来越重要。传统的图书管理方式通常只关注图书的借阅和归还&#xff0c;忽视了数据分析在图书管理中的潜力。通过对图书借阅、购买、和用户偏好等数据的分析&#xff0c;能够深入…

数论之组合数

组合数1&#xff1a; 预处理每一个组合数的大小 类似于dp&#xff0c;从a个苹果里面选b个出来&#xff1a;首先从a个苹果里面拿出来一个&#xff0c;这样就分成了两种&#xff0c;一种是包括这个拿出来的苹果的方案数&#xff0c;此时就只需要拿b-1个苹果。一种是不包括这种苹…

嵌入式笔记:半加器与全加器

一&#xff0c;门电路 本文使用digital软件中的双掷继电器来实现以下的门电路&#xff0c;并结合这些门电路实现半加器与八位全加器。 与门 当输入信号A&#xff0c;B都置高电平时&#xff0c;继电器带电具有磁性&#xff0c;将下方双刀开关吸附&#xff0c;使电路导通。在输出…

C++20中的约束与概念

类模板、函数模板和非模板函数(通常是类模板的成员)可能与约束(constraint)相关联&#xff0c;该约束指定对模板参数的要求(requirements)&#xff0c;可用于选择最合适的函数重载和模板特化。约束是使用模板时需要通过模板参数满足的条件或要求。这些要求的命名集合称为概念(c…

Ai+若依(系统接口--Swagger):04篇

Swagger&#xff0c;能够自动生成 API 的同步在线文档&#xff0c;并提供Web界面进行接口调用和测试。 可以直接去测试&#xff1a;--有的接口测试需要权限 我们可以去这样操作 F12 报错404 是因为多了个前缀 /dev-api 我们去后台删掉&#xff1a; 重启刷新&#xff1a;

day 39 代码随想录 | 打家劫舍 动态规划

198.打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个…

Ruby+Watir进行web UI自动化测试

1.新建工程文件 打开RubyMine&#xff0c;新建一个工程文件目录如下&#xff1a; login_mail.rb文件 # encoding:UTF-8 # frozen_string_literal: true当(/^打开谷歌浏览器&#xff0c;进入163邮箱登陆页面$/) do$driver Watir::Browser.new :chromesleep(2)$driver.window.…

裸机:串口通信

串口通信的基本原理 单工通信和双工通信 (1)单工就是单方向&#xff0c;双工就是双方同时收发&#xff0c;同时只能但方向但是方向可以改变叫半双工 (2)如果只能A发B收则单工&#xff0c;A发B收或者B发A收&#xff08;两个方向不能同时&#xff09;叫半双工&#xff0c;A发B收…