23 个版本 (13 个破坏性更新)
0.14.1 | 2024 年 8 月 14 日 |
---|---|
0.14.0 | 2024 年 6 月 26 日 |
0.13.1 | 2024 年 5 月 8 日 |
0.12.0 | 2024 年 3 月 20 日 |
0.3.0 | 2023 年 6 月 17 日 |
#10 in WebSocket
11,698 每月下载量
用于 4 个 Crates (3 个直接使用)
485KB
8K SLoC
Socketioxide
🚀🦀
Rust 中 socket.io
服务器实现,与 Tower
生态系统和 Tokio stack
集成。它集成到任何基于 tower 的服务器框架中,如 Axum
、Warp
、Salvo
、Viz
或 Hyper
。使用 tower-http
在 socketioxide 上添加任何其他基于 tower 的中间件,如 CORS、授权、压缩等。
⚠️ 此 crate 正在积极开发中,API 仍不稳定。

功能
- 集成
- 支持任何基于 tower 的中间件
- 命名空间和动态命名空间
- 房间
- 带有确认的发送和接收
- 二进制包
- 轮询和 WebSocket 传输
- 扩展以向套接字添加自定义数据
- 使用流进行内存高效的 HTTP 有效负载解析
- 灵活的 axum-like API 用于处理事件。带有提取器从处理器中提取数据
- 经过官方 端到端测试套件 的良好测试
- 支持的 Socket.io 版本

计划中的功能
- 其他适配器以在服务器实例之间共享状态(如 redis 适配器),目前仅实现了内存适配器
- 套接字重新连接时的状态恢复

示例
聊天应用 💬(见完整示例 此处)
io.ns("/", |s: SocketRef| {
s.on("new message", |s: SocketRef, Data::<String>(msg)| {
let username = s.extensions.get::<Username>().unwrap().clone();
let msg = Res::Message {
username,
message: msg,
};
s.broadcast().emit("new message", msg).ok();
});
s.on(
"add user",
|s: SocketRef, Data::<String>(username), user_cnt: State<UserCnt>| {
if s.extensions.get::<Username>().is_some() {
return;
}
let num_users = user_cnt.add_user();
s.extensions.insert(Username(username.clone()));
s.emit("login", Res::Login { num_users }).ok();
let res = Res::UserEvent {
num_users,
username: Username(username),
};
s.broadcast().emit("user joined", res).ok();
},
);
s.on("typing", |s: SocketRef| {
let username = s.extensions.get::<Username>().unwrap().clone();
s.broadcast()
.emit("typing", Res::Username { username })
.ok();
});
s.on("stop typing", |s: SocketRef| {
let username = s.extensions.get::<Username>().unwrap().clone();
s.broadcast()
.emit("stop typing", Res::Username { username })
.ok();
});
s.on_disconnect(|s: SocketRef, user_cnt: State<UserCnt>| {
if let Some(username) = s.extensions.get::<Username>() {
let num_users = user_cnt.remove_user();
let res = Res::UserEvent {
num_users,
username: username.clone(),
};
s.broadcast().emit("user left", res).ok();
}
});
});
Axum 的 Echo 实现 🏓
use axum::routing::get;
use serde_json::Value;
use socketioxide::{
extract::{AckSender, Bin, Data, SocketRef},
SocketIo,
};
use tracing::info;
use tracing_subscriber::FmtSubscriber;
fn on_connect(socket: SocketRef, Data(data): Data<Value>) {
info!("Socket.IO connected: {:?} {:?}", socket.ns(), socket.id);
socket.emit("auth", data).ok();
socket.on(
"message",
|socket: SocketRef, Data::<Value>(data), Bin(bin)| {
info!("Received event: {:?} {:?}", data, bin);
socket.bin(bin).emit("message-back", data).ok();
},
);
socket.on(
"message-with-ack",
|Data::<Value>(data), ack: AckSender, Bin(bin)| {
info!("Received event: {:?} {:?}", data, bin);
ack.bin(bin).send(data).ok();
},
);
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing::subscriber::set_global_default(FmtSubscriber::default())?;
let (layer, io) = SocketIo::new_layer();
io.ns("/", on_connect);
io.ns("/custom", on_connect);
let app = axum::Router::new()
.route("/", get(|| async { "Hello, World!" }))
.layer(layer);
info!("Starting server");
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
Ok(())
}
贡献和反馈/问题
欢迎任何贡献,请随意打开一个问题或 PR。如果您想贡献但不知道从哪里开始,您可以查看 问题。
如果您有任何问题或反馈,请在该 讨论页面 上打开一个线程。
许可协议 🔐
此项目受 MIT 许可协议 的许可。
依赖项
~6–35MB
~526K SLoC