国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Rust Web 全棧開發(fā)之編寫 WebAssembly 應(yīng)用

這篇具有很好參考價值的文章主要介紹了Rust Web 全棧開發(fā)之編寫 WebAssembly 應(yīng)用。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Rust Web 全棧開發(fā)之編寫 WebAssembly 應(yīng)用

MDN Web Docs:https://developer.mozilla.org/zh-CN/docs/WebAssembly

官網(wǎng):https://webassembly.org/

項目結(jié)構(gòu) 和 功能

Web App 教師注冊 <-> WebService <-> WebAssembly App 課程管理

什么是 WebAssembly

  • WebAssembly 是一種新的編碼方式,可以在現(xiàn)代瀏覽器中運行
    • 它是一種低級的類匯編語言
    • 具有緊湊的二進(jìn)制格式
    • 可以接近原生的性能運行
    • 并為 C/C ++ 、 C# 、 Rust 等語言提供一個編譯目標(biāo),以便它們可以在 Web上運行
    • 它也被設(shè)計為可以與 JavaScript 共存,允許兩者一起工作。

機器碼

  • 機器碼是計算機可直接執(zhí)行的語言
  • 匯編語言比較接近機器碼

ASSEMBLY -> ASSEMBLER -> MACHINE CODE

機器碼與 CPU 架構(gòu)

  • 不同的 CPU 架構(gòu)需要不同的機器碼和匯編
  • 高級語言可以“翻譯”成機器碼,以便在 CPU 上運行
  • SOURCE CODE
    • x64
    • x86
    • ARM

WebAssembly

  • WebAssembly 其實不是匯編語言,它不針對特定的機器,而是針對瀏覽器的。
  • WebAssembly 是中間編譯器目標(biāo)
  • SOURCE CODE
    • WASM
      • x64
      • x86
      • ARM

WebAssembly 是什么樣的?

  • 文本格式 .wat
  • 二進(jìn)制格式: .wasm

WebAssembly 能做什么

  • 可以把你編寫 C/C++ 、 C# 、 Rust 等語言的代碼編譯成 WebAssembly 模塊
  • 你可以在 Web 應(yīng)用中加載該模塊,并通過 JavaScript 調(diào)用它
  • 它并不是為了替代 JS ,而是與 JS 一起工作
  • 仍然需要 HTML 和 JS ,因為WebAssembly 無法訪問平臺 API ,例如 DOM , WebGL...

WebAssembly 如何工作

  • 這是 C/C++ 的例子

Hello.c -> EMSCRIPTEN(編譯器) -> hello.wasm hello.js hello.html

WebAssembly 的優(yōu)點

  • 快速、高效、可移植
    • 通過利用常見的硬件能力, WebAssembly 代碼在不同平臺上能夠以接近本地速度運行。
  • 可讀、可調(diào)試
    • WebAssembly 是一門低階語言,但是它有確實有一種人類可讀的文本格式(其標(biāo)準(zhǔn)最終版本目前仍在編制),這允許通過手工來寫代碼,看代碼以及調(diào)試代碼。
  • 保持安全
    • WebAssembly 被限制運行在一個安全的沙箱執(zhí)行環(huán)境中。像其他網(wǎng)絡(luò)代碼一樣,它遵循瀏覽器的同源策略和授權(quán)策略。
  • 不破壞網(wǎng)絡(luò)
    • WebAssembly 的設(shè)計原則是與其他網(wǎng)絡(luò)技術(shù)和諧共處并保持向后兼容。

Rust WebAssembly 部分相關(guān) crate

  • wasm-bindgen
  • wasm-bindgen-future
  • web-sys
  • js-sys

搭建環(huán)境

Rust 官網(wǎng):https://www.rust-lang.org/zh-CN/what/wasm

Rust and WebAssembly:https://rustwasm.github.io/docs/book/

Rust和WebAssembly中文文檔:https://rustwasm.wasmdev.cn/docs/book/

安裝

wasm-pack

下載安裝地址:https://rustwasm.github.io/wasm-pack/installer/

Install wasm-pack

You appear to be running a *nix system (Unix, Linux, MacOS). Install by running:

curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

If you're not on *nix, or you don't like installing from curl, follow the alternate instructions below.

cargo-generate

cargo-generate helps you get up and running quickly with a new Rust project by leveraging a pre-existing git repository as a template.

Install cargo-generate with this command:

cargo install cargo-generate
~ via ?? base
? curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

info: downloading wasm-pack
info: successfully installed wasm-pack to `/Users/qiaopengjun/.cargo/bin/wasm-pack`

~ via ?? base took 8.9s
?

ws on  main via ?? 1.67.1 via ?? base took 23.7s
? cargo install cargo-generate

    Updating crates.io index
warning: spurious network error (2 tries remaining): failed to connect to github.com: Operation timed out; class=Os (2)
warning: spurious network error (1 tries remaining): failed to connect to github.com: Operation timed out; class=Os (2)
  Downloaded cargo-generate v0.18.3
  Downloaded 1 crate (94.7 KB) in 0.73s
	......
   Compiling git2 v0.17.2
   Compiling cargo-generate v0.18.3
    Finished release [optimized] target(s) in 3m 48s
  Installing /Users/qiaopengjun/.cargo/bin/cargo-generate
   Installed package `cargo-generate v0.18.3` (executable `cargo-generate`)

ws on  main via ?? 1.67.1 via ?? base took 3m 48.8s
? npm --version
9.5.0

ws on  main via ?? 1.67.1 via ?? base

Clone the Project Template

ws on  main via ?? 1.67.1 via ?? base
? cd ..

~/rust via ?? base
? cargo generate --git https://github.com/rustwasm/wasm-pack-template

??   Project Name: wasm-game-of-life
??   Destination: /Users/qiaopengjun/rust/wasm-game-of-life ...
??   project-name: wasm-game-of-life ...
??   Generating template ...
??   Moving generated files into: `/Users/qiaopengjun/rust/wasm-game-of-life`...
Initializing a fresh Git repository
?   Done! New project created /Users/qiaopengjun/rust/wasm-game-of-life

~/rust via ?? base took 21.2s
? cd wasm-game-of-life

wasm-game-of-life on  master [?] via ?? 1.67.1 via ?? base
? c

wasm-game-of-life on  master [?] via ?? 1.67.1 via ?? base
?

構(gòu)建項目

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 1m 23.4s 
? wasm-pack build

[INFO]: ??  Checking for the Wasm target...
info: downloading component 'rust-std' for 'wasm32-unknown-unknown'
info: installing component 'rust-std' for 'wasm32-unknown-unknown'
[INFO]: ??  Compiling to Wasm...
   Compiling proc-macro2 v1.0.59
   Compiling unicode-ident v1.0.9
   Compiling quote v1.0.28
   Compiling wasm-bindgen-shared v0.2.86
   Compiling log v0.4.18
   Compiling bumpalo v3.13.0
   Compiling once_cell v1.17.2
   Compiling wasm-bindgen v0.2.86
   Compiling cfg-if v1.0.0
   Compiling syn v2.0.18
   Compiling wasm-bindgen-backend v0.2.86
   Compiling wasm-bindgen-macro-support v0.2.86
   Compiling wasm-bindgen-macro v0.2.86
   Compiling console_error_panic_hook v0.1.7
   Compiling wasm-game-of-life v0.1.0 (/Users/qiaopengjun/rust/wasm-game-of-life)
warning: function `set_panic_hook` is never used
 --> src/utils.rs:1:8
  |
1 | pub fn set_panic_hook() {
  |        ^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `wasm-game-of-life` (lib) generated 1 warning
    Finished release [optimized] target(s) in 11.45s
[INFO]: ??  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ?   Done in 1m 41s
[INFO]: ??   Your wasm pkg is ready to publish at /Users/qiaopengjun/rust/wasm-game-of-life/pkg.

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 1m 52.0s 
? 

問題

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.67.1 via ?? base took 3.8s 
? wasm-pack build

[INFO]: ??  Checking for the Wasm target...
Error: wasm32-unknown-unknown target not found in sysroot: "/opt/homebrew/Cellar/rust/1.67.1"

Used rustc from the following path: "/opt/homebrew/bin/rustc"
It looks like Rustup is not being used. For non-Rustup setups, the wasm32-unknown-unknown target needs to be installed manually. See https://rustwasm.github.io/wasm-pack/book/prerequisites/non-rustup-setups.html on how to do this.

Caused by: wasm32-unknown-unknown target not found in sysroot: "/opt/homebrew/Cellar/rust/1.67.1"

Used rustc from the following path: "/opt/homebrew/bin/rustc"
It looks like Rustup is not being used. For non-Rustup setups, the wasm32-unknown-unknown target needs to be installed manually. See https://rustwasm.github.io/wasm-pack/book/prerequisites/non-rustup-setups.html on how to do this.


wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.67.1 via ?? base 
? 

解決

https://rustwasm.github.io/wasm-pack/book/prerequisites/non-rustup-setups.html

電腦中有兩個Rust,默認(rèn)使用的是brew install rust

通過 brew uninstall rust 卸載 Rust

并運行 rustup self uninstall 卸載通過官網(wǎng)安裝的 Rust

最后重新通過官網(wǎng)安裝Rust,再次執(zhí)行命令解決問題。

Rust安裝

~ via ?? base
? curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.

Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:

  /Users/qiaopengjun/.rustup

This can be modified with the RUSTUP_HOME environment variable.

The Cargo home directory is located at:

  /Users/qiaopengjun/.cargo

This can be modified with the CARGO_HOME environment variable.

The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:

  /Users/qiaopengjun/.cargo/bin

This path will then be added to your PATH environment variable by
modifying the profile files located at:

  /Users/qiaopengjun/.profile
  /Users/qiaopengjun/.bash_profile
  /Users/qiaopengjun/.zshenv

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:


   default host triple: aarch64-apple-darwin
     default toolchain: stable (default)
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1

info: profile set to 'default'
info: default host triple is aarch64-apple-darwin
info: syncing channel updates for 'stable-aarch64-apple-darwin'
info: latest update on 2023-06-01, rust version 1.70.0 (90c541806 2023-05-31)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
 13.5 MiB /  13.5 MiB (100 %)   4.6 MiB/s in  2s ETA:  0s
info: downloading component 'rust-std'
 25.5 MiB /  25.5 MiB (100 %)   4.9 MiB/s in  5s ETA:  0s
info: downloading component 'rustc'
 52.6 MiB /  52.6 MiB (100 %)   6.5 MiB/s in  8s ETA:  0s
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
 13.5 MiB /  13.5 MiB (100 %)   7.9 MiB/s in  1s ETA:  0s
info: installing component 'rust-std'
 25.5 MiB /  25.5 MiB (100 %)  20.9 MiB/s in  1s ETA:  0s
info: installing component 'rustc'
 52.6 MiB /  52.6 MiB (100 %)  23.6 MiB/s in  2s ETA:  0s
info: installing component 'rustfmt'
info: default toolchain set to 'stable-aarch64-apple-darwin'

  stable-aarch64-apple-darwin installed - rustc 1.70.0 (90c541806 2023-05-31)


Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).

To configure your current shell, run:
source "$HOME/.cargo/env"

~ via ?? base took 2m 46.4s
? source "$HOME/.cargo/env"

~ via ?? base
? rustc --version
rustc 1.70.0 (90c541806 2023-05-31)

~ via ?? base
? rustup toolchain list
stable-aarch64-apple-darwin (default)

~ via ?? base
?

構(gòu)建

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 1m 23.4s 
? wasm-pack build

[INFO]: ??  Checking for the Wasm target...
info: downloading component 'rust-std' for 'wasm32-unknown-unknown'
info: installing component 'rust-std' for 'wasm32-unknown-unknown'
[INFO]: ??  Compiling to Wasm...
   Compiling proc-macro2 v1.0.59
   Compiling unicode-ident v1.0.9
   Compiling quote v1.0.28
   Compiling wasm-bindgen-shared v0.2.86
   Compiling log v0.4.18
   Compiling bumpalo v3.13.0
   Compiling once_cell v1.17.2
   Compiling wasm-bindgen v0.2.86
   Compiling cfg-if v1.0.0
   Compiling syn v2.0.18
   Compiling wasm-bindgen-backend v0.2.86
   Compiling wasm-bindgen-macro-support v0.2.86
   Compiling wasm-bindgen-macro v0.2.86
   Compiling console_error_panic_hook v0.1.7
   Compiling wasm-game-of-life v0.1.0 (/Users/qiaopengjun/rust/wasm-game-of-life)
warning: function `set_panic_hook` is never used
 --> src/utils.rs:1:8
  |
1 | pub fn set_panic_hook() {
  |        ^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `wasm-game-of-life` (lib) generated 1 warning
    Finished release [optimized] target(s) in 11.45s
[INFO]: ??  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ?   Done in 1m 41s
[INFO]: ??   Your wasm pkg is ready to publish at /Users/qiaopengjun/rust/wasm-game-of-life/pkg.

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 1m 52.0s 
? 

Npm 初始化項目

npm 中文網(wǎng):https://npm.nodejs.cn/

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 24.9s 
? npm init wasm-app www
?? Rust + ?? Wasm = ?

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 22.7s 
? 

www/package.json

{
  "name": "create-wasm-app",
  "version": "0.1.0",
  "description": "create an app to consume rust-generated wasm packages",
  "main": "index.js",
  "bin": {
    "create-wasm-app": ".bin/create-wasm-app.js"
  },
  "scripts": {
    "build": "webpack --config webpack.config.js",
    "start": "webpack-dev-server"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/rustwasm/create-wasm-app.git"
  },
  "keywords": [
    "webassembly",
    "wasm",
    "rust",
    "webpack"
  ],
  "author": "Ashley Williams <ashley666ashley@gmail.com>",
  "license": "(MIT OR Apache-2.0)",
  "bugs": {
    "url": "https://github.com/rustwasm/create-wasm-app/issues"
  },
  "homepage": "https://github.com/rustwasm/create-wasm-app#readme",
  "dependencies": {
    "wasm-game-of-life": "file:../pkg"
  },
  "devDependencies": {
    "hello-wasm-pack": "^0.1.0",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.1.0",
    "webpack-dev-server": "^3.1.5",
    "copy-webpack-plugin": "^5.0.0"
  }
}

安裝依賴

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 22.7s 
? cd www                                                               

www on  master [!] is ?? 0.1.0 via ? v19.7.0 via ?? base 
? npm install        

www/index.js

import * as wasm from "wasm-game-of-life";

wasm.greet();

運行

www on  master [!] is ?? 0.1.0 via ? v19.7.0 via ?? base took 2m 15.4s 
? npm run start

訪問:http://localhost:8080/

src/lib.rs

mod utils;

use wasm_bindgen::prelude::*;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(s : &str) {
    alert(format!("Hello, {}!", s).as_str());
}

構(gòu)建

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base 
? wasm-pack build
[INFO]: ??  Checking for the Wasm target...
[INFO]: ??  Compiling to Wasm...
   Compiling wasm-game-of-life v0.1.0 (/Users/qiaopengjun/rust/wasm-game-of-life)
warning: function `set_panic_hook` is never used
 --> src/utils.rs:1:8
  |
1 | pub fn set_panic_hook() {
  |        ^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `wasm-game-of-life` (lib) generated 1 warning
    Finished release [optimized] target(s) in 0.37s
[INFO]: ??  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: ?   Done in 0.80s
[INFO]: ??   Your wasm pkg is ready to publish at /Users/qiaopengjun/rust/wasm-game-of-life/pkg.

wasm-game-of-life on  master [?] is ?? 0.1.0 via ?? 1.70.0 via ?? base 
? 

安裝并運行

www on  master [!] is ?? 0.1.0 via ? v19.7.0 via ?? base took 8m 0.8s 
? npm install && npm run start
npm WARN deprecated fsevents@1.2.9: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2

www/index.js

import * as wasm from "wasm-game-of-life";

wasm.greet("Dave");

打開項目 ws

webservice/Cargo.toml

[package]
name = "webservice"
version = "0.1.0"
edition = "2021"
default-run = "teacher-service"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix-cors = "0.6.4"
actix-web = "4.3.1"
actix-rt = "2.8.0"
dotenv = "0.15.0"
serde = { version = "1.0.163", features = ["derive"] }
chrono = { version = "0.4.24", features = ["serde"] }
openssl = { version = "0.10.52", features = ["vendored"] }
sqlx = { version = "0.6.3", default_features = false, features = [
    "postgres",
    "runtime-tokio-rustls",
    "macros",
    "chrono",
] }


[[bin]]
name = "teacher-service"

webservice/src/bin/teacher-service.rs

use actix_cors::Cors;
use actix_web::{http, web, App, HttpServer};
use dotenv::dotenv;
use sqlx::postgres::PgPoolOptions;
use std::env;
use std::io;
use std::sync::Mutex;

#[path = "../dbaccess/mod.rs"]
mod dbaccess;
#[path = "../errors.rs"]
mod errors;
#[path = "../handlers/mod.rs"]
mod handlers;
#[path = "../models/mod.rs"]
mod models;
#[path = "../routers.rs"]
mod routers;
#[path = "../state.rs"]
mod state;

use errors::MyError;
use routers::*;
use state::AppState;

#[actix_rt::main]
async fn main() -> io::Result<()> {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set.");
    let db_pool = PgPoolOptions::new().connect(&database_url).await.unwrap();
    let shared_data = web::Data::new(AppState {
        health_check_response: "I'm Ok.".to_string(),
        visit_count: Mutex::new(0),
        db: db_pool,
    });
    let app = move || {
        let cors = Cors::default()
            .allowed_origin("http://localhost:8080/")
            .allowed_origin_fn(|origin, _req_head| {
                origin.as_bytes().starts_with(b"http://localhost")
            })
            .allowed_methods(vec!["GET", "POST", "DELETE"])
            .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
            .allowed_header(http::header::CONTENT_TYPE)
            .max_age(3600);

        App::new()
            .app_data(shared_data.clone())
            .app_data(web::JsonConfig::default().error_handler(|_err, _req| {
                MyError::InvalidInput("Please provide valid json input".to_string()).into()
            }))
            .configure(general_routes)
            .configure(course_routes) // 路由注冊
            .wrap(cors)
            .configure(teacher_routes)
    };

    HttpServer::new(app).bind("127.0.0.1:3000")?.run().await
}

Wasm 克隆項目模板

ws on  main via ?? 1.70.0 via ?? base 
? cargo generate --git https://github.com/rustwasm/wasm-pack-template

??   Project Name: wasm-client
??   Destination: /Users/qiaopengjun/rust/ws/wasm-client ...
??   project-name: wasm-client ...
??   Generating template ...
??   Moving generated files into: `/Users/qiaopengjun/rust/ws/wasm-client`...
Initializing a fresh Git repository
?   Done! New project created /Users/qiaopengjun/rust/ws/wasm-client

ws on  main [!?] via ?? 1.70.0 via ?? base took 29.2s 
? 

Npm 初始化項目

ws/wasm-client on  main [!?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 11.9s 
? npm init wasm-app www
?? Rust + ?? Wasm = ?

ws/wasm-client on  main [!?] is ?? 0.1.0 via ?? 1.70.0 via ?? base took 3.5s 
? 

構(gòu)建項目

wasm-pack build

Cargo.toml

[workspace]
members = ["webservice", "webapp", "wasm-client"]

wasm-client/Cargo.toml

[package]
name = "wasm-client"
version = "0.1.0"
authors = ["QiaoPengjun5162 <qiaopengjun0@gmail.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
chrono = { version = "0.4.26", features = ["serde"] }
serde = { version = "1.0.163", features = ["derive"] }
serde_derive = "1.0.163"
serde_json = "1.0.96"
js-sys = "0.3.63"
wasm-bindgen = { version = "0.2.86", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4.36"
web-sys = { version = "0.3.63", features = [
    "Headers",
    "Request",
    "RequestInit",
    "RequestMode",
    "Response",
    "Window",
    "Document",
    "Element",
    "HtmlElement",
    "Node",
    "console",
] }

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.5", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.13"

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

注意:并不是所有的 Rust crate 都能在wasm中使用

https://getbootstrap.com/docs/5.2/getting-started/introduction/#cdn-links

項目目錄

ws on  main [!?] via ?? 1.70.0 via ?? base 
? tree -a -I "target|.git|node_modules|.bin"
.
├── .env
├── .gitignore
├── .vscode
│   └── settings.json
├── Cargo.lock
├── Cargo.toml
├── README.md
├── wasm-client
│   ├── .appveyor.yml
│   ├── .gitignore
│   ├── .travis.yml
│   ├── Cargo.toml
│   ├── LICENSE_APACHE
│   ├── LICENSE_MIT
│   ├── README.md
│   ├── pkg
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── wasm_client.d.ts
│   │   ├── wasm_client.js
│   │   ├── wasm_client_bg.js
│   │   ├── wasm_client_bg.wasm
│   │   └── wasm_client_bg.wasm.d.ts
│   ├── src
│   │   ├── errors.rs
│   │   ├── lib.rs
│   │   ├── models
│   │   │   ├── course.rs
│   │   │   └── mod.rs
│   │   └── utils.rs
│   ├── tests
│   │   └── web.rs
│   └── www
│       ├── .gitignore
│       ├── .travis.yml
│       ├── LICENSE-APACHE
│       ├── LICENSE-MIT
│       ├── README.md
│       ├── bootstrap.js
│       ├── index.html
│       ├── index.js
│       ├── package-lock.json
│       ├── package.json
│       └── webpack.config.js
├── webapp
│   ├── .env
│   ├── Cargo.toml
│   ├── src
│   │   ├── bin
│   │   │   └── svr.rs
│   │   ├── errors.rs
│   │   ├── handlers.rs
│   │   ├── mod.rs
│   │   ├── models.rs
│   │   └── routers.rs
│   └── static
│       ├── css
│       │   └── register.css
│       ├── register.html
│       └── teachers.html
└── webservice
    ├── Cargo.toml
    └── src
        ├── bin
        │   └── teacher-service.rs
        ├── dbaccess
        │   ├── course.rs
        │   ├── mod.rs
        │   └── teacher.rs
        ├── errors.rs
        ├── handlers
        │   ├── course.rs
        │   ├── general.rs
        │   ├── mod.rs
        │   └── teacher.rs
        ├── main.rs
        ├── models
        │   ├── course.rs
        │   ├── mod.rs
        │   └── teacher.rs
        ├── routers.rs
        └── state.rs

19 directories, 65 files

ws on  main [!?] via ?? 1.70.0 via ?? base 
? 

wasm-client/www/package.json

{
  "name": "create-wasm-app",
  "version": "0.1.0",
  "description": "create an app to consume rust-generated wasm packages",
  "main": "index.js",
  "bin": {
    "create-wasm-app": ".bin/create-wasm-app.js"
  },
  "scripts": {
    "build": "webpack --config webpack.config.js",
    "start": "webpack-dev-server"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/rustwasm/create-wasm-app.git"
  },
  "keywords": [
    "webassembly",
    "wasm",
    "rust",
    "webpack"
  ],
  "author": "Ashley Williams <ashley666ashley@gmail.com>",
  "license": "(MIT OR Apache-2.0)",
  "bugs": {
    "url": "https://github.com/rustwasm/create-wasm-app/issues"
  },
  "homepage": "https://github.com/rustwasm/create-wasm-app#readme",
  "dependencies": {
    "wasm-client": "file:../pkg"
  },
  "devDependencies": {
    "hello-wasm-pack": "^0.1.0",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.1.0",
    "webpack-dev-server": "^3.1.5",
    "copy-webpack-plugin": "^5.0.0"
  }
}

wasm-client/src/errors.rs

use serde::Serialize;

#[derive(Debug, Serialize)]
pub enum MyError {
    SomeError(String),
}

impl From<String> for MyError {
    fn from(s: String) -> Self {
        MyError::SomeError(s)
    }
}

impl From<wasm_bindgen::JsValue> for MyError {
    fn from(js_value: wasm_bindgen::JsValue) -> Self {
        MyError::SomeError(js_value.as_string().unwrap())
    }
}

wasm-client/src/models/mod.rs

pub mod course;

wasm-client/src/models/course.rs

use super::super::errors::MyError;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response};

#[derive(Debug, Deserialize, Serialize)]
pub struct Course {
    pub teacher_id: i32,
    pub id: i32,
    pub name: String,
    pub time: NaiveDateTime,

    pub description: Option<String>,
    pub format: Option<String>,
    pub structure: Option<String>,
    pub duration: Option<String>,
    pub price: Option<i32>,
    pub language: Option<String>,
    pub level: Option<String>,
}

pub async fn get_courses_by_teacher(teacher_id: i32) -> Result<Vec<Course>, MyError> {
    // 訪問webservice 讀取課程
    let mut opts = RequestInit::new();
    opts.method("GET");
    opts.mode(RequestMode::Cors); // 跨域

    let url = format!("http://localhost:3000/courses/{}", teacher_id);

    let request = Request::new_with_str_and_init(&url, &opts)?;
    request.headers().set("Accept", "application/json")?;

    let window = web_sys::window().ok_or("no window exists".to_string())?;
    let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;

    assert!(resp_value.is_instance_of::<Response>());

    let resp: Response = resp_value.dyn_into().unwrap();
    let json = JsFuture::from(resp.json()?).await?;

    // let courses: Vec<Course> = json.into_serde().unwrap();
    let courses: Vec<Course> = serde_wasm_bindgen::from_value(json).unwrap();

    Ok(courses)
}

pub async fn delete_course(teacher_id: i32, course_id: i32) -> () {
    let mut opts = RequestInit::new();
    opts.method("DELETE");
    opts.mode(RequestMode::Cors);

    let url = format!("http://localhost:3000/courses/{}/{}", teacher_id, course_id);

    let request = Request::new_with_str_and_init(&url, &opts).unwrap();
    request.headers().set("Accept", "application/json").unwrap();

    let window = web_sys::window().unwrap();
    let resp_value = JsFuture::from(window.fetch_with_request(&request))
        .await
        .unwrap();

    assert!(resp_value.is_instance_of::<Response>());

    let resp: Response = resp_value.dyn_into().unwrap();
    let json = JsFuture::from(resp.json().unwrap()).await.unwrap();

    // let _course: Course = json.into_serde().unwrap();
    let _courses: Course = serde_wasm_bindgen::from_value(json).unwrap();
}

use js_sys::Promise;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub async fn add_course(name: String, description: String) -> Result<Promise, JsValue> {
    let mut opts = RequestInit::new();
    opts.method("POST");
    opts.mode(RequestMode::Cors);
    let str_json = format!(
        r#"
        {{
            "teacher_id": 1,
            "name": "{}",
            "description": "{}"
        }}
        "#,
        name, description
    );
    opts.body(Some(&JsValue::from_str(str_json.as_str())));
    let url = "http://localhost:3000/courses/";

    let request = Request::new_with_str_and_init(&url, &opts)?;
    request.headers().set("Content-Type", "application/json")?;
    request.headers().set("Accept", "application/json")?;

    let window = web_sys::window().ok_or("no window exists".to_string())?;
    let resp_value = JsFuture::from(window.fetch_with_request(&request))
        .await
        .unwrap();

    assert!(resp_value.is_instance_of::<Response>());

    let resp: Response = resp_value.dyn_into().unwrap();
    Ok(resp.json()?)
}

wasm-client/src/lib.rs

mod utils;

use wasm_bindgen::prelude::*;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
    fn confirm(s: &str) -> bool;

    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen]
pub fn greet(_name: &str) {
    alert("Hello, wasm-client!");
}

pub mod errors;
pub mod models;

use models::course::{delete_course, get_courses_by_teacher, Course};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::*;
use web_sys::HtmlButtonElement;

#[wasm_bindgen(start)]
pub async fn main() -> Result<(), JsValue> {
    let window = web_sys::window().expect("no global window exists");
    let document = window.document().expect("no global document exists");

    let left_tbody = document
        .get_element_by_id("left-tbody")
        .expect("left div not exists");

    let courses: Vec<Course> = get_courses_by_teacher(1).await.unwrap();
    for c in courses.iter() {
        let tr = document.create_element("tr")?;
        tr.set_attribute("id", format!("tr-{}", c.id).as_str())?;
        let td = document.create_element("td")?;
        td.set_text_content(Some(format!("{}", c.id).as_str()));
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        td.set_text_content(Some(c.name.as_str()));
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        td.set_text_content(Some(c.time.format("%Y-%m-%d").to_string().as_str()));
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        if let Some(desc) = c.description.clone() {
            td.set_text_content(Some(desc.as_str()));
        }
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        // let btn = document.create_element("button")?;
        let btn: HtmlButtonElement = document
            .create_element("button")
            .unwrap()
            .dyn_into::<HtmlButtonElement>()
            .unwrap();

        let cid = c.id;
        let click_closure = Closure::wrap(Box::new(move |_event: web_sys::MouseEvent| {
            let r = confirm(format!("確認(rèn)刪除 ID 為 {} 的課程?", cid).as_str());
            match r {
                true => {
                    spawn_local(delete_course(1, cid)); // delete_course 異步函數(shù) spawn_local 把 future 放在當(dāng)前線程
                    alert("刪除成功!");

                    web_sys::window().unwrap().location().reload().unwrap();
                }
                _ => {}
            }
        }) as Box<dyn Fn(_)>);

        btn.add_event_listener_with_callback("click", click_closure.as_ref().unchecked_ref())?; // 要把閉包轉(zhuǎn)化為 function 的引用
        click_closure.forget(); // 走出作用域后函數(shù)依然有效 但會造成內(nèi)存泄漏

        btn.set_attribute("class", "btn btn-danger btn-sm")?;
        btn.set_text_content(Some("Delete"));
        td.append_child(&btn)?;
        tr.append_child(&td)?;

        left_tbody.append_child(&tr)?;
    }

    Ok(())
}

wasm-client/www/index.js

import * as wasm from "wasm-client";

const myForm = document.getElementById('form');
myForm.addEventListener('submit', (e) => {
  e.preventDefault();
  const name = document.getElementById('name').value;
  const desc = document.querySelector('#description').value;

  wasm.add_course(name, desc).then((json) => {
    alert("添加成功!");
    window.location.reload();
  });
});

wasm-client/www/index.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Hello wasm-pack!</title>
  <link  rel="stylesheet"
    integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous" />
</head>

<body>
  <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
  <nav class="navbar navbar-dark bg-primary">
    <div class="container-fluid">
      <a class="navbar-brand" href="#">Wasm-client</a>
    </div>
  </nav>
  <div class="m-3" style="height: 600px">
    <div class="row">
      <div class="col">
        <div class="card border-info mb-3">
          <div class="card-header">課程列表</div>
          <!-- <div class="card-body">
            <button type="button" class="btn btn-primary">Add</button>
          </div> -->
          <table class="table talbe-hover table-bordered table-sm">
            <thead>
              <tr>
                <th scope="col">ID</th>
                <th scope="col">名稱</th>
                <th scope="col">時間</th>
                <th scope="col">簡介</th>
                <th scope="col">操作</th>
              </tr>
            </thead>
            <tbody id="left-tbody"></tbody>
          </table>
          <div id="left"></div>
        </div>
      </div>
      <div class="col">
        <div class="card border-info mb-3">
          <div class="card-header">添加課程</div>
          <div class="card-body">
            <form class="row g-3 needs-validation" id="form">
              <div class="mb-3">
                <label for="name" class="form-label">課程名稱</label>
                <input type="name" class="form-control" id="name" required placeholder="課程名稱" />
                <div class="invalid-feedback">
                  請?zhí)顚懻n程名!
                </div>
              </div>
              <div class="mb-3">
                <label for="description" class="form-label">課程簡介</label>
                <textarea class="form-control" id="description" rows="3"></textarea>
              </div>
              <div class="col-12">
                <button type="submit" class="btn btn-primary">提交</button>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script src="./bootstrap.js"></script>
</body>

</html>

運行

ws/wasm-client on  main [!?] is ?? 0.1.0 via ?? 1.70.0 via ?? base 
? wasm-pack build           

ws/webservice on  main [!?] is ?? 0.1.0 via ?? 1.70.0 via ?? base 
? cargo run    

www on  master [!] is ?? 0.1.0 via ? v19.7.0 via ?? base 
? npm install && npm run start

訪問:http://localhost:8080/

問題:報錯 Uncaught (in promise) DOMException: Failed to execute 'appendChild' on 'Node': The new child element contains the parent. 前端獲取不到課程信息

解決:

這個錯誤提示說明在嘗試將一個元素添加為其父元素的子元素時出現(xiàn)了問題,導(dǎo)致了循環(huán)引用。具體來說,錯誤發(fā)生在這一行代碼中:

td.append_child(&td)?;

在這行代碼中,你試圖將創(chuàng)建的td元素添加為其自身的子元素。這是不允許的,因為一個元素不能成為自己的子元素。

正確的代碼是: tr.append_child(&td)?;

修改之后的代碼:文章來源地址http://www.zghlxwxcb.cn/news/detail-470730.html

#[wasm_bindgen(start)]
pub async fn main() -> Result<(), JsValue> {
    let window = web_sys::window().expect("no global window exists");
    let document = window.document().expect("no global document exists");

    let left_tbody = document
        .get_element_by_id("left-tbody")
        .expect("left div not exists");

    let courses: Vec<Course> = get_courses_by_teacher(1).await.unwrap();
    for c in courses.iter() {
        let tr = document.create_element("tr")?;
        tr.set_attribute("id", format!("tr-{}", c.id).as_str())?;
        let td = document.create_element("td")?;
        td.set_text_content(Some(format!("{}", c.id).as_str()));
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        td.set_text_content(Some(c.name.as_str()));
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        td.set_text_content(Some(c.time.format("%Y-%m-%d").to_string().as_str()));
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        if let Some(desc) = c.description.clone() {
            td.set_text_content(Some(desc.as_str()));
        }
        tr.append_child(&td)?;

        let td = document.create_element("td")?;
        // let btn = document.create_element("button")?;
        let btn: HtmlButtonElement = document
            .create_element("button")
            .unwrap()
            .dyn_into::<HtmlButtonElement>()
            .unwrap();

        let cid = c.id;
        let click_closure = Closure::wrap(Box::new(move |_event: web_sys::MouseEvent| {
            let r = confirm(format!("確認(rèn)刪除 ID 為 {} 的課程?", cid).as_str());
            match r {
                true => {
                    spawn_local(delete_course(1, cid)); // delete_course 異步函數(shù) spawn_local 把 future 放在當(dāng)前線程
                    alert("刪除成功!");

                    web_sys::window().unwrap().location().reload().unwrap();
                }
                _ => {}
            }
        }) as Box<dyn Fn(_)>);

        btn.add_event_listener_with_callback("click", click_closure.as_ref().unchecked_ref())?; // 要把閉包轉(zhuǎn)化為 function 的引用
        click_closure.forget(); // 走出作用域后函數(shù)依然有效 但會造成內(nèi)存泄漏

        btn.set_attribute("class", "btn btn-danger btn-sm")?;
        btn.set_text_content(Some("Delete"));
        td.append_child(&btn)?;
        tr.append_child(&td)?;

        left_tbody.append_child(&tr)?;
    }

    Ok(())
}

到了這里,關(guān)于Rust Web 全棧開發(fā)之編寫 WebAssembly 應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 快速配置 Rust 開發(fā)環(huán)境并編寫一個小應(yīng)用

    快速配置 Rust 開發(fā)環(huán)境并編寫一個小應(yīng)用

    安裝: curl --proto \\\'=https\\\' --tlsv1.2 -sSf https://sh.rustup.rs | sh 更新: Rust 的升級非常頻繁. 如果安裝 Rustup 后已有一段時間,那么很可能 Rust 版本已經(jīng)過時, 運行 rustup update 獲取最新版本的 Rust rustc:編譯Rust程序 rustc只適合簡單的Rust程序,較大型的項目還是推薦使用Cargo Cargo:Rust 的構(gòu)建

    2024年02月16日
    瀏覽(28)
  • 前端Rust開發(fā)WebAssembly與Swc插件快速入門

    現(xiàn)代前端對速度的追求已經(jīng)進(jìn)入二進(jìn)制工具時代,Rust 開發(fā)成為每個人的必修課。 一般我們將常見的前端 Rust 開發(fā)分為以下幾類,難度由上至下遞增: 開發(fā) wasm 。 開發(fā) swc 插件。 開發(fā)代碼處理工具。 我們將默認(rèn)讀者具備最簡單的 Rust 知識,進(jìn)行快速入門介紹。 開發(fā) wasm 意義

    2024年02月12日
    瀏覽(22)
  • 〖Web全棧開發(fā)⑤〗— CSS基礎(chǔ)

    〖Web全棧開發(fā)⑤〗— CSS基礎(chǔ)

    ??????個人簡介:以山河作禮。 ??????: Python領(lǐng)域新星創(chuàng)作者,CSDN實力新星認(rèn)證,阿里云社區(qū)專家博主 ????:Web全棧開發(fā)專欄:《Web全棧開發(fā)》免費專欄,歡迎閱讀! CSS 的意思為 Cascading Style Sheets,中文名是層疊樣式表。 CSS 是由大名鼎鼎的 W3C 中 CSS 工作組來發(fā)布以

    2024年02月09日
    瀏覽(25)
  • 【 Python 全棧開發(fā) - WEB開發(fā)篇 - 26 】Javascript 基礎(chǔ)

    Javascript 是一種動態(tài)的、基于對象的編程語言,通常用于網(wǎng)頁的客戶端腳本編程。它可以在網(wǎng)頁上實現(xiàn)交互效果、動態(tài)效果、表單驗證、數(shù)據(jù)處理等功能。 學(xué)習(xí) Javascript 可以通過以下途徑: 在線教程:像 w3schools、MDN 等網(wǎng)站提供了詳細(xì)的 Javascript 教程和示例代碼。 書籍:可以

    2024年02月08日
    瀏覽(16)
  • 【 Python 全棧開發(fā) - WEB開發(fā)篇 - 21 】進(jìn)程與線程

    進(jìn)程和線程都是計算機中用來實現(xiàn)多任務(wù)并發(fā)的機制,但它們有區(qū)別和聯(lián)系。 區(qū)別: 定義不同:進(jìn)程是操作系統(tǒng)分配資源的基本單位,是程序執(zhí)行時的一個實例,包括代碼、數(shù)據(jù)和資源,可以看成是程序的一次執(zhí)行過程。而線程是進(jìn)程內(nèi)的一個執(zhí)行單元,是程序執(zhí)行流的最

    2024年02月08日
    瀏覽(21)
  • 前端面試:【Angular】打造強大Web應(yīng)用的全棧框架

    嗨,親愛的Angular探險家!在前端開發(fā)的旅程中,有一個全??蚣?,那就是 Angular 。Angular提供了模塊化、組件化、依賴注入、路由和RxJS等特性,助力你構(gòu)建強大、可擴展的Web應(yīng)用。 1. 什么是Angular? Angular是一個由Google開發(fā)的JavaScript框架,用于構(gòu)建現(xiàn)代Web應(yīng)用。它是一個全棧

    2024年02月11日
    瀏覽(27)
  • 【保姆級教程】如何用Rust編寫一個ChatGPT桌面應(yīng)用

    原因?qū)嵲谔?,我們需要便捷地?dǎo)出記錄,需要在回答長度超長的時候自動加上“繼續(xù)”,需要收藏一些很酷很實用的prompt...... (首先我假設(shè)你是一名如我一樣習(xí)慣用IDEA開發(fā)的java仔) 效率高、資源占用量低。 安全性高:Rust 是一種內(nèi)存安全的語言,其所有操作都經(jīng)過系統(tǒng)級

    2024年02月04日
    瀏覽(25)
  • 〖Web全棧開發(fā)①〗—網(wǎng)絡(luò)編程基礎(chǔ)(上)

    〖Web全棧開發(fā)①〗—網(wǎng)絡(luò)編程基礎(chǔ)(上)

    ??????個人簡介:以山河作禮。 ??????: Python領(lǐng)域新星創(chuàng)作者,CSDN實力新星認(rèn)證,阿里云社區(qū)專家博主 ?? 計算機網(wǎng)絡(luò) : ????計算機網(wǎng)絡(luò)是指將地理位置不同的具有獨立功能的多臺計算機及其外部設(shè)備,通過通信線路連接起來,在網(wǎng)絡(luò)操作系統(tǒng),網(wǎng)絡(luò)管理軟件及網(wǎng)

    2024年02月05日
    瀏覽(23)
  • 〖Web全棧開發(fā)②〗—網(wǎng)絡(luò)編程基礎(chǔ)(下)

    〖Web全棧開發(fā)②〗—網(wǎng)絡(luò)編程基礎(chǔ)(下)

    ??????個人簡介:以山河作禮。 ??????: Python領(lǐng)域新星創(chuàng)作者,CSDN實力新星認(rèn)證,阿里云社區(qū)專家博主 ????:Web全棧開發(fā)專欄:《Web全棧開發(fā)》免費專欄,歡迎閱讀! ?? 學(xué)習(xí)目標(biāo) 能夠知道TCP客戶端程序的開發(fā)流程 1. TCP 網(wǎng)絡(luò)應(yīng)用程序開發(fā)流程的介紹 TCP 網(wǎng)絡(luò)應(yīng)用程

    2024年02月04日
    瀏覽(23)
  • 〖Web全棧開發(fā)③〗—HTTP協(xié)議和靜態(tài)web服務(wù)器

    〖Web全棧開發(fā)③〗—HTTP協(xié)議和靜態(tài)web服務(wù)器

    ??????個人簡介:以山河作禮。 ??????: Python領(lǐng)域新星創(chuàng)作者,CSDN實力新星認(rèn)證,阿里云社區(qū)專家博主 ????:Web全棧開發(fā)專欄:《Web全棧開發(fā)》免費專欄,歡迎閱讀! TCP (Transmission Control Protocol) 是在互聯(lián)網(wǎng)協(xié)議(IP)上的一種基于連接(面向連接)的傳輸層協(xié)議 。數(shù)據(jù)

    2024年02月05日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包