Обмен WebSocket-сообщениями в Rust
После перехода стороны обмениваются кадрами в обе стороны. Клиент шлет короткий текст, сервер читает его в цикле и может сразу записать ответ в тот же сокет.
На сервере при текстовом кадре
hello отправим ответ hi:
use axum::{
extract::ws::{Message, WebSocket, WebSocketUpgrade},
response::IntoResponse,
routing::get,
Router,
};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(ws_entry));
let listener = match tokio::net::TcpListener::bind("127.0.0.1:3000").await {
Ok(l) => l,
Err(e) => {
eprintln!("{}", e);
return;
}
};
match axum::serve(listener, app).await {
Ok(()) => {}
Err(e) => eprintln!("{}", e),
}
}
async fn ws_entry(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket)
}
async fn handle_socket(mut socket: WebSocket) {
loop {
let msg = match socket.recv().await {
Ok(Some(m)) => m,
Ok(None) => break,
Err(e) => {
eprintln!("{}", e);
break;
}
};
match msg {
Message::Text(text) if text == "hello" => {
match socket.send(Message::Text("hi".into())).await {
Ok(()) => println!("replied"),
Err(e) => {
eprintln!("{}", e);
break;
}
}
}
Message::Text(text) => {
println!("skip {}", text);
}
Message::Close(_) => break,
_ => {}
}
}
}
Запись ответа тоже возвращает результат. Сбой отправки выходим из цикла, чтобы не читать из уже мертвого канала.
Клиент шлет hello и ждет текст
в ответ:
use futures_util::{SinkExt, StreamExt};
use tokio_tungstenite::{connect_async, tungstenite::Message};
#[tokio::main]
async fn main() {
let url = "ws://127.0.0.1:3000";
let (stream, _response) = match connect_async(url).await {
Ok(pair) => pair,
Err(e) => {
eprintln!("{}", e);
return;
}
};
let (mut write, mut read) = stream.split();
match write.send(Message::Text("hello".into())).await {
Ok(()) => {}
Err(e) => {
eprintln!("{}", e);
return;
}
}
match read.next().await {
Some(Ok(Message::Text(text))) => println!("answer {}", text),
Some(Ok(_)) => println!("unexpected"),
Some(Err(e)) => eprintln!("{}", e),
None => println!("closed"),
}
}
Дан текст запроса:
let text = "hello";
На сервере при таком кадре отправьте
hi и выведите done
после успешной записи.
Дан текст запроса:
let text = "hello";
На клиенте отправьте его, прочитайте
один кадр и напечатайте строку
hi при варианте текста.
Скажите, почему ответ на сервере удобно писать сразу после чтения в том же асинхронном блоке.