上一篇【Rust程式語言兼具Python與C優點】介紹Rust程式語言特點,這次我們透過程式實際見證它的威力,以Rust開發一個網站,可以提供各式的網頁(Html、CSS、JS、圖片...)服務。
開發簡單的網站步驟如下:
cargo new simple_web_server
use std::{
fs,
io::{prelude::*, BufReader},
net::{TcpListener, TcpStream},
thread,
time::Duration,
};
fn main() {
// 建立 TCP Listener, 監聽 8000 埠(port)
let listener = TcpListener::bind("127.0.0.1:8000").unwrap();
// 接收到請求,由 handle_connection 處理
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
// 讀取請求(Request)內容
let buf_reader = BufReader::new(&mut stream);
let request_line = buf_reader.lines().next().unwrap().unwrap();
let mut file_name = "";
let mut status_line = "";
// 判斷第一行內容
match request_line.as_str() {
"GET / HTTP/1.1" | "GET /index.html HTTP/1.1" => {
// 準備回應內容
status_line = "HTTP/1.1 200 OK";
// 讀取 index.html 內容
file_name = "index.html";
},
"GET /register.html HTTP/1.1" => {
// 準備回應內容
status_line = "HTTP/1.1 200 OK";
// 讀取 index.html 內容
file_name = "register.html";
},
_ => {
// 準備回應內容:404 無此網頁
status_line = "HTTP/1.1 404 NOT FOUND";
// 讀取 404.html 內容
file_name = "404.html";
},
}
let contents = fs::read_to_string(file_name).unwrap();
// 計算 index.html 內容長度
let length = contents.len();
// 回應要求
let response =
format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
stream.write_all(response.as_bytes()).unwrap();
}
說明如下:
. 讀取瀏覽器提出的請求(Request)內容:逐行讀取。
// 讀取請求(Request)內容
let buf_reader = BufReader::new(&mut stream);
let request_line = buf_reader.lines().next().unwrap().unwrap();
. 判斷第一行內容:如為【/】即傳回首頁(index.html),如為【/register.html】傳回register.html,其他則傳回404.html,表無此網頁。
// 判斷第一行內容
match request_line.as_str() {
"GET / HTTP/1.1" | "GET /index.html HTTP/1.1" => {
...
file_name = "index.html";
},
"GET /register.html HTTP/1.1" => {
...
file_name = "register.html";
},
_ => {
...
file_name = "404.html";
},
}
. 回應要求,回傳Html檔案內容。
let contents = fs::read_to_string(file_name).unwrap();
// 計算 index.html 內容長度
let length = contents.len();
// 回應要求
let response =
format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
stream.write_all(response.as_bytes()).unwrap();
程式就這麼簡單,接著在專案資料夾放入Html檔案,如下:
進行建置與執行。
cd simple_web_server
cargo run
在瀏覽器輸入http://localhost:8000 ,畫面如下。
可點選【註冊】,可連結到register.html。
!
可輸入http://localhost:8000/1 ,會得到404.html內容。
https://ithelp.ithome.com.tw/upload/images/20240819/20001976zYqssJUIi6.png
以上程式有個缺點,每個請求都要依序處理,前面的請求未處理完,後面的請求就必須等待,因此,我們可以使用Rust強項,加入並行處理(Concurrency),讓每個請求(Request)都可以獨立執行,不互相影響。Amazon開發一個套件Tokio,可以讓我們輕鬆開發並行處理的應用程式。
步驟如下:
cargo new tokio_web_server
cargo add tokio -F full
use tokio::net::TcpListener;
use tokio::net::TcpStream;
use tokio::fs;
use tokio::io::{self, BufReader, AsyncBufReadExt};
use tokio::io::AsyncReadExt; // for read_to_end()
#[tokio::main]
async fn main() {
// 建立 TCP Listener, 監聽 8000 埠(port)
let listener = TcpListener::bind("127.0.0.1:8000").await.unwrap();
// 接收到請求,由 handle_connection 處理
loop {
let (stream, _) = listener.accept().await.unwrap();
tokio::spawn(async {
handle_connection(stream).await;
});
}
}
使用Rust開發生產力雖不及Python,但是,效能、記憶體的掌控及安全性,就不是Python可以比擬的,大家一起來玩玩吧。
Rust雖然具備諸多優點,但學習曲線陡峭,因此筆者最近剛完成【Rust 最佳入門與實戰】一書的撰寫,希望能與讀者分享Rust開發心得,內容除了Rust語言的入門、設計典範(Design patterns)外,也著重應用的探討,包括網頁、WebAssembly、桌面程式、資料庫、機器學習/深度學習、區塊鏈…等。
本文相關範例放在這裡,【Rust 最佳入門與實戰】還有各式各樣的範例供大家下載。