SSE (Server-Sent Events) — стандарт W3C, позволяющий серверу отправлять потоковые данные клиенту через одно HTTP-соединение в одностороннем режиме. В отличие от WebSocket, SSE работает поверх обычного HTTP и не требует специального протокола или библиотеки на стороне клиента. По данным спецификации W3C HTML Living Standard (2025), EventSource API поддерживается во всех современных браузерах, включая Chrome, Firefox, Safari и Edge.
Главное
SSE (Server-Sent Events) — технология, позволяющая веб-серверу отправлять данные клиенту в любой момент после установления соединения. Она стандартизирована WHATWG как часть HTML Living Standard и использует MIME-тип text/event-stream. SSE поддерживает передачу текстовых данных с возможностью указания идентификатора сообщения, типа события и задержки переподключения.
В отличие от WebSocket, который требует двустороннего протокола и upgrade-запроса, SSE работает поверх обычного HTTP. Сервер устанавливает заголовок Content-Type: text/event-stream, отправляет данные порциями и держит соединение открытым. Клиент получает данные через браузерный EventSource API, который автоматически парсит stream и генерирует события.
По данным CanIUse (2025), EventSource API поддерживается в 97.5% браузеров глобально. Не поддерживается в Internet Explorer и некоторых мобильных браузерах (Samsung Internet до версии 7.0). Для этих случаев существуют полифиллы, эмулирующие EventSource через XHR streaming. SSE не работает с HTTP/1.1 pipelining, но полностью совместим с HTTP/2 server push.
SSE был предложен как часть HTML5 спецификации в 2009 году под названием Server-Sent DOM Events. Первая реализация появилась в Opera 9.0, затем в Firefox 6.0 (2011), Chrome 9.0 (2011) и Safari 5.0 (2010). В 2015 году спецификация была выделена в отдельный раздел HTML Living Standard. Несмотря на десятилетнюю историю, SSE остаётся менее популярным, чем WebSocket, из-за односторонней природы.
Механизм работы SSE следующий: клиент создаёт экземпляр EventSource с URL серверного эндпоинта. Браузер отправляет GET-запрос с заголовком Accept: text/event-stream. Сервер отвечает статусом 200 OK и заголовком Content-Type: text/event-stream, после чего начинает отправлять данные в формате event-stream. Соединение остаётся открытым, пока сервер не отправит terminate-сигнал или клиент не вызовет close().
На стороне сервера данные отправляются порциями (chunked transfer encoding). Каждый блок данных — это текстовое сообщение, состоящее из строк полей (event, data, id, retry). Сервер может отправлять сообщения в любой момент времени, что делает SSE идеальным для уведомлений и обновлений статуса. Соединение не требует постоянного обмена heartbeat-пакетами (как WebSocket), хотя retry-поле управляет частотой переподключения.
По данным тестов производительности (2024), SSE обеспечивает throughput до 10 000 сообщений в секунду на одно соединение при размере сообщения 256 байт. На стороне сервера каждое SSE-соединение потребляет примерно 5–10 KB памяти, что позволяет одному серверу поддерживать 50 000+ одновременных соединений при 1 GB RAM. Это значительно меньше, чем у WebSocket, из-за отсутствия бинарного протокола.
Формат text/event-stream — простой текстовый протокол, где каждое сообщение состоит из именованных полей, разделённых символами новой строки. Каждое поле имеет формат «ИмяПоля: значение». Сообщения разделяются двумя символами новой строки (\n\n).
Поддерживаемые поля: event (тип события, по умолчанию message), data (строка данных, может быть многострочной), id (последний идентификатор события, сохраняется в Last-Event-ID), retry (время переподключения в миллисекундах). Комментарии начинаются с двоеточия (:) и игнорируются парсером, но могут использоваться для heartbeat.
| Поле | Обязательное | Назначение |
|---|---|---|
| event | Нет | Тип события (message по умолчанию) |
| data | Да | Строка данных сообщения |
| id | Нет | Идентификатор события для Last-Event-ID |
| retry | Нет | Задержка переподключения в мс |
: heartbeat comment
event: update
data: {"user": "Alice", "action": "typing"}
id: 1001
event: notification
data: {"type": "info", "text": "New version available"}
data: {"type": "action", "url": "/upgrade"}
retry: 3000
event: close
data: Session ended
EventSource API — встроенный интерфейс браузера для приёма SSE. Для создания соединения достаточно вызвать конструктор с URL эндпоинта. EventSource автоматически устанавливает соединение, обрабатывает переподключение и парсит входящие сообщения в JavaScript-события.
События EventSource: open (соединение установлено), message (получено сообщение без указания event), error (ошибка соединения). Для пользовательских событий (event: custom) можно использовать addEventListener с именем события. EventSource автоматически отправляет заголовок Last-Event-ID при переподключении, что позволяет серверу возобновить поток с прерванного места.
По данным документации MDN (2025), EventSource поддерживает CORS и передачу credentials (withCredentials). Для передачи custom-заголовков или тела запроса EventSource не подходит — требуется ручная реализация через fetch + ReadableStream. EventSource не поддерживает бинарные данные — только текст и JSON.
const eventSource = new EventSource('/api/events/stream');
eventSource.addEventListener('open', () => {
console.log('SSE connection opened');
});
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
renderUpdate(data);
});
eventSource.addEventListener('notification', (event) => {
const notification = JSON.parse(event.data);
showNotification(notification.text);
});
eventSource.addEventListener('error', (error) => {
console.error('SSE error:', error);
// Browser auto-reconnects
});
// Close connection
eventSource.close();
SSE и WebSocket — разные технологии для real-time коммуникации, каждая со своими сильными сторонами. WebSocket подходит для двустороннего обмена данными (чаты, игры, совместное редактирование), SSE — для односторонних потоков от сервера к клиенту (уведомления, ленты новостей, тикеры).
Ключевое различие — WebSocket требует upgrade-запроса с HTTP/1.1 до WebSocket-протокола (ws://), что может блокироваться корпоративными прокси. SSE работает поверх обычного HTTP, проходит через любые прокси и не требует специальной конфигурации сервера. SSE также проще в реализации — серверу не нужна дополнительная библиотека, достаточно правильно сформировать HTTP-ответ.
По данным сравнительного тестирования (2024), на одном серверном процессе SSE поддерживает на 30–50% больше соединений, чем WebSocket, из-за более простого протокола. Однако latency у SSE выше (50–200 мс против 10–50 мс у WebSocket) из-за того, что SSE использует chunked HTTP, а не полноценный двунаправленный поток с бинарными фреймами.
| Характеристика | SSE | WebSocket |
|---|---|---|
| Направление | Сервер → клиент | Двустороннее |
| Протокол | HTTP (text/event-stream) | ws:// / wss:// (RFC 6455) |
| Браузеры | 97.5% (встроенный EventSource) | 97% (встроенный WebSocket) |
| Данные | Только текст / JSON | Текст + бинарные (Blob, ArrayBuffer) |
| Обработка прокси | Проходит любые прокси | Требует настройки прокси |
| Переподключение | Автоматическое (браузер) | Ручная реализация |
| История | Last-Event-ID | Нет встроенной истории |
Реализация SSE на сервере не требует библиотек — достаточно установить правильные HTTP-заголовки и отправлять данные в формате text/event-stream. Рассмотрим пример на Node.js с использованием встроенного модуля http. Сервер устанавливает заголовки Content-Type и Cache-Control, после чего отправляет сообщения каждые N секунд.
По данным MDN Web Docs (2025), обязательные заголовки для SSE: Content-Type: text/event-stream, Cache-Control: no-cache и Connection: keep-alive. Без Cache-Control браузер может закешировать SSE-поток, что приведёт к остановке доставки. Connection: keep-alive явно указывает браузеру держать соединение открытым.
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
let eventId = 0;
const interval = setInterval(() => {
eventId++;
res.write(`id: ${eventId}\n`);
res.write(`event: update\n`);
res.write(`data: {"time": "${new Date().toISOString()}", "id":${eventId}}\n\n`);
}, 2000);
req.on('close', () => {
clearInterval(interval);
});
}).listen(3000);
from flask import Response, Flask
import time
import json
app = Flask(__name__)
@app.route('/stream')
def stream():
def generate():
event_id = 0
while True:
event_id += 1
data = json.dumps(
{'ticker': 'AAPL', 'price': 150.25})
yield f'id: {event_id}\nevent: price\ndata: {data}\n\n'
time.sleep(1)
return Response(generate(),
mimetype='text/event-stream')
Использование SSE в мобильных приложениях ограничено отсутствием нативной реализации EventSource для iOS и Android. На мобильных платформах SSE реализуется через сторонние библиотеки: на iOS — через URLSession с NSURLProtocol, на Android — через OkHttp с SSE-поддержкой (okhttp-sse). Для React Native и Flutter доступны пакеты, эмулирующие EventSource.
На iOS нативная реализация SSE возможна через URLSessionDataDelegate. При получении данных в методе urlSession(_:dataTask:didReceive:) приложение накапливает буфер и парсит event-stream формат вручную. По данным блога iOS-разработки (2024), потребление батареи при SSE на iOS на 40% ниже, чем при постоянном WebSocket-соединении, из-за отсутствия heartbeat-пакетов.
На Android OkHttp предоставляет класс EventSource.Factory для подписки на SSE-потоки. Android-приложения могут использовать SSE для уведомлений, когда FCM недоступен, или для синхронизации данных в фоновом режиме. SSE на Android хорошо работает с WorkManager для долгоживущих фоновых задач. По данным документации OkHttp (2025), okhttp-sse поддерживает автоматическое переподключение с кастомным listener.
Часто задаваемые вопросы
SSE — односторонняя передача (сервер → клиент) поверх HTTP, не требует библиотек на клиенте. WebSocket — двусторонняя передача с бинарным протоколом. SSE проще в реализации, WebSocket подходит для задач, где клиент тоже отправляет данные.
Нет, SSE передаёт только текстовые данные. Для бинарных данных (изображения, аудио) требуется кодировка Base64, которая увеличивает размер на 33%. Для бинарных потоков лучше использовать WebSocket.
EventSource автоматически переподключается при обрыве. Время задержки задаётся полем retry в потоке (по умолчанию 1000 мс). При переподключении браузер отправляет заголовок Last-Event-ID, позволяющий серверу восстановить поток с прерванного места.
Каждый браузер имеет ограничение на количество одновременных HTTP-соединений с одним доменом. Для HTTP/1.1 — 6–8 соединений на домен, для HTTP/2 — до 100. SSE использует одно соединение, поэтому конкуренции с другими запросами не возникает.
SSE подходит только для получения сообщений (входящие). Для отправки сообщений (исходящие) потребуется отдельный HTTP-запрос (POST). Для полноценного чата удобнее использовать WebSocket или Socket.IO с двусторонней связью в одном соединении.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также