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/chatHTTP/1.1 404 Not Found
Content-Type: text/plain
Endpoint GET /chat not found
Подключайтесь к нему только клиентом WebSocket и по адресу со схемой ws:// (или wss:// для HTTPS).