域名 AXUM.RS 将于2025年10月到期。我们无意再对其进行续费,我们希望你能够接续这个域名,让更多 AXUM 开发者继续受益。现在,我们已启用新域名 AXUM.EU.ORG
  • 方案AXUM.RS 域名 = 3000
如果你有意接续这份 AXUM 情怀,请与我们取得联系。
说明:
  1. 如果有人购买 AXUM.RS 域名,或者该域名到期,本站将使用免费域名 AXUM.EU.ORG 继续提供服务。

简单ECHO服务

本章我们将使用 AXUM 和 Websocket 实现一个简单的 Echo 服务。

所谓 Echo 服务,就是无论客户端发送什么消息,服务端总是将该消息原样返回给客户端。

SimpleEcho

依赖

对于本章要实现的简单Echo服务而言,只依赖 tokioaxum

为了使用 Websocket,axum 要启用 ws feature

为了使用 Websocket,axum 要启用 ws feature

源码解析

main() 函数

#[tokio::main]
async fn main() {
    // 创建TCP监听器
    let listener = TcpListener::bind("0.0.0.0:56789").await.unwrap();
    // 定义路由
    let app = Router::new().route("/ws", get(websocket_handler));
    // 打印日志
    println!("监听于 {}", listener.local_addr().unwrap());
    // 启动 axum 服务
    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}

这是一个很简单、很典型的创建axum服务的代码。和之前版本不同的是,从 0.7 版本开始,去除了 axum::Server::bind(),而是需要:

  • 使用 tokio::io::TcpListener::bind() 来事先创建好一个TCP监听器
  • 然后使用 axum::serve() 函数来启动 axum 服务

我们是这样定义路由的:

let app = Router::new().route("/ws", get(websocket_handler));

我们来看看这个 websocket_handler 函数。

websocket_handler() 函数

async fn websocket_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
    ws.on_upgrade(handle_socket)
}
  • 参数:ws: WebSocketUpgradeWebSocketUpgrade是 axum 提供的一个 extract,用于将 HTTP 提升为 Websocket,建立 Webscoket 连接。注意,它只接受 GET 方法。
  • 返回值:impl IntoResponse。我们在《漫游AXUM之各种响应》一文中讨论过这个 trait。就是说,这个函数的返回值可以是所有可以作为 axum 响应的数据类型。
  • 函数体:很好,函数体只有一条语句:ws.on_upgrade(handle_socket)
    • on_upgrade()方法,用于完成协议的提升(HTTP 提升为 WEBSOCKET),并将Websocket消息流,传递给回调函数
    • 它的默认回调函数接收一个数据类型为WebSocket参数
      • 由于我们的回调函数 handle_socket的签名刚好和默认回调函数一致,所以可以简写成 ws.on_upgrade(handle_socket)
      • 完整的写法是:ws.on_upgrade(move |socket| handle_socket(socket))。当我们需要传递多个参数时,就需要用这种写法。后续章节有该写法的实例。
  • on_upgrade()方法,用于完成协议的提升(HTTP 提升为 WEBSOCKET),并将Websocket消息流,传递给回调函数
  • 它的默认回调函数接收一个数据类型为WebSocket参数
    • 由于我们的回调函数 handle_socket的签名刚好和默认回调函数一致,所以可以简写成 ws.on_upgrade(handle_socket)
    • 完整的写法是:ws.on_upgrade(move |socket| handle_socket(socket))。当我们需要传递多个参数时,就需要用这种写法。后续章节有该写法的实例。
  • 由于我们的回调函数 handle_socket的签名刚好和默认回调函数一致,所以可以简写成 ws.on_upgrade(handle_socket)
  • 完整的写法是:ws.on_upgrade(move |socket| handle_socket(socket))。当我们需要传递多个参数时,就需要用这种写法。后续章节有该写法的实例。

handle_socket() 函数

async fn handle_socket(mut socket: WebSocket) {
    while let Some(Ok(msg)) = socket.recv().await {
        match msg {
            Message::Close(_) => {
                println!("客户端断开连接");
                break;
            }
            Message::Text(text) => {
                println!("收到客户端文本消息:{}", text);
                // 向客户端原样发送收到的消息
                socket.send(Message::Text(text)).await.unwrap();
            }
            _ => println!("收到客户端消息:{:?}", msg),
        };
    }
}

流程如下:

测试

本章代码在01/simple-echo分支。

要查看完整内容,请先登录