Server Sent Events
Server-sent events (SSE) - это технология, которая позволяет получать обновления веб - страницы с сервера в реальном режиме времени.
Таким образом, используя данную технологию, мы имеем возможность поддерживать постоянное обновление содержимого страницы.
Данная технология предоставляет много различных возможностей для html5 приложений, к примеру, если нужно постоянно обновлять информацию о ходе каких-то событий, выполнения длительных операций на стороне сервера, отображения информации о заказах, состоянии подключения к серверу, и так далее.
Server-Send Events vs. WebSockets
Существование такой технологии как WebSocket, которая предоставляет клиенту и серверу держать постоянное подключение, и работать в условиях приближенных к реальному времени, ставит под вопрос, в чем заключается преимущество и необходимость использования SSE технологии перед WebSockets?
Основная причина, почему SSE технология не столь известна как WebSockets, заключается в том, что ее появление было позже, чем WebSockets.
WebSockets - обеспечивает более богатый протокол для выполнения двунаправленной, полнодуплексной связи, и используется преимущественно для создания игр, чатов и всех тех приложений где скорость обмена между клиентом и сервером должна быть приведена максимально к режиму реального времени, а также в тех случаях, когда может понадобиться синхронизация между открытыми вкладками одного сайта, или одного сайта в разных браузерах.
Server-sent Events обеспечивает получение сообщений только со стороны сервера, и для работы этой технологии используется http протокол, в отличие от WebSockets.
Как это работает?
JavaScript API
Для начала работы необходимо определить источник получаемых сообщений. В качестве источника может выступать php скрипт.
var source = new EventSource("demo.php");
Далее для работы необходимо назначить обработчики событий. Объект EventSource поддерживает три стандартных типа событий, это:
Обработчик события | Тип обработчика событий (addEventListener) |
---|---|
onopen | open |
onmessage | message |
onerror | error |
Так объект EventSource содержит такие свойства:
Наименование | Значение |
---|---|
CONNECTING | Числовое значение - 0. Соединение не было установлено, или было закрыто и восстанавливается |
OPEN | Числовое значение - 1. Соединение открыто и были привязаны события. |
CLOSED | Числовое значение - 2. Соединения закрыто и попытка переподключения не выполняется. |
Кроме стандартных событий, можно организовать пользовательские события. Пользовательские события задаются определенной строкой ответа с сервера. Способы их определения будут описан ниже, при рассмотрении форматов сообщения которые формирует сервер.
Следующим шагом необходимо привязать события:
source.addEventListener("message", function(e) { console.log(e.data); }, false); source.addEventListener("open", function(e) { // Connection was opened. }, false); source.addEventListener("error", function(e) { if (e.readyState === EventSource.CLOSED) { // Connection was closed. } }, false);
Когда с сервера поступают новые сообщения, срабатывает событие onmessage. Данные которые были отправлены сервером, пишутся в атрибут события e.data.
В случае если связь с сервером разрывается, повторное подключение осуществляется примерно через 3 секунды. Интервал повторного подключения регулируется настройками сервера.
Формат ответа с сервера
Источник событий должен отвечать определенным стандартам для разграничения типа содержимого.
В нашем, demo.php для начала работы необходимо задать заголовок, который будет отдаваться с сервера:
header("Content-Type: text/event-stream"); // необходимо для работы Server-sent Events
header("Cache-Control: no-cache"); // рекомендуется делать для того, чтобы ответ не кэшировался.
Формат сообщения сервера должен содержать ключевое слово "data:", далее текст сообщения, и завершаться двумя символами "\n\n", пример:
data: Your message \n\n
В случае, если необходимо передать большое сообщение, тогда нужно в каждую строку с префиксом "data:" добавлять одни символ "\n", а последней строке два "\n\n".
data: first line\n
data: second line\n\n
В случае если необходимо отправить данные в формате JSON, можно использовать такой способ
data: {\n data: "msg": "hello world",\n data: "id": 12345\n data: }\n\n source.addEventListener("message", function(e) { var data = JSON.parse(e.data); console.log(data.id, data.msg); }, false);
В случае если необходимо сервером отправлять уникальные события, со стороны сервера в ответе с помощью ключевого слова "event:" задается название события.
event: eventName\n data: {"name": "value"}\n\n source.addEventListener("message", function(e) { var data = JSON.parse(e.data); console.log(data.msg); }, false); source.addEventListener("eventName", function(e) { var data = JSON.parse(e.data); console.log("User login:" + data.username); }, false);
Пример:
serverEvents: (function () { "use strict"; var controls = {}, handlers = { open: function (event) { console.log(event); }, message: function (event) { console.log(event); }, error: function (event) { console.log(event); }, getId: function (event) { console.log("id"); } }; function getControls() { controls.eventSource = new EventSource("php/server-events.php"); } function addEvent() { var eventSource = controls.eventSource; eventSource.addEventListener("open", handlers.open, false); eventSource.addEventListener("message", handlers.message, false); eventSource.addEventListener("get_id", handlers.getId, false); eventSource.addEventListener("error", handlers.error, false); } function init() { getControls(); addEvent(); } return { init: function () { init(); } } }())
Data.php
header("Content-Type: text/event-stream"); header("Content-Encoding: none; "); header("Cache-Control: no-cache"); header("Access-Control-Allow-Origin: *"); $lastEventId = floatval(isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? $_SERVER["HTTP_LAST_EVENT_ID"] : 0); if ($lastEventId == 0) { $lastEventId = floatval(isset($_GET["lastEventId"]) ? $_GET["lastEventId"] : 0); } ob_start(); echo ":" . str_repeat(" ", 2048) . "\n"; // 2 kB padding for IE echo "retry: 2000\n"; // event-stream $i = $lastEventId; $c = $i + 100; while (++$i < $c) { echo "id: " . $i . PHP_EOL; echo "event: get_id" . PHP_EOL; echo "data: " . $i . ";" . PHP_EOL; echo PHP_EOL . str_repeat(" ", 1024 * 64); // Заполняется ответ пробелами, необходимо для корректного вывода буфера ob_flush(); flush(); sleep(1); }
Так как данная технология на текущий момент не поддерживается в IE, можно воспользоваться библиотекой https://github.com/Yaffle/EventSource/, которая эмулирует работу объекта EventSources. Для работы библиотеки, ее нужно просто подключить.
Пример в действии:
http://jsfiddle.net/valsie/MCLds/1/