#socket-io #tower #hyper #axum #tower-service #tower-middleware

socketioxide

Socket IO 服务器在 Rust 中的实现,作为 Tower 服务

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

Download history 2504/week @ 2024-05-04 1897/week @ 2024-05-11 2398/week @ 2024-05-18 2685/week @ 2024-05-25 2878/week @ 2024-06-01 3205/week @ 2024-06-08 2344/week @ 2024-06-15 2078/week @ 2024-06-22 1911/week @ 2024-06-29 2423/week @ 2024-07-06 2626/week @ 2024-07-13 2547/week @ 2024-07-20 3229/week @ 2024-07-27 2982/week @ 2024-08-03 2833/week @ 2024-08-10 2289/week @ 2024-08-17

11,698 每月下载量
用于 4 个 Crates (3 个直接使用)

MIT 许可

485KB
8K SLoC

Socketioxide 🚀🦀

Rust 中 socket.io 服务器实现,与 Tower 生态系统和 Tokio stack 集成。它集成到任何基于 tower 的服务器框架中,如 AxumWarpSalvoVizHyper。使用 tower-http 在 socketioxide 上添加任何其他基于 tower 的中间件,如 CORS、授权、压缩等。

⚠️ 此 crate 正在积极开发中,API 仍不稳定。

Crates.io Documentation CI

功能

计划中的功能

  • 其他适配器以在服务器实例之间共享状态(如 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