Несколько WebSocket-соединений в Rust
Один прием может обслуживать много переходов подряд. Если обработку держать в том же блоке, что и согласие, второй гость ждет, пока первый закончит читать свой сокет.
Каждый переход можно передать в фоновую задачу: согласие возвращается сразу, а цикл чтения живет отдельно:
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(|socket| async move {
tokio::spawn(async move {
handle_socket(socket).await;
});
})
}
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) => println!("client {}", text),
Message::Close(_) => break,
_ => {}
}
}
}
Два клиента могут слать текст одновременно: у каждого свой цикл в своей задаче. Сбой в одном сокете не останавливает прием новых переходов на порту.
Добавим счетчик на экран при старте каждой фоновой обработки:
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT: AtomicU64 = AtomicU64::new(1);
async fn ws_entry(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(|socket| async move {
let id = NEXT.fetch_add(1, Ordering::Relaxed);
tokio::spawn(async move {
println!("start {}", id);
handle_socket(socket).await;
println!("stop {}", id);
});
})
}
Дан текст кадра:
let text = "a";
Запустите две клиентские программы и убедитесь, что сервер печатает оба кадра, не дожидаясь конца первого соединения.
Скажите, зачем согласие на переход отдают быстро, а чтение переносят в задачу исполнителя.
Дан порт:
let port = 3000;
Опишите, что произойдет с уже запущенной задачей, если клиент оборвет канал без кадра закрытия.