Rust Web 全栈开发(二):构建 HTTP Server

从零开始:用 Rust 编写你的第一个 Web 服务 Rust 是一种现代、高性能的编程语言,近年来在 Web 开发领域也有了一席之地。本文将介绍如何使用 Rust 编写一个简单的 Web程序,从搭建开发环境到创建第一个 Web 页面。 阅读详情

Rust Web 全栈开发(二):构建 HTTP Server

参考视频:https://www.bilibili.com/video/BV1RP4y1G7KF

Web Server 的消息流动图:

在这里插入图片描述

Server:监听 TCP 字节流

Router:接收 HTTP 请求,并决定调用哪个 Handler

Handler:处理 HTTP 请求,构建 HTTP 响应

HTTP Library:

  • 解释字节流,把它转换为 HTTP 请求
  • 把 HTTP 响应转换回字节流

构建步骤:

  1. 解析 HTTP 请求消息
  2. 构建 HTTP 响应消息
  3. 路由与 Handler
  4. 测试 Web Server

创建成员包/库:httpserver、http

在原项目下新建成员包 httpserver、成员库 http:

cargo new httpserver
cargo new --lib http

在这里插入图片描述

在工作区内运行 cargo new 会自动将新创建的包添加到工作区内 Cargo.toml 的 [workspace] 定义中的 members 键中,如下所示:

在这里插入图片描述

在 http 成员库的 src 目录下新建两个文件:httprequest.rs、httpresponse.rs。

此时,我们可以通过运行 cargo build 来构建工作区。项目目录下的文件应该是这样的:

├── Cargo.lock
├── Cargo.toml
├── httpserver
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── http
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
│       └── httprequest.rs
│       └── httpresponse.rs
├── tcpclient
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── tcpserver
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

解析 HTTP 请求

HTTP 请求的构成

HTTP 请求报文由 3 部分组成:请求行、请求头、请求体。

在这里插入图片描述

构建 HttpRequest

3 个数据结构:

名称类型描述
HttpRequeststruct表示 HTTP 请求
Methodenum指定所允许的 HTTP 方法
Versionenum指定所允许的 HTTP 版本

以上 3 个数据结构都需要实现的 3 个 trait:

名称描述
From<&str>用于把传进来的字符串切片转换为 HttpRequest
Debug打印调试信息
PartialEq用于解析和自动化测试脚本里做比较

打开 http 成员库中的 httprequest.rs,编写代码:

use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum Method {
    Get,
    Post,
    Uninitialized,
}
impl From<&str> for Method {
    fn from(s: &str) -> Method {
        match s {
            "GET" => Method::Get,
            "POST" => Method::Post,
            _ => Method::Uninitialized,
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Version {
    V1_1,
    V2_0,
    Uninitialized,
}

impl From<&str> for Version {
    fn from(s: &str) -> Version {
        match s {
            "HTTP/1.1" => Version::V1_1,
            _ => Version::Uninitialized,
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Resource {
    Path(String),
}

#[derive(Debug)]
pub struct HttpRequest {
    pub method: Method,
    pub resource: Resource,
    pub version: Version,
    pub headers: HashMap<String, String>,
    pub body: String,
}

impl From<String> for HttpRequest {
    fn from(request: String) -> HttpRequest {
        let mut parsed_method = Method::Uninitialized;
        let mut parsed_resource = Resource::Path("".to_string());
        let mut parsed_version =  Version::V1_1;
        let mut parsed_headers = HashMap::new();
        let mut parsed_body = "";

        for line in request.lines() {
            if line.contains("HTTP") {
                let (method, resource, version) = process_request_line(line);
                parsed_method = method;
                parsed_resource = resource;
                parsed_version = version;
            } else if line.contains(":") {
                let (key, value) = process_header_line(line);
                parsed_headers.insert(key, value);
            } else if line.len() == 0 {

            } else {
                parsed_body = line;
            }
        }

        HttpRequest {
            method: parsed_method,
            resource: parsed_resource,
            version: parsed_version,
            headers: parsed_headers,
            body: parsed_body.to_string(),
        }
    }
}

fn process_header_line(s: &str) -> (String, String) {
    let mut header_items = s.split(":");
    let mut key = String::from("");
    let mut value = String::from("");

    if  let Some(k) = header_items.next() {
        key = k.to_string();
    }
    if let Some(v) = header_items.next() {
        value = v.to_string();
    }

    (key, value)
}

fn process_request_line(s: &str) -> (Method, Resource, Version) {
    let mut words = s.split_whitespace();
    let method = words.next().unwrap();
    let resource = words.next().unwrap();
    let version = words.next().unwrap();

    (
        method.into(),
        Resource::Path(resource.to_string()),
        version.into()
    )
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_method_into() {
        let method: Method = "GET".into();
        assert_eq!(method, Method::Get);
    }

    #[test]
    fn test_version_into() {
        let version: Version = "HTTP/1.1".into();
        assert_eq!(version, Version::V1_1);
    }

    #[test]
    fn test_read_http() {
        let s = String::from("GET /greeting HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.71.1\r\nAccept: */*\r\n\r\n");

        let mut headers_excepted = HashMap::new();
        headers_excepted.insert("Host".into(), " localhost".into());
        headers_excepted.insert("Accept".into(), " */*".into());
        headers_excepted.insert("User-Agent".into(), " curl/7.71.1".into());

        let request: HttpRequest = s.into();

        assert_eq!(request.method, Method::Get);
        assert_eq!(request.resource, Resource::Path("/greeting".to_string()));
        assert_eq!(request.version, Version::V1_1);
        assert_eq!(request.headers, headers_excepted);
    }
}

运行命令 cargo test -p http,测试 http 成员库。

3 个测试都通过了:

在这里插入图片描述

构建 HTTP 响应

HTTP 响应的构成

HTTP 响应报文由 3 部分组成:响应行、响应头、响应体。

在这里插入图片描述

构建 HttpResponse

HttpResponse 需要实现的方法或 trait:

名称描述
Default trait指定成员的默认值
From trait将 HttpResponse 转化为 String
new()使用默认值创建一个新的 HttpResponse 结构体
getter 方法获取 HttpResponse 成员变量的值
send_response()构建响应,将原始字节通过 TCP 传送

打开 http 成员库中的 httpresponse.rs,编写代码:

use std::collections::HashMap;
use std::io::{Result, Write};

#[derive(Debug, PartialEq, Clone)]
pub struct HttpResponse<'a> {
    version: &'a str,
    status_code: &'a str,
    status_text: &'a str,
    headers: Option<HashMap<&'a str, &'a str>>,
    body: Option<String>,
}

impl<'a> Default for HttpResponse<'a> {
    fn default() -> Self {
        Self {
            version: "HTTP/1.1".into(),
            status_code: "200".into(),
            status_text: "OK".into(),
            headers: None,
            body: None,
        }
    }
}

impl<'a> From<HttpResponse<'a>> for String {
    fn from(response: HttpResponse) -> String {
        let res = response.clone();
        format!(
            "{} {} {}\r\n{}Content-Length: {}\r\n\r\n{}",
            &res.version(),
            &res.status_code(),
            &res.status_text(),
            &res.headers(),
            &response.body.unwrap().len(),
            &res.body(),
        )
    }
}

impl<'a> HttpResponse<'a> {
    pub fn new(
        status_code: &'a str,
        headers: Option<HashMap<&'a str, &'a str>>,
        body: Option<String>,
    ) -> HttpResponse<'a> {
        let mut response: HttpResponse<'a> = HttpResponse::default();
        if status_code != "200" {
            response.status_code = status_code.into();
        }
        response.status_text = match response.status_code {
            // 消息
            "100" => "Continue".into(),
            "101" => "Switching Protocols".into(),
            "102" => "Processing".into(),
            // 成功
            "200" => "OK".into(),
            "201" => "Created".into(),
            "202" => "Accepted".into(),
            "203" => "Non-Authoritative Information".into(),
            "204" => "No Content".into(),
            "205" => "Reset Content".into(),
            "206" => "Partial Content".into(),
            "207" => "Multi-Status".into(),
            // 重定向
            "300" => "Multiple Choices".into(),
            "301" => "Moved Permanently".into(),
            "302" => "Move Temporarily".into(),
            "303" => "See Other".into(),
            "304" => "Not Modified".into(),
            "305" => "Use Proxy".into(),
            "306" => "Switch Proxy".into(),
            "307" => "Temporary Redirect".into(),
            // 请求错误
            "400" => "Bad Request".into(),
            "401" => "Unauthorized".into(),
            "402" => "Payment Required".into(),
            "403" => "Forbidden".into(),
            "404" => "Not Found".into(),
            "405" => "Method Not Allowed".into(),
            "406" => "Not Acceptable".into(),
            "407" => "Proxy Authentication Required".into(),
            "408" => "Request Timeout".into(),
            "409" => "Conflict".into(),
            "410" => "Gone".into(),
            "411" => "Length Required".into(),
            "412" => "Precondition Failed".into(),
            "413" => "Request Entity Too Large".into(),
            "414" => "Request-URI Too Long".into(),
            "415" => "Unsupported Media Type".into(),
            "416" => "Requested Range Not Satisfiable".into(),
            "417" => "Expectation Failed".into(),
            "421" => "Misdirected Request".into(),
            "422" => "Unprocessable Entity".into(),
            "423" => "Locked".into(),
            "424" => "Failed Dependency".into(),
            "425" => "Too Early".into(),
            "426" => "Upgrade Required".into(),
            "449" => "Retry With".into(),
            "451" => "Unavailable For Legal Reasons".into(),
            // 服务器错误
            "500" => "Internal Server Error".into(),
            "501" => "Not Implemented".into(),
            "502" => "Bad Gateway".into(),
            "503" => "Service Unavailable".into(),
            "504" => "Gateway Timeout".into(),
            "505" => "HTTP Version Not Supported".into(),
            "506" => "Variant Also Negotiates".into(),
            "507" => "Insufficient Storage".into(),
            "509" => "Bandwidth Limit Exceeded".into(),
            "510" => "Not Extended".into(),
            "600" => "Unparseable Response Headers".into(),
            _ => "Not Found".into(),
        };
        response.headers = match &headers {
            Some(_h) => headers,
            None => {
                let mut header = HashMap::new();
                header.insert("Content-Type", "text/html");
                Some(header)
            }
        };
        response.body = body;

        response
    }

    fn version(&self) -> &str {
        self.version
    }

    fn status_code(&self) -> &str {
        self.status_code
    }

    fn  status_text(&self) -> &str {
        self.status_text
    }

    fn headers(&self) -> String {
        let map = self.headers.clone().unwrap();
        let mut headers_string = "".into();
        for (key, value) in map.iter() {
            headers_string = format!("{headers_string}{}:{}\r\n", key, value);
        }

        headers_string
    }

    fn body(&self) -> &str {
        match &self.body {
            Some(b) => b.as_str(),
            None => "",
        }
    }

    pub fn send_response(&self, write_stream: &mut impl Write) -> Result<()> {
        let response = self.clone();
        let response_string: String = String::from(response);
        let _ = write!(write_stream, "{}", response_string);

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_response_struct_creation_200() {
        let response_actual = HttpResponse::new(
            "200",
            None,
            Some("xxxx".into()),
        );
        let response_excepted = HttpResponse {
            version: "HTTP/1.1",
            status_code: "200",
            status_text: "OK",
            headers: {
                let mut h = HashMap::new();
                h.insert("Content-Type", "text/html");
                Some(h)
            },
            body: Some("xxxx".into()),
        };

        assert_eq!(response_actual, response_excepted);
    }

    #[test]
    fn test_response_struct_creation_404() {
        let response_actual = HttpResponse::new(
            "404",
            None,
            Some("xxxx".into()),
        );
        let response_excepted = HttpResponse {
            version: "HTTP/1.1",
            status_code: "404",
            status_text: "Not Found",
            headers: {
                let mut h = HashMap::new();
                h.insert("Content-Type", "text/html");
                Some(h)
            },
            body: Some("xxxx".into()),
        };

        assert_eq!(response_actual, response_excepted);
    }

    #[test]
    fn test_http_response_creation() {
        let response_excepted = HttpResponse {
            version: "HTTP/1.1",
            status_code: "404",
            status_text: "Not Found",
            headers: {
                let mut h = HashMap::new();
                h.insert("Content-Type", "text/html");
                Some(h)
            },
            body: Some("xxxx".into()),
        };

        let http_string: String = response_excepted.into();
        let actual_string = 
            "HTTP/1.1 404 Not Found\r\nContent-Type:text/html\r\nContent-Length: 4\r\n\r\nxxxx";

        assert_eq!(http_string, actual_string);
    }
}

运行命令 cargo test -p http,测试 http 成员库。

现在一共有 6 个测试,都通过了:

在这里插入图片描述

构建 server 模块

httpserver 需要引用 http 模块,打开 httpserver/Cargo.toml,在 [dependencies] 部分添加:

http = {path = "../http"}

在 httpserver/src 目录下新建 3 个文件:router.rs、server.rs、handler.rs。

在这里插入图片描述

它们是这样的关系:

  • main.rs 中的 main 函数调用 server
  • server 调用 router
  • router 调用 handler

打开 httpserver 成员库中的 server.rs,编写代码:

use super::router::Router;
use http::httprequest::HttpRequest;
use std::io::prelude::*;
use std::net::TcpListener;
use std::str;

pub struct Server<'a> {
    socket_addr: &'a str,
}

impl<'a> Server<'a> {
    pub fn new(socket_addr: &'a str) -> Self {
        Server { socket_addr }
    }

    pub fn run(&self) {
        let connection_listener = TcpListener::bind(self.socket_addr).unwrap();
        println!("Running on {}", self.socket_addr);

        for stream in connection_listener.incoming() {
            let mut stream = stream.unwrap();
            println!("Connection established!");

            let mut read_buffer = [0; 200];
            stream.read(&mut read_buffer).unwrap();
            let request: HttpRequest = String::from_utf8(read_buffer.to_vec()).unwrap().into();
            Router::route(request, &mut stream);
        }
    }
}

打开 httpserver 成员库中的 main.rs,编写代码:

mod server;
mod router;
mod handler;

use server::Server;

fn main() {
    let server = Server::new("localhost:3000");
    server.run();
}

目前,代码还不能运行,需要继续构建 router 和 handler 模块。

构建 router 模块

打开 httpserver 成员库中的 router.rs,编写代码:

use std::io::prelude::*;
use super::handler::{Handler, WebServerHandler, StaticPageHandler, PageNotFoundHandler};
use http::httprequest;
use http::httprequest::HttpRequest;
use http::httpresponse::HttpResponse;
pub struct Router;

impl Router {
    pub fn route(request: HttpRequest, stream: &mut impl Write) -> () {
        match request.method {
            httprequest::Method::Get => match &request.resource {
                httprequest::Resource::Path(s) => {
                    let route: Vec<&str> = s.split("/").collect();
                    match route[1] {
                        "api" => {
                            let response: HttpResponse = WebServerHandler::handle(&request);
                            let _ = response.send_response(stream);
                        }
                        _ => {
                            let response: HttpResponse = StaticPageHandler::handle(&request);
                            let _ = response.send_response(stream);
                        }
                    }
                }
            },
            _ => {
                let response: HttpResponse = PageNotFoundHandler::handle(&request);
                let _ = response.send_response(stream);
            }
        }
    }
}

构建 handler 模块

首先需要添加两个 crate:serde、serde_json,它们提供了 JSON 的处理和序列化/反序列化的能力。

打开 httpserver/Cargo.toml,在 [dependencies] 部分添加:

serde = {version = "1.0.131", features = ["derive"]}
serde_json = "1.0.72"

打开 httpserver 成员库中的 handler.rs,编写代码:

use std::collections::HashMap;
use std::env;
use std::fs;
use serde::{Deserialize, Serialize};
use http::httprequest;
use http::httprequest::HttpRequest;
use http::httpresponse::HttpResponse;

pub trait Handler {
    fn handle(request: &HttpRequest) -> HttpResponse;

    fn load_file(file_name: &str) -> Option<String> {
        let default_path = format!("{}/public", env!("CARGO_MANIFEST_DIR"));
        let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
        let full_path = format!("{}/{}", public_path, file_name);

        let contents = fs::read_to_string(full_path);
        contents.ok()
    }
}

pub struct WebServerHandler;
pub struct StaticPageHandler;
pub struct PageNotFoundHandler;

#[derive(Serialize, Deserialize)]
pub struct OrderStatus {
    order_id: i32,
    order_date: String,
    order_status: String,
}

impl WebServerHandler {
    fn load_json() -> Vec<OrderStatus> {
        let default_path = format!("{}/data", env!("CARGO_MANIFEST_DIR"));
        let data_path = env::var("DATA_PATH").unwrap_or(default_path);
        let full_path = format!("{}/{}", data_path, "orders.json");

        let json_contents = fs::read_to_string(full_path);
        let orders: Vec<OrderStatus> =
            serde_json::from_str(json_contents.unwrap().as_str()).unwrap();

        orders
    }
}

impl Handler for WebServerHandler {
    fn handle(request: &HttpRequest) -> HttpResponse {
        let httprequest::Resource::Path(s) = &request.resource;
        let route: Vec<&str> = s.split("/").collect();

        match route[2] {
            // localhost:3000/api/shipping/orders
            "shipping" if route.len() > 2 && route[3] == "orders" => {
                let body = Some(serde_json::to_string(&Self::load_json()).unwrap());
                let mut headers: HashMap<&str, &str> = HashMap::new();
                headers.insert("Content-Type", "application/json");

                HttpResponse::new("200", Some(headers), body)
            }
            _ => HttpResponse::new("404", None, Self::load_file("404.html"))
        }
    }
}

impl Handler for StaticPageHandler {
    fn handle(request: &HttpRequest) -> HttpResponse {
        let httprequest::Resource::Path(s) = &request.resource;
        let route: Vec<&str> = s.split("/").collect();

        match route[1] {
            "" => HttpResponse::new("200", None, Self::load_file("index.html")),
            "health" => HttpResponse::new("200", None, Self::load_file("health.html")),
            path => match Self::load_file(path) {
                Some(contents) => {
                    let mut map: HashMap<&str, &str> = HashMap::new();
                    if path.ends_with(".css") {
                        map.insert("Content-Type", "text/css");
                    } else if path.ends_with(".js") {
                        map.insert("Content-Type", "text/javascript");
                    } else {
                        map.insert("Content-Type", "text/html");
                    }

                    HttpResponse::new("200", Some(map), Some(contents))
                }
                None => HttpResponse::new("404", None, Self::load_file("404.html")),
            }
        }
    }
}
impl Handler for PageNotFoundHandler {
    fn handle(request: &HttpRequest) -> HttpResponse {
        HttpResponse::new("404", None, Self::load_file("404.html"))
    }
}

最后贴上各个 HTML 文件和 json 文件。

在 httpserver 目录下新建两个文件夹:data 和 public。

httpserver/data/orders.json:

[
  {
    "order_id": 1,
    "order_date": "21 Jan 2020",
    "order_status": "Delivered"
  },
  {
    "order_id": 2,
    "order_date": "2 Feb 2020",
    "order_status": "Pending"
  }
]

httpserver/public/404.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Not Found!</title>
</head>

<body>
    <h1>404 Error</h1>
    <p>Sorry the requested page does not exist</p>
</body>

</html>

httpserver/public/health.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <title>Health!</title>
</head>

<body>
    <h1>Hello welcome to health page!</h1>
    <p>This site is perfectly fine</p>
</body>

</html>

httpserver/public/index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="styles.css">
    <title>Index!</title>
</head>

<body>
    <h1>Hello, welcome to home page</h1>
    <p>This is the index page for the web site</p>
</body>

</html>

httpserver/public/styles.css:

h1 {
    color: red;
    margin-left: 25px;
}

现在,整个 httpserver 都建立好了。

在终端运行命令 cargo run -p httpserver,终端首先输出:

Running on localhost:3000

尝试在浏览器输入 URL,每次连接建立后,终端还会输出 Connection established!

各个页面的显示效果如下所示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

目前最流行的 Rust Web 框架有哪些? 需要极致性能的 Web 服务(如高频交易系统、实时 WebSocket 应用),以及想要一个“全家桶”解决方案,快速开发复杂系统。Axum 是 Rust Web 框架里比较新的,具体还没有身边的朋友使用过,如果有用过的小伙伴也可以分享一下使用感受。如果你是 Rust 新手,或者想快速构建一个简单、直观的 Web 应用,Rocket 你冲就完了,铁子。Poem 是一款轻量级的 Web 框架,非常适合中小型项目或快速迭代的应用开发。如果你需要一个高性能且专注异步编程的 Web 框架,Warp会是你的心头好。 阅读详情

相关推荐

Rust Web 全栈开发 - 4 HTTP Server

4. HTTP Server 4.1 构建server模块 因为HTTP Server需要引用HTTP Package,因此我们先在如下文件中添加依赖 /Users/qinjianquan/s1/httpserver/Cargo.toml [dependencies] http = {path = "../http"} 然后在如下文件夹中创建三个mod /Users/qinjianquan/s1/httpserver/src handler.rs, router.rs, server.rs 调用顺序:ma

weixin_51487151的博客 738

Rust Web 框架入门和对比

利用 Tokio、Tower 和 Hyper 建立强大的生态系统。很棒的开发者经验。仍处于 0.x 版本,因此可能会发生重大更改。强大、独立的生态系统。通过主要版本保证实现稳定的 API。很棒的文档。丰富的内置功能。不像以前那样积极发展。对于初学者来说仍然是一个不错的选择。Functional approach. 函数式方法。Very expressive. 非常富有表现力。靠近 Tokio、Tower 和 Hyper,拥有强大的生态系统。

xuejianxinokok的专栏 4917

从零开始使用 axum-server 构建 HTTP/HTTPS 服务

本文介绍了如何使用Rust生态中的axum-server框架构建高性能Web服务。教程从环境搭建开始,指导安装Rust工具链并创建基础HTTP服务,实现"Hello World"响应。进阶内容包括多路由处理、JSON请求响应、HTTPS安全配置(基于rustls)以及服务器优雅关闭机制。文章详细展示了每个功能的实现步骤,包括代码示例和测试方法,涵盖了从基础路由到生产环境所需的安全与稳定性功能。最后还提出了错误处理、性能优化等进一步学习方向,为开发者构建完整的Web服务提供了全面指导。

北京锋通科技有限公司的技术blog 1978

Rust Web(二)—— 自建HTTP Server

通过本次HTTP《简易》设计,可以更深刻地体会一些后端设计思想、Rust本身的特点以及基于HTTP协议的Server设计思路;测试整个相应,自定义响应实例中的请求体数据长度要保持一致;依照HTTP协议原理以及Rust本身的特性,先实现。解析后台的数据部分,进行相应的序列化和反序列化;内的空格不能忽略,或者进行进一步优化;这两个模块联合起来处理接收到的请求,其中。至此,HTTP的基本功能实现就到此为止;判定请求的合法性,适当返回错误反馈;在自定的目录下,创建两个子项目目录。不同的请求状况交由不同类型的句柄。.

AlanKSorata的博客 3184

【Rust http编程】Rust搭建webserver的底层原理与应用实战

Rust作为一门系统级编程语言,凭借其出色的性能、内存安全性和并发特性,在网络编程领域展现出强大的潜力。 本文将详细介绍如何使用Rust进行HTTP编程,涵盖从基础概念到实际应用的各个方面。

景天科技苑 2万+

rust-http-server:使用Rust构建的简单http服务器

HTTP服务器

Rust Web 全栈开发(三):使用 Actix 构建简单的 Web Service

Rust Web 全栈开发(三):使用 Actix 构建简单的 Web Service

ProgramNovice的博客 1514

避坑指南:用逐飞开源库驱动MT9V03X摄像头时容易忽略的3个细节(附TFT显示优化技巧)

本文详细解析了在智能汽车竞赛中使用MT9V03X摄像头(总钻风)时常见的硬件连接、图像采集和TFT显示优化问题。重点介绍了逐飞开源库中容易被忽略的三个关键细节,包括电源管理、看门狗设置和中断优先级配置,并提供了针对IPS200和TFT180显示屏的优化技巧,帮助参赛队伍提升图像处理效率和显示质量。

study的专栏 747

Rust Web 全栈开发(七):错误处理

Rust Web 全栈开发(七):错误处理

ProgramNovice的博客 970

STM32F103+W5500网络通信

STM32F103+W5500网络通信一、W5500模块(一)W5500以太网模块介绍(二)模块排针功能表二、 modbus协议(一)modbus协议原理(二)Modbus 通讯方式1.Modbus三种通讯方式2.在Modbus网络上传输3.以太网(modbus tcp/ip)三、参考资料 一、W5500模块 (一)W5500以太网模块介绍 D-W5500 EVB以太网模块是一款基于WIZnet W5500芯片的以太网模块,且性价比高的以太网模块。W5500是一款全硬件TCP/IP嵌入式以太网控制器,为

”一只特立独行的猪“的博客 1万+

心电图机原理及电路超详细讲解

前置心电放大电路(虚线框:这是隔离电路的分界,在隔离电路分析中知道,完整隔离包括信号隔离(脉宽调制)、控制信号隔离(光电耦合)和电源隔离)后级放大电路。

fcxgfdjy的博客 2万+

Rust Rosetta Code Web开发:使用Rust构建简单的Web服务器

在Web开发领域,Rust凭借其高性能和内存安全特性正逐渐成为开发者的新选择。Rust Rosetta Code项目提供了丰富的代码示例,其中就包含了使用Rust构建Web服务器的实用实现。本文将带你了解如何利用Rust Rosetta Code中的示例,快速搭建一个功能完备的Web服务器。 ### 一、Rust Web服务器基础架构 Rust标准库提供了构建网络应用的核心组件,通过`std:

gitblog_00882的博客 524

【硬件】LT3763中文手册

1)精确控制输入和输出电流;2) 3000:1真彩色PWM™调光;3) ±1.5%电压调节精度;4) ±6%电流调节精度;5)6V至60 V输入电压范围;6)宽输出范围最高可达55V;7)<2µA关断电流;8)用于负载电流热控制的控制引脚;9)输入和输出电流监控和限制;10)开路、短路和C/10故障检测;11)适用于LED应用的PWM驱动器输出;12)热增强型28引脚FE封装。

qq_39200110的博客 491

【Rust】从0到1开发和运行Web相关功能,并简单实现数据库连接和查询

上一篇文章我们已经学习和了解了Rust编程语言的大概知识点以及常见的应用场景, 因此,本篇文章博主将从Rust的Web开发,从0到1开发并运行Web相关功能,简单实现图片采集并设置系统壁纸。

小5聊的博客 7779

光学传播仿真软件:ASAP_(1).ASAP软件介绍与安装

ASAP(Advanced System for Analysis and Propagation)是一款强大的光学传播仿真软件,广泛应用于光学系统的设计、分析和优化。它提供了丰富的建模工具和仿真功能,可以帮助用户从简单的光学元件到复杂的光学系统进行仿真。:支持光学、机械、热学等多物理场的联合仿真。:采用高精度的算法,确保仿真结果的准确性。:内置了大量的光学元件,如透镜、反射镜、棱镜、光栅等。:提供直观的用户界面,支持多种建模和仿真方式。:支持使用脚本语言进行二次开发,增强软件的定制能力。

kkchenjj的博客 1601

3D 结构光相机工作原理全解 | 从编码投射到三维重建,原理 + 代码 + 工业实战一站式掌握

本文从3D结构光相机的整体架构出发,层层拆解了编码投射、相位计算、相位解包裹、三维重建的核心原理,补充了工业实战所需的公式推导、代码实现、参数调优、设备选型和案例落地,核心总结如下:相位是3D结构光成像的“灵魂”,其精度、稳定性直接决定3D重建的效果,包裹相位与绝对相位的区分的是工业实战的基础;编码投射的核心是“正弦条纹+格雷码条纹”组合,正弦条纹负责高精度相位提取,格雷码条纹辅助解包裹,适配工业场景的精度与稳定性需求;

getusushu的博客 418

30天拿下Rust之实战Web Server

随着互联网技术的飞速发展,Web服务器作为承载网站与应用的核心组件,其性能、稳定性和安全性都显得至关重要。Rust语言凭借其独特的内存安全保证、高效的性能以及丰富的生态系统,成为了构建现代Web服务器的理想选择。在本文中,我们不仅搭建了一个基础的Web服务器,还实现了路由、JSON请求、动态路由参数等功能。Rust凭借其严谨的安全模型、出色的性能和丰富的生态系统,为构建高效、安全的Web服务器提供了坚实的基础。

hope_wisdom的技术博客 3306

同步调相机启动与并网控制技术方案

同步调相机起动与并网控制技术方案采用背靠背变流器系统,通过静止变频器(SFC)策略实现无原动机驱动的同步电机从静止到同步转速的平稳起动。方案详细阐述了硬件自检、模块初始化、转子位置检测等关键环节,重点解决启动转矩为零、冲击电流大等挑战。系统基于STM32或C2000系列控制器,包含网侧/机侧变流器、检测保护单元及通信接口,通过励磁脉冲和电压响应分析实现高精度转子初始定位,为后续并网提供必要条件。代码级实现涵盖了ADC自检、数字IO验证及通信端口测试等核心功能。

huanghm88的专栏 308

Rust-esp32-std-demo HTTP服务器实现:构建嵌入式Web服务的完整指南

想要在ESP32微控制器上使用Rust语言快速构建一个功能完整的HTTP服务器吗?🚀 rust-esp32-std-demo项目为你提供了一个完美的起点!这个开源演示应用展示了如何在ESP32平台上利用Rust标准库的强大功能,轻松实现WiFi连接、网络通信和Web服务开发。无论你是嵌入式开发新手还是经验丰富的Rust开发者,这个项目都能帮助你快速掌握在资源受限的嵌入式设备上构建可靠Web服务的

gitblog_00878的博客 808

在 SAP BTP 中为 SAP AI Core 创建 Service Instance 和 Service Key 的完整实战指南

摘要:本文详细介绍了在SAP BTP上创建AI Core服务实例和密钥的关键步骤,这是实现ABAP系统与Gen AI大语言模型集成的核心技术环节。文章通过一个制造业财务问答场景的案例,系统梳理了从前提条件准备、架构理解到具体操作的全过程,重点解析了service instance创建、X.509证书配置等关键节点。特别强调了Extended计划选择、证书格式等易错点,为ABAP开发人员实现Gen AI场景提供了实用指南。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 273

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

从0到1搭建Rust Web应用:Are We Web Yet推荐技术栈实战教程

Are We Web Yet项目是Rust Web开发生态的权威指南,致力于评估Rust在Web开发领域的成熟度。本教程将基于该项目推荐的技术栈,带您快速构建高性能Rust Web应用,体验Rust带来的安全与速度优势。 ## 🚀 为什么选择Rust构建Web应用? Rust凭借内存安全、零成本抽象和高性能特性,已成为Web开发的理想选择。Are We Web Yet项目明确指出:**"Ye

gitblog_00809的博客 1094

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Rust Web入门(一):TCP 和 HTTP Server

本教程笔记来自 杨旭老师的 rust web 全栈教程,链接如下:https://www.bilibili.com/video/BV1RP4y1G7KF?p=1&vd_source=8595fbbf160cc11a0cc07cadacf22951学习 Rust Web 需要学习 rust 的前置知识可以学习杨旭老师的另一门教程https://www.bilibili.com/video/BV1hp4y1k7SV/?spm_id_from=333.999.0.0&vd_source=8595fbbf160cc

weixin_46463785的博客 2354

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Rust编程学习 - 如何快速构建一个单线程 web server

浏览器通过连续发送两个换行符来代表一个 HTTP 请求的结束,所以为了从流中获取一个请求,我们会读取行直到遇到一个空字符串的行。),它包含响应的 HTTP 版本、一个数字状态码(status code)用以总结请求的结果和一个描述之前状态码的文本原因短语(reason phrase)。这些协议的细节超出了本书的范畴,不过一个简单的概括会提供我们所需的信息。例如,很多系统限制它所能支持的同时打开的连接数,超出数量限制的新连接尝试会产生错误,直到一些现有的连接关闭为止。有了这些修改,运行我们的代码并进行请求。

2301_79651252的博客 1171

Dioxus 不只是前端:Server Functions 到底把全栈做到了哪一步

Dioxus 最容易被记住的标签,是“一套 Rust 代码跑 Web、桌面、移动”。但真到项目往前推进,你迟早会碰到另一个问题:接口怎么写,数据库怎么接,服务端逻辑放哪,SSR 和交互怎么串起来。到 Dioxus 0.7 这一代,Server Functions 已经把这条线接上了,只是很多人还没认真拆过它。这篇按 **2026 年 6 月 24 日** 能查到的 Dioxus 0.7 官方 Learn 文档和相关资料来写,也会顺手对照 Leptos,聊聊它到底把全栈做到了哪一步,适合什么项目,又还差什么。

weixin_47417831的博客 344
上一篇: Rust Web 全栈开发(一):构建 TCP Server
下一篇: Rust Web 全栈开发(三):使用 Actix 构建简单的 Web Service
UestcXiye
博客等级 码龄8年 9031粉丝 1306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值