WebSocket библиотеки Rust: tokio-tungstenite и async-tungstenite

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

WebSocket библиотеки Rust: tokio-tungstenite и async-tungstenite

WebSocket — это протокол, обеспечивающий двустороннюю связь между клиентом и сервером по одному TCP-соединению. В экосистеме Rust для работы с WebSocket используются две основные библиотеки: tokio-tungstenite (асинхронная на базе Tokio) и async-tungstenite (асинхронная, но с поддержкой разных рантаймов). Обе построены на основе низкоуровневой библиотеки tungstenite, которая реализует сам протокол WebSocket.

Зачем использовать WebSocket в Rust?

Rust — системный язык, который идеально подходит для создания высоконагруженных сетевых приложений. WebSocket библиотеки Rust позволяют:

  • Создавать чаты, игры, системы уведомлений в реальном времени
  • Обрабатывать тысячи одновременных соединений с минимальным потреблением памяти
  • Использовать безопасность памяти Rust на уровне компиляции
  • Интегрироваться с другими асинхронными библиотеками (например, с HTTP-серверами)

Установка и настройка

Добавьте зависимость в Cargo.toml:

[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.21"
futures-util = "0.3"

Для async-tungstenite:

[dependencies]
async-tungstenite = "0.25"
async-std = "1.12"

Создание WebSocket клиента на Rust

Пример простого клиента, который подключается к серверу и отправляет сообщение:

use futures_util::{SinkExt, StreamExt};
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() {
    let url = "wss://echo.websocket.org";
    let (mut ws_stream, _) = connect_async(url).await.expect("Ошибка подключения");

    println!("Подключено к серверу");

    // Отправляем сообщение
    ws_stream.send(Message::Text("Привет, WebSocket!".into())).await.unwrap();

    // Получаем ответ
    if let Some(msg) = ws_stream.next().await {
        let msg = msg.unwrap();
        if let Message::Text(text) = msg {
            println!("Получено: {}", text);
        }
    }

    ws_stream.close(None).await.unwrap();
}

Создание WebSocket сервера на Rust

Сервер, который принимает соединения и эхо-ответы:

use tokio::net::TcpListener;
use tokio_tungstenite::accept_async;
use futures_util::{SinkExt, StreamExt};
use tokio_tungstenite::tungstenite::Message;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
    println!("Сервер запущен на ws://127.0.0.1:8080");

    while let Ok((stream, addr)) = listener.accept().await {
        tokio::spawn(async move {
            println!("Новое соединение: {}", addr);

            let ws_stream = accept_async(stream).await.unwrap();
            let (mut sender, mut receiver) = ws_stream.split();

            while let Some(msg) = receiver.next().await {
                match msg {
                    Ok(Message::Text(text)) => {
                        println!("Сообщение от {}: {}", addr, text);
                        sender.send(Message::Text(format!("Эхо: {}", text))).await.unwrap();
                    }
                    Ok(Message::Close(_)) => break,
                    _ => {}
                }
            }

            println!("Соединение с {} закрыто", addr);
        });
    }
}

Основные возможности библиотек

  • Поддержка TLS/SSL — защищенные соединения через WSS (требуется включение feature tls)
  • Автоматическое переподключение — можно реализовать с помощью циклов и таймеров
  • Пинг/понг фреймы — для поддержания соединения
  • Бинарные сообщения — работа с Message::Binary
  • Разделение на отправителя и получателя — через .split()

Работа с бинарными данными

Пример отправки и получения бинарных сообщений:

use tokio_tungstenite::tungstenite::Message;

// Отправка бинарных данных
let binary_data = vec![0x00, 0x01, 0x02, 0x03];
ws_stream.send(Message::Binary(binary_data)).await.unwrap();

// Получение бинарных данных
if let Message::Binary(data) = msg {
    println!("Получено {} байт", data.len());
}

Сравнение tokio-tungstenite и async

Рекомендаций