- Using server-sent events
- Receiving events from the server
- Sending events from the server
- Error handling
- Closing event streams
- Event stream format
- Fields
- Examples
- Data-only messages
- Named events
- Mixing and matching
- nurettin / gist:0421fc97c62480cdac998f0577fa0968
- Server-Sent Events: пример использования
- Подготовка
- Сервер
- Клиент
- Создание приложений реального времени с помощью Server-Sent Events
- Polling
- Long Polling
- WebSockets
- Протокол Server-Sent Events
- Multipart XMLHTTPRequest
- XMLHTTPRequest: Interactive
- Пример: Чат на Server-Sent Events
- Заключение
- Ссылки
Using server-sent events
Developing a web application that uses server-sent events is straightforward. You’ll need a bit of code on the server to stream events to the front-end, but the client side code works almost identically to websockets in part of handling incoming events. This is a one-way connection, so you can’t send events from a client to a server.
Receiving events from the server
The server-sent event API is contained in the EventSource interface; to open a connection to the server to begin receiving events from it, create a new EventSource object with the URL of a script that generates the events. For example:
If the event generator script is hosted on a different origin, a new EventSource object should be created with both the URL and an options dictionary. For example, assuming the client script is on example.com :
Once you’ve instantiated your event source, you can begin listening for messages from the server by attaching a handler for the message event:
This code listens for incoming messages (that is, notices from the server that do not have an event field on them) and appends the message text to a list in the document’s HTML.
You can also listen for events with addEventListener() :
This code is similar, except that it will be called automatically whenever the server sends a message with the event field set to «ping»; it then parses the JSON in the data field and outputs that information.
Warning: When not used over HTTP/2, SSE suffers from a limitation to the maximum number of open connections, which can be especially painful when opening multiple tabs, as the limit is per browser and is set to a very low number (6). The issue has been marked as «Won’t fix» in Chrome and Firefox. This limit is per browser + domain, which means that you can open 6 SSE connections across all of the tabs to www.example1.com and another 6 SSE connections to www.example2.com (per Stackoverflow). When using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100).
Sending events from the server
The server-side script that sends events needs to respond using the MIME type text/event-stream . Each notification is sent as a block of text terminated by a pair of newlines. For details on the format of the event stream, see Event stream format.
The PHP code for the example we’re using here follows:
The code above generates an event every second, with the event type «ping». Each event’s data is a JSON object containing the ISO 8601 timestamp corresponding to the time at which the event was generated. At random intervals, a simple message (with no event type) is sent. The loop will keep running independent of the connection status, so a check is included to break the loop if the connection has been closed (e.g. client closes the page).
Note: You can find a full example that uses the code shown in this article on GitHub — see Simple SSE demo using PHP.
Error handling
When problems occur (such as a network timeout or issues pertaining to access control), an error event is generated. You can take action on this programmatically by implementing the onerror callback on the EventSource object:
Closing event streams
By default, if the connection between the client and server closes, the connection is restarted. The connection is terminated with the .close() method.
Event stream format
The event stream is a simple stream of text data which must be encoded using UTF-8. Messages in the event stream are separated by a pair of newline characters. A colon as the first character of a line is in essence a comment, and is ignored.
Note: The comment line can be used to prevent connections from timing out; a server can send a comment periodically to keep the connection alive.
Each message consists of one or more lines of text listing the fields for that message. Each field is represented by the field name, followed by a colon, followed by the text data for that field’s value.
Fields
Each message received has some combination of the following fields, one per line:
A string identifying the type of event described. If this is specified, an event will be dispatched on the browser to the listener for the specified event name; the website source code should use addEventListener() to listen for named events. The onmessage handler is called if no event name is specified for a message.
The data field for the message. When the EventSource receives multiple consecutive lines that begin with data: , it concatenates them, inserting a newline character between each one. Trailing newlines are removed.
The event ID to set the EventSource object’s last event ID value.
The reconnection time. If the connection to the server is lost, the browser will wait for the specified time before attempting to reconnect. This must be an integer, specifying the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.
All other field names are ignored.
Note: If a line doesn’t contain a colon, the entire line is treated as the field name with an empty value string.
Examples
Data-only messages
In the following example, there are three messages sent. The first is just a comment, since it starts with a colon character. As mentioned previously, this can be useful as a keep-alive mechanism if messages might not be sent regularly.
The second message contains a data field with the value «some text». The third message contains a data field with the value «another message\nwith two lines». Note the newline special character in the value.
Named events
This example sends named events. Each has an event name specified by the event field, and a data field whose value is an appropriate JSON string with the data needed for the client to act on the event. The data field could, of course, have any string data; it doesn’t have to be JSON.
Mixing and matching
You don’t have to use just unnamed messages or typed events; you can mix them together in a single event stream.
Источник
nurettin / gist:0421fc97c62480cdac998f0577fa0968
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
buildscript < |
.. . |
> |
.. . |
android < |
.. . |
> |
dependencies < |
.. . |
compile ‘ org.kaazing:gateway.client.java:5.1.0.3 ‘ |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
try < |
final SseEventSourceFactory sseEventSourceFactory = SseEventSourceFactory . createEventSourceFactory(); |
final SseEventSource sseEventSource = sseEventSourceFactory . createEventSource( new URI ( » http://my/event/url » )); |
sseEventSource . connect(); |
final SseEventReader sseEventReader = sseEventSource . getEventReader(); |
SseEventType type = sseEventReader . next(); |
while (type != SseEventType . EOS ) < |
log( » new event » ); |
if (type != null && type . equals( SseEventType . DATA )) < |
CharSequence data = sseEventReader . getData(); |
log(data . toString()); |
> else < |
log( » type null or not data: » + type); |
> |
type = sseEventReader . next(); |
> |
> catch ( URISyntaxException e) < |
e . printStackTrace(); |
> catch ( IOException e) < |
e . printStackTrace(); |
> |
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Источник
Server-Sent Events: пример использования
Доброго времени суток, друзья!
В этом туториале мы рассмотрим Server Sent Events: встроенный класс EventSource, который позволяет поддерживать соединение с сервером и получать от него события.
О том, что такое SSE и для чего он используется можно почитать здесь.
Что конкретно мы будем делать?
Мы напишем простой сервер, который будет по запросу клиента отправлять ему данные 10 случайных пользователей, а клиент с помощью этих данных будет формировать карточки пользователей.
Сервер будет реализован на Node.js, клиент — на JavaScript. Для стилизации будет использоваться Bootstrap, в качестве API — Random User Generator.
Код проекта находится здесь.
Если вам это интересно, прошу следовать за мной.
Подготовка
Создаем директорию sse-tut :
Заходим в нее и инициализируем проект:
axios будет использоваться для получения данных пользователей.
Сервер
Приступаем к реализации сервера.
Подключаем http и axios, определяем порт:
Создаем функцию получения данных пользователя:
Создаем счетчик количества отправленных пользователей:
Пишем функцию отправки данных клиенту:
Создаем и запускаем сервер:
Выполняем команду yarn start или npm start . В терминале появляется сообщение «Server ready.». Открываем http://localhost:3000 :
С сервером закончили, переходим к клиентской части приложения.
Клиент
Открываем файл client.js .
Создаем функцию генерации шаблона пользовательской карточки:
Шаблон генерируется с использованием следующих данных: идентификатор пользователя (если имеется), имя, логин, адрес электронной почты и возраст пользователя.
Начинаем реализовывать основной функционал:
Сначала реализуем закрытие соединения:
Переходим к открытию потока событий:
Обрабатываем кастомное событие «randomUser»:
Не забываем реализовать обработку ошибок:
Наконец, инициализируем приложение:
На всякий случай перезапускаем сервер. Открываем http://localhost:3000 . Нажимаем на кнопку «Start»:
Начинаем получать данные от сервера и рендерить карточки пользователей.
Если нажать на кнопку «Stop», отправка данных будет приостановлена:
Снова нажимаем «Start», отправка данных продолжается.
При достижении лимита (10 пользователей) сервер отправляет идентификатор со значением -1 и закрывает соединение. Клиент, в свою очередь, также закрывает поток событий:
Как видите, SSE очень похож на веб-сокеты. Недостатком является однонаправленность сообщений: сообщения могут отправляться только сервером. Преимущество состоит в автоматическом переподключении и простоте реализации.
Поддержка данной технологии на сегодняшний день составляет 95%:
Надеюсь, статья вам понравилась. Благодарю за внимание.
Источник
Создание приложений реального времени с помощью Server-Sent Events
Буквально недавно стало известно, что Firefox 6 получит SSE (уже есть в Opera 10.6+, Chrome, WebKit 5+, iOS Safari 4+, Opera Mobile 10+) так, что поддержка более половины всех браузеров (охват аудитории пользователей) уже не за горами. Настало время присмотреться к этой технологии. SSE предложил Ian Hickson более 7 лет назад, но только год назад она стала появляться в браузерах. У нас же есть WebSockets зачем нам ещё один какой-то протокол?! Но во всем есть свои плюсы и минусы, давайте посмотрим чем же SSE может быть полезен.
Идея SSE проста — клиент подписывается на события сервера и как только происходит событие — клиент сразу же получает уведомление и некоторые данные, связанные с этим событием. Чтобы понять полезность протокола SSE необходимо сравнить его с привычными методами получения событий, вкратце объясню их суть:
Polling
Самый простой, но самый не эффективный, метод: клиент раз в несколько секунд опрашивает сервер на наличие событий. Даже если ничего нет, то клиент всеравно делает запрос — а мало ли что придет.
Плюсы:
— Просто
— Данные могут быть пожаты
Минусы:
— Очень много лишних запросов
— События всегда приходят с опозданием
— Серверу приходится хранить события пока клиент не заберет их или пока они не устареют
Long Polling
Улучшенный вариант предыдущего метода. Клиент отправляет запрос на сервер, сервер держит открытым соединение пока не придут какие-нибудь данные или клиент не отключится самостоятельно. Как только данные пришли — отправляется ответ и соединение закрывается и открывается следующее и так далее.
Плюсы по сравнению с Polling:
— Минимальное количество запросов
— Высокая временная точность событий
— Сервер хранит события только на время реконнекта
Минусы по сравнению с Polling:
— Более сложная схема
WebSockets
Это бинарный дуплексный протокол, позволяющий клиенту и серверу общаться на равных. Этот протокол можно применять для игр, чатов и всех тех приложений где вам нужны предельно точные события близкие к реальному времени.
Плюсы по сравнению с Long Polling:
— Поднимается одно соединение
— Предельно высокая временная точность событий
— Управление сетевыми сбоями контролирует браузер
Минусы по сравнению с Long Polling:
— HTTP не совместимый протокол, нужен свой сервер, усложняется отладка
Так почему же стоит применять SSE, раз у нас есть такой прекрасный протокол WebSockets?! Во-первых не каждому веб-приложению необходима двусторонняя связь — подойдет и SSE. Во-вторых SSE — HTTP совместимый протокол и вы можете реализовать рассылку событий на любом веб-сервере.
Протокол Server-Sent Events
Клиент отправляет запрос на сервер, сервер в ответ отправляет следующий заголовок:
И не закрывает соединение (на php можно создать бесконечный цикл, как сделать на node.js будет объеснено в примере статьи). Вот и все — SSE работает! Чтобы отправить клиенту какие-то данные сервер просто пишет в сокет строку следующего формата:
Если необходимо отправить несколько строк данных, то формат будет следующим:
Вот, впринципе, и вся база протокола. Кроме этого сервер может отправлять id сообщения это нужно на случай если соединение было разорвано. Если соединение было сброшено, то клиент при попытке подключения отправит специальный заголовок (Last-Event-ID), чтобы восстановить утраченные события:
Время переподключения (retry) в случае ошибок:
Поле id и retry не обязательны.
На клиенте все будет выглядеть следующим образом:
Все предельно просто. Давайте построим приложение на основе протокола SSE. Как водится, это будет чат.
Multipart XMLHTTPRequest
Ещё называют multipart streaming (Поддерживает только Firefox). Очень похожий на SSE протокол.
Его заголовок имеет фомат:
А части отсылаются в таком формате:
В клиенте создается обычный XHR, но перед отправкой запроса необходимо поставить флаг req.multipart = true;
Правда похоже на SSE? Подробнее
Есть ещё один протокол, который можно привести к SSE:
XMLHTTPRequest: Interactive
Для использования его необходима поддержка браузером специального readyState с кодом 3 (interactive) — этот статус сообщает о том, что часть данных пришла, но соединение ещё не закрыто. Для jQuery есть одноименный плагин, использующий readyState с кодом 3. И как всегда не все браузеры поддерживают readyState с кодом 3.
Пример: Чат на Server-Sent Events
Мы будем принимать поток событий по SSE: уход в оффлайн, приход в онлайн, сообщение. Т.к. по SSE нельзя отправлять сообщение, то мы будем отправлять их по HTTP.
Схема работы такая:
— При входе в чат запрашивается имя
— Клиент подключается к серверу чата. Создается поток событий.
— При подключении клиента чат рассылает всем событие: %username% online
— При отключении клиента чат рассылает всем событие: %username% offline
— Клиент может отправлять сообщение по HTTP «POST /message» Cервер принимает это сообщение и рассылает всем клиентам принятое сообщение по SSE
Разберем код клиента. Для того, чтобы у некоторых браузеров не было бесконечной загрузки мы вместо $.ready выполняем setTimeout:
Запрашиваем имя пользователя:
Создаем EventSource и передаем ему имя пользователя (теперь пользователь онлайн) и слушаем необходимые события:
Не буду рассматривать метод renderMessage и разметку страницы. Весь код клиента можно посмотреть тут: index.html
На стороне сервера у нас будет Node.js. Тут все сложнее, но основная сложность в мультикасте сообщений от одного пользователя ко всем, а не в построении коммуникации по SSE.
Подключаем необходимые модули
Роуты
Создаем список роутов Routes, включающий в себя следующие объекты:
1. Статика. Индексная страница, мы просто шлем статику:
2. Поднятие SSE соедиения:
3. Сообщение от клиента:
4. Роут по умолчанию — Страница 404:
Менеджер клиентов — Clients
При добавлении нового клиента (add) менеджер рассылает все сообщение о том, что клиент пришел:
При удалении закрывает соединение и рассылает всем, что клиент оффлайн:
Для отправки сообщений клиентам используется private метод _send:
Метод _send используют public методы broadcast и unicast для рассылки сообщения всем и одному клиенту соответственно.
Создаем и включаем сервер
Наш чат на SSE готов. Запускаем сервер:
Открываем один из браузеров: Firefox 6, Opera 10.6+, Chrome, WebKit 5+, iOS Safari 4+, Opera Mobile 10+. Переходим на http:// localhost/ и чатим!
Заключение
SSE — хорошая технология, которая должна вытеснить Long Poling она проста и не менее эффективна, чем WebSockets. Сейчас SSE поддерживают Opera 10.6+ (Opera 9 поддерживает старый стандарт SSE), Chrome, Safari 5+. Firefox поддерживает Multipart XMLHTTPRequest, для которого можно написать обертку и использовать как интерфейс SSE.
Ссылки
1. Онлайн пример SSE чата можно посмотреть вот тут: sse-chat.nodester.com
Это несколько урезанная версия чата из-за особенностей проксирования Nodester (нет сообщения о количестве пользователей онлайн и нет сообщений о выходе из чата, может быть частый реконнект)
2. Исходник примера: github.com/azproduction/event-source-chat
3. Ещё один туториал по SSE
4. Спецификация
PS Похоже, что чат накрыл хабраэффект, но возможно что-то с nodester(у него часто такое бывает). Если вам интересен результат, то скачайте исходник с GitHub.
UPD Добавил Multipart XMLHTTPRequest, XMLHTTPRequest: Interactive спасибо за дополнение yui_room9
Источник