使用原生rust搭建http服务器

一、搭建http服务器

        在8080端口创建tcp监听,引入handle_client请求处理模块,ThreadPool模块创建多线程。

use handle::handle_client;
use std::net::TcpListener;
use thread::ThreadPool;

fn main() -> std::io::Result<()> {
  let listener = TcpListener::bind("127.0.0.1:8080")?;

  let pool = ThreadPool::new(4);

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

    pool.execute(|| handle_client(stream));
  }

  Ok(())
}

二、创建多线程

        在ThreadPool用于创建Worker,发送要执行job的函数給Worker。Worker创建后用于监听ThreadPool发送过来job函数并执行。

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

pub struct Worker {
  pub id: usize,
  pub thread: thread::JoinHandle<()>,
}
impl Worker {
  fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
    // fn new(ide: usize, receiver: mpsc::Receiver<Job>) -> Worker {
    let thread = thread::spawn(move || {
      loop {
        let job = receiver.lock().unwrap().recv().unwrap();
        // println!("Worker {} got a job!", id);
        job();
      }
    });

    Worker { id, thread }
  }
}

pub struct ThreadPool {
  // workers: Vec<Worker>,
  sender: mpsc::Sender<Job>,
}

type Job = Box<dyn FnOnce() + Send + 'static>;

impl ThreadPool {
  pub fn new(size: usize) -> ThreadPool {
    let num = if size > 0 { size } else { 1 };

    let mut workers = Vec::with_capacity(num);
    let (sender, receiver) = mpsc::channel();
    let receiver = Arc::new(Mutex::new(receiver)); //线程安全

    for id in 0..size {
      // workers.push(Worker::new(id));
      workers.push(Worker::new(id, Arc::clone(&receiver)));
    }

    ThreadPool {
      // threads
      // workers,
      sender,
    }
  }

  pub fn execute<F>(&self, f: F)
  where
    F: FnOnce() + Send + 'static,
  {
    let job = Box::new(f);
    self.sender.send(job).unwrap();
  }
}

 三、请求处理模块

        在请求处理模块中引入MySQL模块,serde模块用于将数据库查询结果转换成json返回给前端。在tools中封装params_parse、send_file、send、json,分别用于获取请求类型、返回文件、返回字符串、返回json格式字符串。

use std::io::{BufReader, Read};
use std::net::TcpStream;
// use mysql::prelude::*;

mod tools;
use database::{mysql_connect, User};
use mysql::prelude::Queryable;
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Json<T>
where
  T: Serialize,
{
  arr: Vec<T>,
}

pub fn handle_client(mut stream: TcpStream) {
  let (method, path, _protocol) = tools::params_parse(&mut stream);

  if method == "GET" {
    // 处理 GET 请求
    if path == "/" {
      tools::send_file(&mut stream, String::from("view/index.html"));
    } else if path == "/home" {
      tools::send_file(&mut stream, String::from("view/home.html"));
    } else if path == "/user" {
      let mut conn = if let Ok(conn) = mysql_connect() {
        conn
      } else {
        tools::send(&mut stream, String::from("mysql connect error!"));
        return;
      };

      // 执行查询
      let res: Result<Vec<User>, _> = conn.query_map(
        "SELECT * from users",
        |(id, nick_name, avatar_url, job, introduce)| User {
          id,
          nick_name,
          avatar_url,
          job,
          introduce,
        },
      );

      let json = match res {
        Ok(value) => serde_json::to_string(&value).unwrap(),
        Err(_) => {
          let value = String::from("{arr:[]}");
          serde_json::to_string(&value).unwrap()
        }
      };

      tools::json(&mut stream, json);
      return;
    } else {
      tools::send(&mut stream, String::from("hello world!"));
      return;
    }
  } else if method == "POST" {
    // 处理 POST 请求
    let mut reader = BufReader::new(&mut stream);
    let mut buffer = vec![0; 1024];
    if let Err(_) = reader.read_to_end(&mut buffer) {
      tools::send(&mut stream, String::from("params parse error!"));
      return;
    };

    let request_body = String::from_utf8_lossy(&buffer).to_string();

    tools::send(&mut stream, request_body);
    return;
  } else {
    // 处理其他请求
    tools::send(&mut stream, String::from("Not Implemented"));
    return;
  }
}

四、tools.rs

        在写返回数据时,如果返回的内容在页面中没有正确显示,并且服务器已响应,那可能是没有正确的实现http协议,可能是Content-Type、Content-Length、charset等参数设置错误所导致的。

use std::fs;
use std::io::{BufRead, BufReader};
use std::io::{Read, Write};
use std::net::TcpStream;

// 解析请求类型,请求路径
pub fn params_parse(mut stream: &mut TcpStream) -> (String, String, String) {
  let mut reader = BufReader::new(&mut stream);
  let mut request_line = String::new();
  if let Err(_) = reader.read_line(&mut request_line) {
    send(&mut stream, String::from("params parse error!"));
    return (
      String::from(""),
      String::from(""),
      String::from(""),
    );
  };

  let tokens: Vec<&str> = request_line.split_whitespace().collect();

  let method = String::from(tokens[0]);
  let path = String::from(tokens[1]);
  let protocol = String::from(tokens[2]);

  println!(
    "Method: {}, Path: {}, Protocol: {}, token: {:?}",
    method, path, protocol, tokens
  );

  (method, path, protocol)
}

pub fn send_file(stream: &mut TcpStream, file_path: String) {
  // 如果打开文件失败
  let mut file = match fs::File::open(file_path) {
    Ok(file) => file,
    Err(_) => {
      let response = "HTTP/1.1 404 Not Found\r\n\r\n";
      stream.write(response.as_bytes()).unwrap();
      return;
    }
  };

  // thread::sleep(time::Duration::from_millis(1000));

  let mut contents = String::new();
  // 如果读取内容失败
  if let Err(_) = file.read_to_string(&mut contents) {
    let response = "HTTP/1.1 500 Internal Server Error\r\n\r\n";
    stream.write(response.as_bytes()).unwrap();
    return;
  }
  let response = format!(
    "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: {}\r\n\r\n{}",
    contents.len(),
    contents
  );
  stream.write(response.as_bytes()).unwrap();

  stream.flush().unwrap();
}

pub fn send(stream: &mut TcpStream, content: String) {
  let response = format!(
    "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: {}\r\n\r\n{}",
    content.len(),
    content
  );
  stream.write(response.as_bytes()).unwrap();
  stream.flush().unwrap();
}

pub fn json(stream: &mut TcpStream, json: String) {
  let response = format!(
    "HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: {}\r\n\r\n{}",
    json.len(),
    json
  );
  stream.write(response.as_bytes()).unwrap();
  stream.flush().unwrap();
}

五、数据库连接

use mysql::*;
use serde::Serialize;

#[derive(Serialize, Debug, PartialEq, Eq)]
pub struct User {
  pub id: i32,
  pub nick_name: String,
  pub avatar_url: String,
  pub job: String,
  pub introduce: String,
}

pub fn mysql_connect() -> std::result::Result<PooledConn, Box<dyn std::error::Error>> {
  let url = "mysql://root:root@localhost:3306/test";

  let pool = Pool::new(url)?;

  let conn = pool.get_conn()?;

  return Ok(conn);
}

使用到的第三方库mysql、serde、serde_json。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在 Rust 中编写 HTTP 服务器需要使用到网络编程库。目前有很多可用的库,其中一个比较流行的是 `hyper`。 下面是一个简单的使用 `hyper` 编写的 HTTP 服务器的例子: ``` use hyper::{service::service_fn, Body, Request, Response, Server}; use hyper::rt::{self, Future}; fn main() -> Result<(), Box<dyn std::error::Error>> { let addr = ([127, 0, 0, 1], 3000).into(); let new_svc = || { service_fn(|req: Request<Body>| -> Future<Response<Body>, hyper::Error> { let mut response = Response::new(Body::empty()); match (req.method(), req.uri().path()) { (&hyper::Method::GET, "/") => { *response.body_mut() = Body::from("Hello, World!"); } _ => { *response.status_mut() = hyper::StatusCode::NOT_FOUND; } } rt::spawn(future::ok(response)) }) }; let server = Server::bind(&addr) .serve(new_svc) .map_err(|e| eprintln!("server error: {}", e)); println!("Listening on http://{}", addr); rt::run(server); Ok(()) } ``` 这个例子中,我们定义了一个处理请求的函数 `service_fn`,并将它传递给 `Server` 的 `serve` 方法。`service_fn` 接受一个 `Request<Body>` 参数并返回一个 `Future<Response<Body>, hyper::Error>` 类型的值。在这个例子中,我们只处理了 `GET` 请求和根路径(`"/"`),对于其他请求,返回一个 `404 NOT FOUND` 状态码。 希望这个例子能帮到你。如果你想要学习更多关于 Rust 网络编程的内容,建议你参考官方文档:https://doc.rust-lang.org/std/net/index.html ### 回答2: Rust是一种系统级编程语言,具有高性能和内存安全的特点,非常适合编写高性能的HTTP服务器。下面是如何使用Rust编写HTTP服务器的一般步骤: 1. 使用Cargo创建一个新的Rust项目: ``` $ cargo new http_server ``` 2. 在`http_server`目录中,打开`Cargo.toml`文件并添加`hyper`依赖项: ``` [dependencies] hyper = "0.14" ``` 3. 在`src`文件夹中创建一个新的文件`main.rs`。 4. 在`main.rs`中导入必要的依赖项: ```rust use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server}; use std::convert::Infallible; ``` 5. 实现一个处理函数用来处理HTTP请求并返回响应,例如: ```rust async fn handle_request(_req: Request<Body>) -> Result<Response<Body>, Infallible> { // 处理请求并返回响应 let response = Response::new("Hello, World!".into()); Ok(response) } ``` 6. 在`main`函数中创建并启动HTTP服务器: ```rust #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // 创建一个服务 let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle_request)) }); // 创建HTTP服务器 let addr = ([127, 0, 0, 1], 3000).into(); let server = Server::bind(&addr).serve(make_svc); // 启动服务器并等待它运行 println!("Server running at http://{}", addr); server.await?; Ok(()) } ``` 7. 编译和运行项目: ``` $ cargo run ``` 现在,你已经用Rust编写了一个最简单的HTTP服务器,它将监听本地地址的3000端口,并在访问时返回"Hello, World!"。 ### 回答3: Rust是一种系统级编程语言,它提供了强大的内存安全性和高性能的特性。使用Rust编写HTTP服务器可以通过以下步骤完成: 1. 创建一个新的Rust项目。可以使用Cargo工具来快速创建一个新的Rust项目,命令为“cargo new <project_name>”。 2. 导入所需的依赖。HTTP服务器通常需要使用到一些库来处理HTTP协议和网络连接。在项目的Cargo.toml文件中,添加必要的依赖,例如“actix-web”或“hyper”。 3. 定义HTTP请求处理函数。在Rust中,可以使用各种框架来处理HTTP请求和生成响应。根据所选择的框架,编写HTTP请求处理函数来处理不同的URL路径和HTTP方法。 4. 创建HTTP服务器使用框架提供的功能,创建一个HTTP服务器实例,并将之前定义的HTTP请求处理函数绑定到服务器上的特定路由和端口。 5. 运行HTTP服务器使用“cargo run”命令来运行HTTP服务器,并监听指定的端口。一旦服务器启动成功,它将能够处理来自客户端的HTTP请求并返回响应。 6. 测试HTTP服务器使用任何HTTP请求测试工具(例如curl或Postman),向服务器发送HTTP请求并检查响应是否符合预期。 在实现HTTP服务器时,还需要考虑一些安全性和性能方面的问题,例如防止请求被劫持或拒绝服务攻击,以及使用并发处理来提高服务器的吞吐量。 总结起来,使用Rust编写HTTP服务器需要选择合适的框架和依赖,定义处理HTTP请求的函数,创建服务器实例,运行服务器并进行必要的测试和性能优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值