在Rust中如何高效實現WebSocket? - ahmadrosid
如果您正在學習 Web 套接字,您可能會編寫一個聊天伺服器,但今天讓我們做一些不同的事情。這是我們今天要介紹的內容:學習如何編寫 Web 套接字伺服器以提高生產力。
建立 websocket 伺服器:
- 用於呈現 html 的索引端點。
- 用於傳送事件檔案更改的 Websocket 端點。
- 用於監聽來自 websocket 的事件檔案更改的 Javascript。
Actix Web
在 Rust 世界中,actix web 具有豐富的構建 Web 應用程式的功能。所以讓我們將這些庫添加到我們的專案中。
actix = "0.13" actix-web = "4" actix-web-actors = "4.1" |
接下來讓我們建立用於在索引頁面呈現 html 的處理程式。
接下來,讓我們建立處理程式,在索引頁上渲染html。
在這種情況下,我們將讀取index.html檔案作為html模板,並替換table.html檔案中的內容。
#[get("/")] async fn index() -> Result<HttpResponse> { let content = std::fs::read_to_string("table.html").expect("table not found"); let body = include_str!("index.html") .to_string() .replace("{content}", &content); Ok(HttpResponse::build(StatusCode::OK) .content_type("text/html; charset=utf-8") .body(body)) } |
併為我們的websocket伺服器新增入口點:
use websocket::FileWatcherWebsocket; async fn echo_ws(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> { ws::start(FileWatcherWebsocket::new(), &req, stream) } |
然後是websocket伺服器本身:
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(1); const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); const FILE_PATH: &'static str = "table.html"; pub struct FileWatcherWebsocket { hb: Instant, modified: SystemTime, } impl FileWatcherWebsocket { pub fn new() -> Self { let metadata = fs::metadata(FILE_PATH).unwrap(); let modified = metadata.modified().unwrap(); Self { hb: Instant::now(), modified, } } fn hb(&self, ctx: &mut <Self as Actor>::Context) { ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { println!("Websocket Client heartbeat failed, disconnecting!"); ctx.stop(); return; } let modified = fs::metadata(FILE_PATH).unwrap().modified().unwrap(); if modified.duration_since(act.modified).unwrap() > Duration::from_millis(0) { act.modified = modified; println!("Sending file changes event! {}", &FILE_PATH); ctx.text("file_changed") } ctx.ping(b""); }); } } impl Actor for FileWatcherWebsocket { type Context = ws::WebsocketContext<Self>; fn started(&mut self, ctx: &mut Self::Context) { self.hb(ctx); } } impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for FileWatcherWebsocket { fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) { match msg { Ok(ws::Message::Ping(msg)) => { self.hb = Instant::now(); ctx.pong(&msg); } Ok(ws::Message::Pong(_)) => { self.hb = Instant::now(); } Ok(ws::Message::Text(text)) => ctx.text(text), Ok(ws::Message::Binary(bin)) => ctx.binary(bin), Ok(ws::Message::Close(reason)) => { ctx.close(reason); ctx.stop(); } _ => ctx.stop(), } } } |
這個websocket伺服器將每隔一秒Ping一次客戶端,然後檢查最後修改的檔案table.html,如果有任何檔案變化,它將傳送*file_changed事件給客戶端。
客戶端
幸運的是,如今支援websocket客戶端開箱即用。因此,這裡是我們在index.html檔案中包含的客戶端的程式碼。
<script> let socket = new WebSocket("ws://localhost:8080/ws"); socket.onopen = function(e) { console.log("[open] Connection established"); console.log("Sending to server"); socket.send("start_connection"); }; socket.onmessage = function(event) { console.log(`[message] Data received from server: ${event.data}`); if (event.data == "file_changed") { window.location.reload(); } }; socket.onclose = function(event) { if (event.wasClean) { console.log(`[close] Connection closed, code=${event.code} reason=${event.reason}`); } else { console.log('[close] Connection died'); } }; socket.onerror = function(error) { console.log(error) }; </script> |
客戶端將監聽每個傳入的訊息,然後在有任何file_changed事件時重新載入瀏覽器。
這是一個非常簡單的實現,但為我節省了大量的時間。
結論
我認為每當我們學習新技術時,用它來解決我們的問題會非常有趣,在這種情況下,我用它來自動重新載入網頁,這讓我減少了除錯工作,我對此感到非常高興。
如果你想檢視完整的原始碼,你可以看這裡。
相關文章
- 在 Pisa-Proxy 中,如何利用 Rust 實現 MySQL 代理RustMySql
- 在Spring Boot中實現WebSocket實時通訊Spring BootWeb
- 在Lua中實現Rust物件的繫結Rust物件
- Lru在Rust中的實現, 原始碼解析Rust原始碼
- websocket是如何實現握手?Web
- 在Vue中使用WebSocket+HighCharts+Canvas實現高效能的頻譜瀑布圖VueWebCanvas
- 你知道在springboot中如何使用WebSocket嗎Spring BootWeb
- Lru-k在Rust中的實現及原始碼解析Rust原始碼
- Lfu快取在Rust中的實現及原始碼解析快取Rust原始碼
- 如何簡單高效的在程式碼中實現兩級快取的管理快取
- 在vue中使用SockJS實現webSocket通訊VueJSWeb
- WebSocket的故事(六)—— Springboot中,實現更靈活的WebSocketWebSpring Boot
- 使用Rust和Elixir實現高效的下發好友列表Rust
- websocket和http有什麼不同?以及websocket協議如何實現?WebHTTP協議
- 在Go中如何實現併發Go
- 【譯】Arc 在 Rust 中是如何工作的Rust
- Rust中實現JWT身份驗證RustJWT
- 帶有ttl的Lru在Rust中的實現及原始碼解析Rust原始碼
- 在Linux中,如何實現負載均衡?Linux負載
- Android在canvas中實現高效能的煙花/粒子特效AndroidCanvas特效
- 如何實現在react現有專案中嵌入Blazor?ReactBlazor
- 如何實現高效地IT服務管理
- 如何實現高效的團隊合作?
- Rust實現hotkeyRust
- 如何實現從 Redis 中訂閱訊息轉發到 WebSocket 客戶端RedisWeb客戶端
- 如何使用Jmeter實現WebSocket協議的介面測試JMeterWeb協議
- Rust 中的Box型別實現堆分配Rust型別
- WWDC 2018:在Swift中如何高效地使用集合Swift
- Flutter websocket 實現訊息推送FlutterWeb
- websocket學習和群聊實現Web
- NodeJS實現websocket代理機制NodeJSWeb
- angular + express 實現websocket通訊AngularExpressWeb
- 使用Java實現WebSocket通訊JavaWeb
- Rust中如何排序Vector?Rust排序
- Laravel 中引入 Swoole Websocket 並實現熱更新 Reload 程式碼LaravelWeb
- 在winform中如何實現雙向資料繫結?ORM
- 如何用OpenCV在Python中實現人臉檢測OpenCVPython
- WPF/C#:在WPF中如何實現依賴注入C#依賴注入