- 支持试读
Axum与Websocket
我们将通过几个案例由浅入深地带你掌握Websocket及其应用场景 - 支持试读
简单ECHO服务
本章我们将使用 AXUM 和 Websocket 实现一个简单的 Echo 服务。 - 支持试读
并发读写的ECHO服务
本章我们将实现并发读写的 ECHO 服务 - 支持试读
Javscript实现WebSocket客户端
本章我们将给我们的 ECHO 服务实现一个客户端。你要明确的是,很多语言都能写 WebSocket 客户端,包括 Rust 在内。但基于我们当前的环境,我们使用 JavaScript(TypeScript) 配合 React(Next.js)来实现。 用户在线检测
本章我们将实现用户在线检测功能:用户登录之后,前端通过 WebSocket 来检测 JWT Token 是否依然有效。
简单ECHO服务
本章我们将使用 AXUM 和 Websocket 实现一个简单的 Echo 服务。
所谓 Echo 服务,就是无论客户端发送什么消息,服务端总是将该消息原样返回给客户端。
依赖
对于本章要实现的简单Echo服务而言,只依赖 tokio
和axum
:
为了使用 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();
}
- 使用
tokio::io::TcpListener::bind()
来事先创建好一个TCP监听器 - 然后使用
axum::serve()
函数来启动 axum 服务
我们是这样定义路由的:
let app = Router::new().route("/ws", get(websocket_handler));
- URL 路径是
/ws
- 使用的是
GET
方法
我们来看看这个 websocket_handler
函数。
websocket_handler()
函数
async fn websocket_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket)
}
- 参数:
ws: WebSocketUpgrade
,WebSocketUpgrade
是 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),
};
}
}
流程如下:
- 通过
while let
来接收客户端发送过来的消息。 Message
枚举定义了多种消息,我们只处理其中的:Close()
:客户端断开连接,使用break
退出循环Text()
:文本信息,我们接收到文本信息之后,再通过send()
方法原封不动的将其发回给客户端socket.send(Message::Text(text)).await.unwrap();
- 其它消息:本案例不做处理,只是简单的打印一下
- 这个函数结束,意味着与客户端之间的连接断开
测试
你可以用任何语言写一个 websocket 客户端来与我们这个简单ECHO服务进行通信,更为简单的是使用现成的工具,比如 Postman、在线测试工具等。这里我们使用WebSocket King,测试结果截图在本文开篇已经给出。
本章代码在01/simple-echo
分支。