WebSocket представляет собой полнодуплексный протокол связи поверх TCP, обеспечивающий постоянное соединение между клиентом и сервером. В отличие от HTTP, где каждое взаимодействие инициируется отдельным запросом, WebSocket создаёт устойчивый канал, по которому данные могут передаваться в обе стороны без повторного установления соединения.
Основные свойства WebSocket, критичные для STOMP.js:
STOMP.js использует WebSocket как транспортный слой, поверх которого реализуется текстовый протокол STOMP (Simple Text Oriented Messaging Protocol). WebSocket в этой архитектуре отвечает только за доставку байтового потока, не интерпретируя содержимое сообщений.
STOMP.js не заменяет WebSocket, а работает поверх него, добавляя семантику сообщений:
WebSocket выполняет роль канала передачи кадров STOMP, где каждый STOMP-фрейм передаётся как текстовое сообщение WebSocket.
Таким образом разделяются уровни:
Подключение STOMP.js к серверу начинается с создания
WebSocket-соединения. В современных версиях библиотеки используется
параметр brokerURL, который указывает адрес WebSocket
endpoint.
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000
});
В этом случае STOMP.js самостоятельно создаёт WebSocket:
new WebSocket('ws://localhost:8080/ws');
Если требуется более тонкий контроль над созданием соединения, используется фабрика:
const client = new Client({
webSocketFactory: () => new WebSocket('ws://localhost:8080/ws')
});
Этот подход применяется при интеграции с кастомными транспортами или дополнительной логикой инициализации соединения.
WebSocket-соединение в контексте STOMP проходит несколько этапов.
Первичный запрос выполняется как HTTP с заголовком:
Upgrade: websocket
Connection: Upgrade
Сервер подтверждает переход на WebSocket-протокол, после чего соединение перестаёт быть HTTP.
После успешного handshake создаётся постоянный канал передачи данных. На этом этапе STOMP ещё не активен.
STOMP.js отправляет первый протокольный кадр:
CONNECT
accept-version:1.2
host:localhost
heart-beat:4000,4000
WebSocket передаёт этот фрейм как текстовое сообщение.
Сервер отвечает:
CONNECTED
version:1.2
heart-beat:4000,4000
После этого канал считается полностью активным.
Каждое сообщение STOMP инкапсулируется в WebSocket frame. WebSocket не анализирует содержимое, поэтому STOMP остаётся независимым уровнем.
Пример отправки сообщения:
client.publish({
destination: '/queue/messages',
body: JSON.stringify({
text: 'hello'
})
});
На уровне WebSocket это превращается в:
SEND
destination:/queue/messages
{"text":"hello"}\0
Особенности:
\0;WebSocket сам по себе не знает о подписках. Эта логика реализуется в STOMP.
client.subscribe('/topic/chat', (message) => {
const body = JSON.parse(message.body);
console.log(body);
});
При подписке отправляется STOMP-фрейм:
SUBSCRIBE
id:sub-0
destination:/topic/chat
Далее сервер маршрутизирует сообщения и отправляет их через WebSocket:
MESSAGE
destination:/topic/chat
subscription:sub-0
message-id:001
{"text":"hi"}\0
WebSocket лишь доставляет этот поток клиенту, не интерпретируя заголовки.
Несмотря на то что WebSocket уже поддерживает постоянное соединение, STOMP добавляет механизм heartbeat для контроля живости соединения.
Конфигурация:
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000
Формат:
\n;Важно, что WebSocket не заменяет heartbeat STOMP, так как:
При разрыве TCP или WebSocket соединения STOMP.js переходит в
состояние DISCONNECTED.
Основные сценарии:
В конфигурации:
reconnectDelay: 5000
STOMP.js автоматически инициирует новый WebSocket:
webSocketFactory: () => new WebSocket('ws://localhost:8080/ws')
и повторяет цикл:
WebSocket передаёт данные как текстовые фреймы. В контексте STOMP это всегда строка следующего вида:
COMMAND\n
header:value\n
header:value\n
\n
body\0
Особенности:
\n;\0.Хотя WebSocket поддерживает бинарные данные, классический STOMP поверх STOMP.js работает только с текстовыми фреймами.
Причины:
Передача бинарных данных возможна только через кодирование:
WebSocket поддерживает указание subprotocol при инициализации:
new WebSocket('ws://localhost:8080/ws', ['v10.stomp', 'v11.stomp', 'v12.stomp']);
Это позволяет серверу определить, что поверх WebSocket будет использоваться STOMP и его версия.
В STOMP.js этот параметр может задаваться через фабрику:
webSocketFactory: () =>
new WebSocket('ws://localhost:8080/ws', ['v12.stomp'])
WebSocket в связке со STOMP обычно подключается к серверному брокеру:
Архитектура выглядит следующим образом:
WebSocket при этом остаётся транспортом без логики маршрутизации.
STOMP-фреймы передаются как plain text, но WebSocket гарантирует:
Однако WebSocket не гарантирует:
Все эти функции реализуются STOMP или брокером.
Использование WebSocket даёт следующие характеристики:
Ограничения:
STOMP.js допускает создание нескольких независимых клиентов:
const chatClient = new Client({ brokerURL: 'ws://localhost:8080/chat' });
const notifClient = new Client({ brokerURL: 'ws://localhost:8080/notifications' });
Каждый клиент:
WebSocket не предоставляет мультиплексирование на уровне протокола, поэтому разделение происходит на уровне STOMP-клиентов.