在当今实时应用盛行的时代,WebSocket 已成为实现双向通信的标配技术。无论是聊天应用、实时协作工具,还是金融交易系统,都需要稳定高效的 WebSocket 服务。本文将带你从零开始,使用 Rust 和 Tokio 框架构建一个高性能的 WebSocket 服务器,并深入探讨其核心实现原理。
为什么选择 Rust 和 Tokio?
Rust 的优势
Rust 以其内存安全、零成本抽象和高并发性能著称。对于需要处理大量并发连接的 WebSocket 服务器来说,Rust 的以下特性尤为重要:
- 无数据竞争:所有权系统和借用检查器确保线程安全
- 零成本抽象:高级抽象不带来运行时开销
- 极小运行时:没有垃圾回收,适合低延迟场景
Tokio 的优势
Tokio 是 Rust 最流行的异步运行时,提供:
- 基于事件驱动的异步 I/O
- 工作窃取调度器
- 丰富的生态(包括 WebSocket 支持)
项目搭建
首先创建新项目:
cargo new websocket-server
cd websocket-server
添加依赖到 Cargo.toml:
[package]
name = "websocket-server"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.0", features = ["full"] }
tokio-tungstenite = "0.20"
futures-util = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
tracing-subscriber = "0.3"
核心实现
1. 基础 WebSocket 服务器
让我们从最简单的 WebSocket 服务器开始:
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::accept_async;
use futures_util::{SinkExt, StreamExt};
use tracing::{info, warn, error};
async fn handle_connection(stream: TcpStream, addr: std::net::SocketAddr) {
info!("新连接: {}", addr);
let ws_stream = match accept_async(stream).await {
Ok(stream) => stream,
Err(e) => {
error!("WebSocket握手失败 {}: {}", addr, e);
return;
}
};
let (mut write, mut read) = ws_stream.split();
// 处理消息循环
while let Some(msg) = read.next().await {
match msg {
Ok(msg) => {
if msg.is_text() || msg.is_binary() {
info!("收到消息 from {}: {:?}", addr, msg);
// 回声响应
if let Err(e) = write.send(msg).await {
warn!("发送失败 {}: {}", addr, e);
break;
}
} else if msg.is_close() {
info!("连接关闭: {}", addr);
break;
}
}
Err(e) => {
error!("读取错误 {}: {}", addr, e);
break;
}
}
}
info!("连接断开: {}", addr);
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化日志
tracing_subscriber::fmt::init();
let addr = "127.0.0.1:8080";
let listener = TcpListener::bind(addr).await?;
info!("WebSocket服务器监听在: {}", addr);
loop {
match listener.accept().await {
Ok((stream, addr)) => {
tokio::spawn(async move {
handle_connection(stream, addr).await;
});
}
Err(e) => {
error!("接受连接失败: {}", e);
}
}
}
}
2. 添加连接管理
简单的回声服务器不够实用,我们需要添加连接管理功能:
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use uuid::Uuid;
type ClientId = Uuid;
type ClientMap = Arc<RwLock<HashMap<ClientId, ClientSender>>>;
#[derive(Clone)]
struct ClientSender {
sender: tokio::sync::mpsc::UnboundedSender<String>,
}
struct ConnectionManager {
clients: ClientMap,
}
impl ConnectionManager {
fn new() -> Self {
Self {
clients: Arc::new(RwLock::new(HashMap::new())),
}
}
async fn add_client(&self, client_id: ClientId, sender: ClientSender) {
let mut clients = self.clients.write().await;
clients.insert(client_id, sender);
info!("客户端 {} 已连接,当前连接数: {}", client_id, clients.len());
}
async fn remove_client(&self, client_id: &ClientId) {
let mut clients = self.clients.write().await;
clients.remove(client_id);
info!("客户端 {} 已断开,当前连接数: {}", client_id, clients.len());
}
async fn broadcast(&self, message: &str, exclude: Option<ClientId>) {
let clients = self.clients.read().await;
for (client_id, client) in clients.iter() {
if let Some(exclude_id) = exclude {
if *client_id == exclude_id {
continue;
}
}
if let Err(e) = client.sender.send(message.to_string()) {
warn!("向客户端 {} 发送消息失败: {}", client_id, e);
}
}
}
async fn send_to(&self, client_id: &ClientId, message: &str) -> Result<(), String> {
let clients = self.clients.read().await;
match clients.get(client_id) {
Some(client) => {
client.sender.send(message.to_string())
.map_err(|e| format!("发送失败: {}", e))
}
None => Err("客户端不存在".to_string()),
}
}
}
3. 消息协议设计
定义清晰的消息协议:
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum ClientMessage {
#[serde(rename = "auth")]
Auth { token: String },
#[serde(rename = "chat")]
Chat {
room: String,
message: String,
},
#[serde(rename = "join")]
JoinRoom { room: String },
#[serde(rename = "leave")]
LeaveRoom { room: String },
#[serde(rename = "ping")]
Ping,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum ServerMessage {
#[serde(rename = "welcome")]
Welcome { client_id: String },
#[serde(rename = "error")]
Error { code: u32, message: String },
#[serde(rename = "chat")]
Chat {
from: String,
room: String,
message: String,
timestamp: u64,
},
#[serde(rename = "room_joined")]
RoomJoined { room: String, members: Vec<String> },
#[serde(rename = "room_left")]
RoomLeft { room: String },
#[serde(rename = "pong")]
Pong { timestamp: u64 },
}
impl ServerMessage {
pub fn to_json(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
4. 完整的 WebSocket 处理器
整合所有组件:
struct WebSocketHandler {
manager: Arc<ConnectionManager>,
rooms: Arc<RwLock<HashMap<String, HashSet<ClientId>>>>,
}
impl WebSocketHandler {
fn new() -> Self {
Self {
manager: Arc::new(ConnectionManager::new()),
rooms: Arc::new(RwLock::new(HashMap::new())),
}
}
async fn handle_client(
&self,
client_id: ClientId,
mut ws_stream: tokio_tungstenite::WebSocketStream<tokio::net::TcpStream>,
) {
// 创建消息通道
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
// 注册客户端
self.manager.add_client(client_id, ClientSender { sender: tx }).await;
// 发送欢迎消息
let welcome = ServerMessage::Welcome {
client_id: client_id.to_string()
};
if let Err(e) = ws_stream.send(welcome.to_json().into()).await {
error!("发送欢迎消息失败: {}", e);
return;
}
// 处理接收