Server-Sent Events

SSE — технология отправки уведомлений сервера к браузеру. Клиент как-бы подключается к стриму обновлений и автоматически получает оповещения в случае новых событий. Server-Sent Events example

Опрос сервера (polling и long polling)

Идея такова: AJAX-приложение проверяет новые данные на сервере. Клиент создает запрос и ждет ответа. Сервер возвращает ответ, даже если обновлений нет. Так что процесс создает дополнительную нагрузку на канал.

Поэтому вспомогаетельно используются “висячие” запросы — если нет обновлений, сервер поддерживает открытое соединение. Когда появляются новые данные, сервер отправляет их и закрывает соединение. После этого процесс повторяется.

WebSocket или SSE

Технология Server-Sent Events открывает одностороннее соединение между сервером и клиентом, после чего сервер передает данные приложению, когда это потребуется (push notifications). Еще одно отличие — SSE обрабатываются непосредственно на стороне клиента, который только слушает канал.

WebSocket — другая технология обмена сообщениями между клиентом и сервером. Ее главное отличие в том, что связь двусторонняя, клиент принимает и отправляет сообщения. WebSocket scheme

Она подходит для продвинутых веб-приложений, игр, мессенджеров и т.д, где требуются связь в режиме реального времени.

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.

Подпишитесь на Хайлоад с помощью Google аккаунта
или закройте эту хрень