Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

WebSocket: чат блога

Обычный HTTP устроен как «вопрос — ответ»: клиент спросил, сервер ответил, соединение больше не нужно. Для чата этого мало: сервер должен сам отправить сообщение, как только его написал кто-то другой. WebSocket — протокол постоянного двустороннего соединения: после подключения и клиент, и сервер могут отправлять сообщения в любой момент. В Javalin WebSocket-маршрут объявляется методом config.routes.ws.

События соединения

Обработчик WebSocket получает объект WsConfig, на котором регистрируются реакции на события:

СобытиеКогда происходит
onConnectКлиент подключился
onMessageПришло текстовое сообщение, его даёт ctx.message()
onCloseСоединение закрыто
onErrorПроизошла ошибка

Класс чата

Чат хранит все открытые соединения и рассылает каждое сообщение всем:

// src/main/java/com/example/blog/Chat.java
package com.example.blog;
 
import io.javalin.websocket.WsConfig;
import io.javalin.websocket.WsContext;
 
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
 
public class Chat {
    private final Set<WsContext> sessions = ConcurrentHashMap.newKeySet();
 
    public void configure(WsConfig ws) {
        ws.onConnect(ctx -> {
            sessions.add(ctx);
            broadcast("Кто-то вошёл, в сети: " + sessions.size());
        });
        ws.onMessage(ctx -> broadcast(ctx.message()));
        ws.onClose(ctx -> {
            sessions.remove(ctx);
            broadcast("Кто-то вышел, в сети: " + sessions.size());
        });
    }
 
    private void broadcast(String text) {
        for (WsContext client : sessions) {
            if (client.session.isOpen()) {
                client.send(text);
            }
        }
    }
}

WsContext — контекст одного соединения, похожий на ctx в HTTP. Метод send отправляет строку клиенту; если передать объект, Javalin превратит его в JSON. Множество из ConcurrentHashMap безопасно для разных потоков.

Маршрут

Зарегистрируйте чат в Main:

// src/main/java/com/example/blog/Main.java
var chat = new Chat();
 
// внутри Javalin.create(config -> { ... })
config.routes.ws("/chat", chat::configure);

Проверка в браузере

curl не умеет WebSocket, зато он есть в любом браузере. Откройте http://localhost:8080 в двух вкладках, в каждой откройте консоль разработчика (F12) и выполните:

const ws = new WebSocket("ws://localhost:8080/chat");
ws.onmessage = e => console.log("Получено:", e.data);

Адрес начинается с ws:// вместо http://. Во второй вкладке отправьте сообщение командой ws.send("Привет, Анна!"). Обе вкладки покажут:

Получено: Кто-то вошёл, в сети: 1
Получено: Кто-то вошёл, в сети: 2
Получено: Привет, Анна!

Первая строка есть только в первой вкладке: она подключилась, когда была одна. Закройте одну вкладку — оставшаяся получит Кто-то вышел, в сети: 1.

Частая ошибка

WebSocket-маршрут не отвечает на обычные HTTP-запросы. Если открыть его адрес в браузере или вызвать curl, придёт 404:

curl -i http://localhost:8080/chat
HTTP/1.1 404 Not Found
Content-Type: text/plain

Endpoint GET /chat not found

Подключайтесь к нему только клиентом WebSocket и по адресу со схемой ws:// (или wss:// для HTTPS).

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.