Прямой ответ
WebSocket и SSE (Server-Sent Events) — два realtime-транспорта для веб-приложений (WebSocket — SSE — «realtime без polling» — long polling), но решают разные задачи; WebSocket (WebSocket — полнодуплексный (двунаправленный — клиент ↔ сервер — WebSocket — «клиент и сервер шлют одновременно» — realtime), бинарный/текстовый (данные любого формата — WebSocket — «бинарные данные» — realtime), persistent connection (одно соединение — WebSocket — «соединение живёт» — realtime), сложный reconnect (клиент сам переподключается — WebSocket — « reconnect вручную» — realtime); SSE (SSE — однонаправленный (сервер → клиент — SSE — «сервер шлёт, клиент слушает» — realtime), только текст (text/event-stream — SSE — «только текст» — realtime), HTTP-based (обычный HTTP — SSE — «SSE — это HTTP» — realtime), авто-reconnect (браузер сам переподключается — SSE — «SSE сам переподключается» — realtime); сравнение (таблица): | Критерий | WebSocket | SSE | | Направление | Двунаправленный | Однонаправленный (сервер→клиент) | | Данные | Бинарный/текст | Только текст | | Протокол | ws:// / wss:// | HTTP | | Reconnect | Вручную | Автоматически | | Браузеры | Все | Все (кроме IE) | | Сложность | Выше | Ниже | | Proxy/firewall | Могут блокировать | HTTP — проходят |; когда WebSocket (WebSocket: чат (клиент ↔ сервер — WebSocket — «чат — двунаправленный» — realtime), онлайн-игры (двунаправленный + бинарный — WebSocket — «игра — WebSocket» — realtime), collaboration (редакторы — WebSocket — «совместное редактирование» — realtime); когда SSE (SSE: уведомления (сервер → клиент — SSE — «уведомления — SSE» — realtime), прогресс-бары (сервер шлёт прогресс — SSE — «прогресс — SSE» — realtime), ленты событий (сервер → клиент — SSE — «лента — SSE» — realtime); когда НЕ WebSocket/SSE: простые случаи (polling достаточно — long polling — «polling проще» — техдолг), редкие обновления (раз в минуту — polling — long polling — «SSE для редких событий» — оверхед — техдолг); антипаттерны: WebSocket для однонаправленных задач (WebSocket — «WebSocket для уведомлений» — SSE проще — SSE — техдолг), SSE для двунаправленных (SSE — «SSE для чата» — WebSocket нужен — WebSocket — техдолг), WebSocket без reconnect (WebSocket — «WebSocket упал — не переподключился» — incident — reconnect обязателен — realtime).
Практика: WebSocket и SSE в FastAPI
WebSocket в FastAPI (WebSocket: @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept(); data = await websocket.receive_text(); await websocket.send_text(f"Echo: {data}") (WebSocket — «WebSocket в FastAPI» — realtime — полнодуплексный (клиент шлёт, сервер отвечает — WebSocket — «двунаправленный» — realtime); SSE в FastAPI (SSE: from sse_starlette.sse import EventSourceResponse @app.get("/events") async def events(): async def event_generator(): while True: yield {"data": "event"} await asyncio.sleep(1) return EventSourceResponse(event_generator()) (SSE — «SSE в FastAPI» — realtime — однонаправленный (сервер шлёт, клиент слушает — SSE — «сервер → клиент» — realtime); Reconnect (WebSocket — вручную): ws.onclose = () => setTimeout(connect, 1000) (WebSocket — «reconnect через секунду» — realtime — exponential backoff (retry — «1 с, 2 с, 4 с, 8 с» — retry — «не спамить сервер» — realtime); Reconnect (SSE — автоматически): const es = new EventSource("/events") (SSE — «SSE сам переподключается» — realtime — браузер делает автоматически — SSE — «SSE reconnect из коробки» — realtime — проще WebSocket (SSE — «SSE проще» — техдолг); Heartbeat (WebSocket): setInterval(() => ws.send("ping"), 30000) (WebSocket — «ping каждые 30 с» — realtime — «соединение живо» — realtime — без heartbeat — proxy/firewall могут закрыть соединение — WebSocket — «heartbeat обязателен» — realtime); Мониторинг (метрики: «WebSocket connections», «SSE clients» — метрики — алерты — алерты — «соединения падают» — incident — «реконнекты растут» — метрика — метрики — «проблемы с соединением» — incident).
Практический сценарий: уведомления и чат — выбор транспорта
Контекст: веб-приложение с двумя realtime-фичами: (1) уведомления (сервер → клиент — «новое уведомление» — SSE), (2) чат (клиент ↔ сервер — WebSocket); решение — разные транспорты для разных задач: (1) уведомления — SSE (SSE — однонаправленный (сервер → клиент — SSE — «уведомления — SSE» — realtime — авто-reconnect (SSE — «SSE сам переподключается» — realtime — проще WebSocket (SSE — «SSE проще» — техдолг); (2) чат — WebSocket (WebSocket — двунаправленный (клиент ↔ сервер — WebSocket — «чат — WebSocket» — realtime — бинарный (можно бинарные данные — WebSocket — «файлы в чате» — realtime); (3) reconnect: SSE — автоматически (SSE — «SSE сам» — realtime), WebSocket — вручную (WebSocket — «ws.onclose → reconnect» — realtime); (4) heartbeat: WebSocket (WebSocket — «ping каждые 30 с» — realtime — «соединение живо» — realtime); (5) мониторинг (метрики — «SSE clients», «WebSocket connections» — метрики — алерты — алерты); результат: уведомления — SSE (проще, авто-reconnect — SSE — техдолг), чат — WebSocket (двунаправленный — WebSocket — realtime); вывод: разные транспорты для разных задач (SSE — однонаправленный, WebSocket — двунаправленный — realtime — ADR — «один транспорт для всего» — не всегда оптимально — техдолг).
FAQ
WebSocket против SSE — в чём главная разница? WebSocket: двунаправленный (клиент ↔ сервер — WebSocket — «двунаправленный» — realtime), SSE: однонаправленный (сервер → клиент — SSE — «однонаправленный» — realtime); WebSocket: бинарный/текст (WebSocket — «бинарный» — realtime), SSE: только текст (SSE — «только текст» — realtime); WebSocket: reconnect вручную (WebSocket — «reconnect вручную» — realtime), SSE: авто-reconnect (SSE — «SSE сам» — realtime); когда WebSocket: чат/игры/collaboration (WebSocket — «двунаправленный» — realtime), когда SSE: уведомления/прогресс/ленты (SSE — «однонаправленный» — realtime); «один транспорт для всего» — не всегда (разные задачи — разные транспорты — realtime — ADR — техдолг).
Когда использовать SSE вместо WebSocket? SSE вместо WebSocket: однонаправленные задачи (сервер → клиент — SSE — «уведомления/прогресс/ленты» — realtime), простота нужна (SSE проще — SSE — «SSE проще WebSocket» — техдолг), авто-reconnect нужен (SSE — «SSE сам переподключается» — realtime), HTTP-friendly (SSE — HTTP — SSE — «proxy/firewall проходят» — realtime); когда НЕ SSE: двунаправленные задачи (чат — WebSocket — «SSE для чата» — не работает — realtime), бинарные данные (WebSocket — «SSE только текст» — realtime); «SSE везде» — антипаттерн (двунаправленные задачи — WebSocket — WebSocket — ADR — техдолг).
Как обрабатывать reconnect в WebSocket и SSE?
WebSocket: вручную (ws.onclose = () => setTimeout(connect, 1000) — WebSocket — «reconnect через секунду» — realtime — exponential backoff (retry — «1 с, 2 с, 4 с» — retry); SSE: автоматически (SSE — «SSE сам переподключается» — realtime — браузер делает — SSE — «SSE reconnect из коробки» — realtime); heartbeat: WebSocket (setInterval(() => ws.send("ping"), 30000) — WebSocket — «ping каждые 30 с» — realtime — «соединение живо» — realtime); «reconnect не нужен» — миф (соединения падают — incident — reconnect обязателен — realtime — ADR — техдолг).
WebSocket и SSE — поддерживаются ли всеми браузерами? WebSocket: все современные браузеры (WebSocket — «WebSocket везде» — realtime — Chrome/Firefox/Safari/Edge — WebSocket — поддержка с 2011 года — realtime — «WebSocket зрелый» — WebSocket); SSE: все, кроме IE (SSE — «SSE везде, кроме IE» — realtime — IE не поддерживает — SSE — «IE — fallback на polling» — long polling — Edge поддерживает (Chromium-based — SSE — «Edge — SSE ок» — realtime); «SSE не поддерживается» — миф (все современные — SSE — только IE — realtime — «IE — редкость в 2026» — техдолг — fallback на polling (long polling — «IE — polling» — realtime — ADR — «SSE для современных браузеров» — техдолг).
Как масштабировать WebSocket и SSE на несколько серверов? WebSocket: stateful (соединение привязано к серверу — WebSocket — «WebSocket на сервере 1» — realtime — sticky sessions (ёмкость — «клиент всегда на сервер 1» — WebSocket — или pub/sub (Redis/Kafka — Redis — «сервер 1 шлёт в Redis, сервер 2 читает» — realtime); SSE: stateless (HTTP — SSE — «SSE — обычный HTTP» — realtime — load balancer распределяет (ёмкость — «SSE проще масштабировать» — SSE — но pub/sub тоже нужен (события на всех серверах — Redis — «событие → все серверы → все клиенты» — realtime); «масштабирование без pub/sub» — не работает (событие на сервере 1 — клиент на сервере 2 не получит — realtime — pub/sub обязателен — Redis — ADR — техдолг).
Источники: MDN WebSockets API, MDN Server-sent events, HTML spec — ссылки в шапке.