SSE — технология отправки уведомлений сервера к браузеру. Клиент как-бы подключается к стриму обновлений и автоматически получает оповещения в случае новых событий.
Опрос сервера (polling и long polling)
Идея такова: AJAX-приложение проверяет новые данные на сервере. Клиент создает запрос и ждет ответа. Сервер возвращает ответ, даже если обновлений нет. Так что процесс создает дополнительную нагрузку на канал.
Поэтому вспомогаетельно используются “висячие” запросы — если нет обновлений, сервер поддерживает открытое соединение. Когда появляются новые данные, сервер отправляет их и закрывает соединение. После этого процесс повторяется.
WebSocket или SSE
Технология Server-Sent Events открывает одностороннее соединение между сервером и клиентом, после чего сервер передает данные приложению, когда это потребуется (push notifications). Еще одно отличие — SSE обрабатываются непосредственно на стороне клиента, который только слушает канал.
WebSocket — другая технология обмена сообщениями между клиентом и сервером. Ее главное отличие в том, что связь двусторонняя, клиент принимает и отправляет сообщения.
Она подходит для продвинутых веб-приложений, игр, мессенджеров и т.д, где требуются связь в режиме реального времени.
Server-Sent Events передаются по HTTP, не требуется реализация специального протокола или отдельного сервера. Тогда как для WebSocket требуется полная дуплексная связь и дополнительные сервера с протоколом. Кроме этого SSE поддерживает переподключение, идентификаторы событий и произвольные события.
Реализация на стороне клиента
API Server-Sent Events построен на интерфейсе EventSource. Поэтому для открытия нового соединения с сервером нужно создать объект EventSource. Он задает путь к скрипту, который создает события:
var evtSource = new EventSource("ssedemo.php");
# если скрипт лежит на другом домене, то так:
var evtSource = new EventSource("//api.example.com/ssedemo.php", { withCredentials: true } );
# SSE на стороне клиента реализуется на JavaScript, скрипт сервера — на PHP, ASP, NodeJS или другом удобном языке
После инициализации соединения клиент может принимать сообщения:
evtSource.onmessage = function(e) {
var newElement = document.createElement("li");
newElement.innerHTML = "message: " + e.data;
eventList.appendChild(newElement);
}
# Принимает сообщения от сервера и добавляет текст сообщения в список HTML
А при помощи директивы addEventListener() также можно принимать события:
evtSource.addEventListener("ping", function(e) {
var newElement = document.createElement("li");
var obj = JSON.parse(e.data);
newElement.innerHTML = "ping at " + obj.time;
eventList.appendChild(newElement);
}, false);
# Вызывается автоматически, когда сервер отправляет событие ping, клиент парсит JSON и выводит результат
Кроме собственных событий также используются стандартные ивенты:
- onopen — при успешном открытии соединения;
- onerror – в случае сбоя соединения;
- onmessage — новое сообщение.
Простейший пример стандартных событий будет выглядеть так:
var eventSource = new EventSource('sse_demo');
eventSource.onopen = function(e) {
console.log("Открыто соединение");
};
eventSource.onerror = function(e) {
if (this.readyState == EventSource.CONNECTING) {
console.log("Ошибка соединения, переподключение");
} else {
console.log("Состояние ошибки: " + this.readyState);
}
};
eventSource.onmessage = function(e) {
console.log("Сообщение: " + e.data);
};
# Попытка переподключения в случае ошибки
Учтите, что событий может быть несколько.
Реализация на стороне сервера
Скрипт, который отправляет события, должен использовать MIME-тип text/event-stream. Каждое сообщение — простой текст, разделенный двумя символами новой линии (\n\n)(пустой строкой).
Наш бэкенд на PHP будет иметь вид:
date_default_timezone_set("America/New_York");
header("Content-Type: text/event-stream\n\n");
$counter = rand(1, 10);
while (1) {
# Отправлять событие "ping" каждую секунду
echo "event: ping\n";
$curDate = date(DATE_ISO8601);
echo 'data: {"time": "' . $curDate . '"}';
echo "\n\n";
$counter--;
if (!$counter) {
echo 'data: Время сообщения ' . $curDate . "\n\n";
$counter = rand(1, 10);
}
ob_end_flush();
flush();
sleep(1);
}
# Рандомно выводит сообщение с временной меткой
Самое главное
Используйте Server-Sent Events если нужна односторонняя связь между сервером и клиентом — вывод уведомлений, обновлений. Для более сложных задач подойдет WebSocket.