Использование websocket в приложениях extjs

Предисловие

Чтобы обозначить контекст рассказа, стоит сказать пару слов о том, для чего нам понадобился такой сервер.

В Почте Mail.Ru есть множество систем, состояние которых меняется. Очевидно, что такой системой является и хранилище писем пользователей. Об изменении состояния — о событиях — можно узнавать несколькими способами. В основном это либо периодический опрос системы (polling), либо — в обратном направлении — уведомления со стороны системы об изменении ее состояния.

У обоих способов есть свои плюсы и минусы, однако если говорить о почте, то чем быстрее пользователь получит новое письмо — тем лучше. Polling в почте — это около 50 тысяч HTTP-запросов в секунду, 60% которых возвращают статус 304, что означает отсутствие изменений в ящике.

Поэтому, чтобы сократить нагрузку на серверы и ускорить доставку писем пользователям, было решено изобрести велосипед написать publisher-subscriber сервер (он же bus, message-broker или event-channel), который, с одной стороны, получает сообщения об изменении состояний, а с другой — подписки на такие сообщения.

Было:

Стало:

На первой схеме отображено то, как было раньше. Браузер периодически ходил в API и спрашивал об изменениях на Storage (хранилище писем).

На второй — новый вариант архитектуры. Браузер устанавливает WebSocket-соединение с API, по которому происходит уведомление о событиях Storage. API является клиентом к серверу Bus и отправляет ему данные своих подписчиков (об этом сервере речи сегодня идти не будет; возможно, расскажу о нем в следующих публикациях). В момент получения нового письма Storage посылает об этом уведомление в Bus (1), Bus — своим подписчикам (2). API определяет, какому соединению отправить полученное уведомление, и посылает его в браузер пользователю (3).

Как вы могли догадаться, речь сегодня пойдет об API, или WebSocket-сервере. Забегая вперед, скажу, что на сервере будет около 3 миллионов живых соединений. Эта цифра еще не раз всплывет в последующем рассказе об оптимизациях.

Общая информация о веб-сокетах

Веб-сокеты, это такая технология, которая позволяет браузеру и серверу создать одно постоянное соединение и через него обмениваться данными.
Преимущества такого подхода в том что для отслеживания изменения на сайте, браузеру теперь нет необходимости постоянно «сыпать» запросы на сервер.
При постоянном соединении сервер теперь может когда ему надо отправить сообщение браузеру, т.е. связь двунаправленная, от браузера к серверу и от сервера к браузеру.

Рассмотрим классическую схему уведомления о сообщениях на сайте.
Когда пользователь авторизуется на сайте, браузер каждый 30 секунд (может и чаще) шлёт ajax-запрос на сайт, по определённому урлу.
Запрос типа — «Пришли ли мне новые сообщения».
Сервер в большинстве случаев будет отвечать «Сообщений новых нет», и только изредка долгожданное «У вас 1 новое сообщение».
Когда пользователей не много такая схема устраивает, но когда их много сервер получает до 1000 и более безсмысленных запросов.
Такая схема использовалась, потому что http построен по принципу сделал запрос, получил ответ и «давай до свидание».
В http нет возможности отправить сообщение от сервера браузеру, если браузер не спросит.
При схеме с веб-сокетами браузеру достаточно создать соединение и ждать, сервер сам ответит браузеру, когда нужно.
Преимущество на лицо — значительно снижается трафик и нагрузка на сервер, и уведомление приходит моментально.
Широта использования веб-сокетов велика: чаты, уведомления, «доставучие» online-консультанты и прочее.

Пространства и «комнаты»¶

В протоколе WebSocket существуют такие понятия, как пространства и «комнаты». По умолчанию посылаемые данные отправляются всем сокетам, но принимают эти данные лишь некоторые из них. Получается, что в определенные моменты времени будет установлено избыточное количество соединений. Чтобы избежать этого, используйте пространства.

Пространства позволяют изолировать одни сокеты от других.

app.js

В приведенном примере с помощью метода на сервере определяются два пространства: и . На клиентской стороне подключение к тому или иному пространству происходит в зависимости от текущего маршрута. Таким образом, при отправке данных, например, из пространства , об этом будут оповещены только сокеты этого пространства. По умолчанию все сокеты находятся в пространстве .

Также и в пределах пространства можно распределять сокеты по так называемым «комнатам».

Чтобы отнести сокет к определенной «комнате» используется метод пространства , который принимает имя «комнаты» (задается пользователем модуля ). Для вынесения сокета из комнаты используйте метод .

Отправка данных в «комнату» осуществляется с помощью метода .

Обработка инициируемых в пределах «комнаты» событий осуществляется с использованием метода .

Специальные требования к серверу

В нашем случае лучше всего использовать сервер на основе цикла событий. Например, NodeJS, Kestrel или Twisted. Идея состоит в том, что при использовании потокового решения будет один поток на соединение. То есть, 1000 соединений = 1000 потоков. В решении на основе цикла событий у нас будет один поток для 1000 соединений.

  1. Вы можете принимать запросы EventSource только в том случае, если HTTP-запрос говорит, что он может принимать MIME-тип event-stream;
  2. Необходимо вести список всех подключенных пользователей, чтобы запускать новые события;
  3. Вы должны прослушивать сброшенные соединения и удалять их из списка подключенных пользователей;
  4. Вы должны поддерживать историю сообщений, чтобы при повторном подключении клиентов можно было отправить им пропущенные сообщения.

Мы получили все, чтобы приложение работало эффективно. Но столкнулись с некоторыми проблемами:

  • Устаревшие прокси-серверы в некоторых случаях удаляют HTTP-соединения после короткого таймаута. Чтобы защитить соединения, авторы могут включать строку комментариев (начинающуюся с символа «:») каждые 15 секунд или около того.
  • Авторы, желающие связать соединения источника событий друг с другом или с определенными ранее документами, могут обнаружить, что использование IP-адресов не работает. Отдельные клиенты могут иметь несколько IP-адресов (из-за наличия нескольких прокси-серверов) и отдельные IP-адреса могут иметь несколько клиентов (из-за совместного использования прокси-сервера). Лучше включать в документ уникальный идентификатор и передавать его как часть URL-адреса при установлении соединения.
  • Использование chunked transfer encoding может уменьшить надежность HTTP протокола, если блокирование выполняется другим слоем, не подозревающим о требованиях к синхронизации. Если эта проблема возникнет, блокирование может быть отключено для обслуживания потоков событий.
  • Клиенты, которые поддерживают ограничение на подключение к серверу через протокол HTTP, могут столкнуться с трудностями при открытии нескольких страниц сайта, если на каждой из этих страниц есть источник событий, расположенный в том же домене. Можно избежать этого, применяя механизм уникальных доменных имен для каждого соединения и разрешая пользователям включать функции EventSource для каждой страницы.
  • Поддержка браузера и полифиллы: Microsoft Edge не поддерживает эту реализацию. Но существует полифиллы, которые позволяют решить данную проблему. Тем не менее, самый важный сегмент для SSE — это мобильные устройства, где браузеры IE / Edge распространены незначительно.

Некоторые из доступных полифиллов:

· Yaffle.

· amvtek.

· remy.

Бесплатное подключение и другие функции

Пользовательские агенты, работающие в контролируемых средах, могут отключить управление соединением с прокси-сервером в сети. В такой ситуации считается, что пользовательский агент включает как программное обеспечение мобильного устройства, так и сетевой прокси-сервер.

Например, браузер на мобильном устройстве, установив соединение, может обнаружить, что он находится в поддерживаемой сети, и попросить прокси-сервер сети взять на себя управление созданным соединением. Последовательность действий в такой ситуации может быть следующей:

  1. Браузер подключается к удаленному HTTP-серверу и запрашивает ресурс, указанный автором в конструкторе EventSource.
  2. Сервер отправляет случайные сообщения.
  3. В промежутке между двумя сообщениями браузер обнаруживает, что он неактивен, за исключением активности сети, связанной с поддержанием TCP- соединения, и решает переключиться в спящий режим для экономии энергии.
  4. Браузер отключается от сервера.
  5. Браузер связывается с сервисом в сети и просит, чтобы служба «push proxy» поддерживала соединение.
  6. Служба «push proxy» связывается с удаленным HTTP-сервером и запрашивает ресурс, указанный в конструкторе EventSource (возможно, включая HTTP-заголовок последнего события и т. д.).
  7. Браузер позволяет мобильному устройству перейти в спящий режим.
  8. Сервер отправляет другое сообщение.
  9. Служба «push proxy» использует технологию OMA push для передачи события на мобильное устройство, которое выходит из спящего режима на время, достаточное для обработки события. Затем возвращается в спящий режим.

Подобный подход может снизить объем передаваемых данных и привести к значительной экономии энергии.

Помимо реализации существующего API и формата передаваемых данных ext/event-stream также могут поддерживаться форматы фреймворка событий, определенные другими спецификациями.

Установление WebSocket-соединения

Протокол работает над TCP.

Это означает, что при соединении браузер отправляет по HTTP специальные заголовки, спрашивая: «поддерживает ли сервер WebSocket?».

Если сервер в ответных заголовках отвечает «да, поддерживаю», то дальше HTTP прекращается и общение идёт на специальном протоколе WebSocket, который уже не имеет с HTTP ничего общего.

Пример запроса от браузера при создании нового объекта :

Описания заголовков:

GET, Host
Стандартные HTTP-заголовки из URL запроса
Upgrade, Connection
Указывают, что браузер хочет перейти на websocket.
Origin
Протокол, домен и порт, откуда отправлен запрос.
Sec-WebSocket-Key
Случайный ключ, который генерируется браузером: 16 байт в кодировке Base64.
Sec-WebSocket-Version
Версия протокола. Текущая версия: 13.

Все заголовки, кроме и , браузер генерирует сам, без возможности вмешательства JavaScript.

Такой XMLHttpRequest создать нельзя

Создать подобный XMLHttpRequest-запрос (подделать ) невозможно, по одной простой причине: указанные выше заголовки запрещены к установке методом .

Сервер может проанализировать эти заголовки и решить, разрешает ли он с данного домена .

Ответ сервера, если он понимает и разрешает -подключение:

Здесь строка представляет собой перекодированный по специальному алгоритму ключ . Браузер использует её для проверки, что ответ предназначается именно ему.

Затем данные передаются по специальному протоколу, структура которого («фреймы») изложена далее. И это уже совсем не HTTP.

Также возможны дополнительные заголовки и , описывающие расширения и подпротоколы (subprotocol), которые поддерживает данный клиент.

Посмотрим разницу между ними на двух примерах:

  • Заголовок означает, что браузер поддерживает модификацию протокола, обеспечивающую сжатие данных.

    Это говорит не о самих данных, а об улучшении способа их передачи. Браузер сам формирует этот заголовок.

  • Заголовок говорит о том, что по WebSocket браузер собирается передавать не просто какие-то данные, а данные в протоколах SOAP или WAMP («The WebSocket Application Messaging Protocol»). Стандартные подпротоколы регистрируются в специальном каталоге IANA.

    Этот заголовок браузер поставит, если указать второй необязательный параметр :

При наличии таких заголовков сервер может выбрать расширения и подпротоколы, которые он поддерживает, и ответить с ними.

Например, запрос:

Ответ:

В ответе выше сервер указывает, что поддерживает расширение , а из запрошенных подпротоколов – только SOAP.

Соединение можно открывать как или как . Протокол представляет собой WebSocket над HTTPS.

Кроме большей безопасности, у есть важное преимущество перед обычным – большая вероятность соединения. Дело в том, что HTTPS шифрует трафик от клиента к серверу, а HTTP – нет

Дело в том, что HTTPS шифрует трафик от клиента к серверу, а HTTP – нет.

Если между клиентом и сервером есть прокси, то в случае с HTTP все WebSocket-заголовки и данные передаются через него. Прокси имеет к ним доступ, ведь они никак не шифруются, и может расценить происходящее как нарушение протокола HTTP, обрезать заголовки или оборвать передачу.

А в случае с весь трафик сразу кодируется и через прокси проходит уже в закодированном виде. Поэтому заголовки гарантированно пройдут, и общая вероятность соединения через выше, чем через .

Реализация клиента на Javascript

Протокол веб-сокет создан уже давно (приобрёл статус RFC в 11.12.2011) и поддерживается большинством браузеров.
Чтобы узнать поддерживает ли ваш браузер веб-сокеты перейдите по .

Работа в браузерах с вебсокетам проходит в несколько этапов:

  • Установка соединения или рукопожатие (handshake).
  • Создание обработчиков событий: onopen (соединение создано), onclose(соединение закрыто), onmessage (пришло сообщение от сервера), onerror (ошибка при работе веб-сокетов).
  • Отправка сообщений (фреймов) на сервер.

Тестировать веб-сокеты мы будем на сервере websocket.org «ws://echo.websocket.org», который будет принимать от нас сообщения и отвечать на них повторением сообщением.
Этот сайт как раз существует, что лучше понять веб-сокеты, он понимает кросс-доменные запросы, поэтому страницу с JavaScript будем размещать у себя на локальном компьютере.

Этап. Рукопожатие

Чтобы создать соединение по веб-сокету достаточно создать объект WebSocket, в котором указывается урл для подключения.

Используйте протокол «ws://», если нужно не шифрованное соединение или протокол «wss://» для шифрованного соединения.

Этап. Создание обработчиков событий.

После того как мы создали объект WebSocket необходимо повесить функции-обработчики на события.

Если нужно повесить несколько функций на событие используем методы «addEventListener» и «removeEventListener». Пример:

Этап. Отправка сообщений на сервер

По веб-сокету сообщения отправляются в виде строки. Пример отправки простого текстового сообщения.

Обработка приходящих данных лежит уже на стороне сервера. Чаще для удобства работы по вебсокету отправляют JSON данные серилизованные в строку и обрабатывают приходящие данные как строка в JSON-e. Пример использования:

Удобный способ отправки сообщений по веб-сокету служит протокол «JSON-RPC» (ссылка). Это очень простой протокол, который облегчит взаимодействие браузера и сервера. Пример использования JSON RPC:

Параметры json-rpc объекта:

  • jsonrpc — версия протокола, может быть «2.0» или «1.0»
  • id — идентификатор запроса. Используется для идентификации ответа от сервера по своем запросу. Т.е. если отправить два запроса, то ответ от сервера по каждому запросу прийдёт в разное время, для этого и нужен id. На сервере необходимо учитывать этот параметр и в ответ прислать именно нужный id.
  • method — наименование метода, любая строка, к примеру «get», «hello», «set» и др.
  • params — параметры связанные с этим методом, тип переменной может быть любой, всё зависит от сервера.

Чтобы закрыть соединение используем метод close().

Бизнес-кейс

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

Виджеты никак не связаны друг с другом. В идеале они все должны быть подписаны на какую-либо конечную точку API и начинать получать данные от нее.

Но при этом число подключений будет увеличиваться с ростом количества виджетов. Поэтому необходимо установить ограничение для браузеров по количеству одновременно обрабатываемых HTTP-запросов.

Данные, которые получат виджеты, в основном состоят из чисел и обновлений для этих чисел: первоначальный ответ содержит 10 акций со значениями их котировок.

Также данные должны включать в себя возможность добавления / удаления торгуемых акций, а также обновление текущих котировок. Мы передаем небольшое количество JSON-строк для каждого обновления так быстро, как это возможно.

HTTP / 2 обеспечивает мультиплексирование запросов, поступающих от одного домена. То есть, мы можем получить одно соединение для нескольких ответов.

Начнем с изучения различных вариантов получения данных и посмотрим, что может дать каждый из них.

  • Мы собираемся использовать NGINX для балансировки нагрузки и реализации прокси, чтобы скрыть все конечные точки за одним и тем же доменом.
  • Мы хотим эффективно использовать сетевой трафик и потребление заряда батареи пользовательского устройства.

Компоненты распознавания и печати штрих-кодов Промо

Комплект программного обеспечения для реализации функций оптического распознавания штрих-кодов различных систем при помощи обычной web-камеры, а также их отображения в печатных формах. Программы могут работать в составе конфигураций, созданных на базе платформ «1С-Предприятие» версий 7.7, 8.2, 8.3. Компонент чтения кодов реализован в виде внешней компоненты 1С с COM-интерфейсом. Компонент отображения создан по стандартной технологии ActiveX для Windows, и может быть встроен в любое приложение, поддерживающее встраивание ActiveX элементов управления, например в документ Word или Excel, или форму VBA.

P.S. Добавлена новая версия программы распознавания. Новые функции: обработка видео в реальном режиме (а не по таймеру, как раньше), добавлена возможность распознавания штрих-кодов из графических файлов JPEG, PNG, GIF, BMP, а также передавать для распознавания картинки из 1С, теперь можно получить в 1С захваченное с камеры или файла изображение, как с выделением мест, содержащих коды, так и без, а также отдельные фрагменты изображений, содержащие код. Добавлены новые свойства и методы для программирования. Обновлена документация.

10 стартмани

SSE (Server Sent Events / EventSource)

SSE — events can be broadcast to multiple clients (Image from javaee.ch)

SSE connections can only push data to the browser. (communication is carried out from server to browser only, browsers can only subscribe to data updates originated by the server, but cannot send any data to the server)

00:00:00 CLIENT-> I need cakes 00:00:02 SERVER-> Have cake-1.00:00:04 SERVER-> Have cake-2.00:00:05 CLIENT-> Enough, I'm full.
  • Sample applications: Twitter updates, stock quotes, cricket scores, notifications to browser
  • Issue #1: .
  • Issue #2: Maximum number of open connections is limited to 6 or 8 over HTTP/1.1 (based on the browser version). If you use HTTP/2, there won’t be an issue because one single TCP connection is enough for all requests (thanks to multiplexed support in HTTP/2).

Простой клиент веб-сокетов

С точки зрения веб-страницы функциональность веб-сокетов легко понять и использовать. Первый шаг — это создать объект WebSocket и передать ему URL. Код для этого подобен следующему:

Строка URL начинается с текста ws://, который идентифицирует подключение типа веб-сокет. Этот URL указывает файл веб-приложения на сервере (в данном случае это сценарий socketServer.php).

Стандарт веб-сокетов также поддерживает URL, которые начинаются с текста wss://, что указывает на требование использовать безопасное, зашифрованное подключение (точно так же, как и при запросе веб-страницы указывается URL, начинающийся с https:// вместо http://).

Веб-сокеты могут подключаться не только к своему веб-серверу. Веб-страница может открыть подключение к серверу веб-сокетов, исполняющемуся на другом веб-сервере, не требуя для этого никаких дополнительных усилий.

Само обстоятельство создания объекта WebSocket понуждает страницу пытаться подключиться к серверу. Дальше надо использовать одно из четырех событий объекта WebSocket: onOpen (при установлении подключения), onError (когда возникает ошибка), onClose (при закрытии подключения) и onMessage (когда страница получает сообщение от сервера):

Например, в случае успешного подключения неплохо бы отправить соответствующее подтверждающее сообщение. Такое сообщение доставляется с помощью метода send() объекта WebSocket, которому в качестве параметра передается обычный текст. Далее приведена функция, которая обрабатывает событие onopen и отправляет сообщение:

Предположительно, веб-сервер получит это сообщение и даст на него ответ.

События onError и onClose можно использовать для отправки извещений посетителю веб-страницы. Но безоговорочно самым важным является событие onMessage, которое срабатывает при получении новых данных от сервера. Опять же, код JavaScript для обработки этого события не представляет никаких сложностей — мы просто извлекаем текст сообщения из свойства data:

Если веб-страница решит, что вся ее работа выполнена, она может закрыть подключение, используя метод disconnect():

Из этого обзора веб-сокетов можно видеть, что использование сервера веб-сокетов стороннего разработчика не представляет никаких трудностей — нам нужно лишь знать, какие сообщения отправлять, а какие — ожидать.

Чтобы заставить подключение веб-сокетов работать, выполняется большой объем работы за кулисами. Прежде всего, веб-страница устанавливает связь по обычному стандарту HTTP. Потом это подключение нужно повысить до подключения веб-сокетов, позволяющего свободную двустороннюю связь. На этом этапе возможны проблемы, если между компьютером клиента и веб-сервером находится прокси-сервер (как, например, в типичной корпоративной сети). Прокси-сервер может отказаться сотрудничать и разорвет подключение. Эту проблему можно решить, обнаруживая неудачное подключение (посредством события onError объекта WebSocket) и применяя один из заполнителей (polyfills) для сокетов, описанных на веб-сайте GitHub. Эти заполнители применяют метод опроса, чтобы эмулировать подключение веб-сокетов.

Post-connection features¶

You can see a summary of this library’s supported WebSocket features by either
running the autobahn test suite against this client, or by reviewing a recently
run autobahn report, available as a .html file in the /compliance directory.

Ping/Pong Usage

The WebSocket specification defines
and

message opcodes as part of the protocol. These can serve as a way to keep a
long-lived connection active even if data is not being transmitted. However, if
a blocking event is happening, there may be some issues with ping/pong. The
below examples demonstrate how ping and pong can be sent by this library. You
can get additional debugging information by using
Wireshark
to view the ping and pong messages being sent. In order for Wireshark to
identify the WebSocket protocol properly, it should observe the initial HTTP
handshake and the HTTP 101 response in cleartext (without encryption) —
otherwise the WebSocket messages may be categorized as TCP or TLS messages.
For debugging, remember that it is helpful to enable .

WebSocket ping/pong example

This example is best for a quick test where you want to check the effect of a
ping, or where situations where you want to customize when the ping is sent.
This type of connection does not automatically respond to a “ping” with a “pong”.

import websocket

websocket.enableTrace(True)
ws = websocket.WebSocket()
ws.connect("ws://echo.websocket.org")
ws.ping()
ws.pong()
ws.close()

WebSocketApp ping/pong example

This example, and in general, is better for long-lived connections.
If a server needs a regular ping to keep the connection alive, this is probably
the option you will want to use. The function will automatically
send a “pong” when it receives a “ping”, per the specification.

import websocket

def on_message(wsapp, message):
    print(message)

def on_ping(wsapp, message):
    print("Got a ping!")

def on_pong(wsapp, message):
    print("Got a pong! No need to respond")

wsapp = websocket.WebSocketApp("wss://stream.meetup.com/2/rsvps",
  on_message=on_message, on_ping=on_ping, on_pong=on_pong)
wsapp.run_forever(ping_interval=60, ping_timeout=10)

Функции и особенности WSS Consultant O365:

  • Создание прозрачного процесса управления отделами/сотрудниками.
  • Адаптация процессов совместной работы с документами в рамках обращения: создание документов из шаблонов, отслеживание этапов движения документов.
  • Настройка удалённой работы сотрудников в ресурсе: доступ к информации (документы, файлы, аудио), возможность аудио/видеоконференций.
  • Автоматизация системы аналитики, настройка конструктора отчётов.
  • Создание централизованного хранилища документов.
  • Эффективность и простота создания обращения.
  • Простой и понятный интерфейс с возможностью отслеживания состояния задач.
  • Организация эффективной коммуникации с исполнителями.
  • Адаптированный интерфейс для мобильных устройств.
  • Возможность организовать совместную работу с документами в рамках обращения.

Для всех пользователей, будет доступен корпоративный портал, в котором можно просмотреть актуальные корпоративные новости, просмотреть ближайшие мероприятия, поднять тему на форуме, а также получить доступ к базе всех внутренних документов. Кроме этого, все зарегистрированные сотрудники, получают доступ к встроенным веб-сервисам Office 365, для организации удаленной работы, таким как: Microsoft Teams, Planner, OneDrive.

Так как решение направлено на корпоративный сегмент рынка, и связано с работой с конфиденциальными и персонализированными данными, мы позаботились о безопасности. WSS Consultant O365 включает в себя двухфакторную аутентификацию, DLP систему(предупреждение потери данных), управление пользовательскими права доступа, шифрование данных в Office 365, расширенную защиту от угроз Exchange Online Protection, Active Directory Federation Service. Все персональные данные, хранящиеся в базе WSS Consultant O365 полностью соответствуют требования законодательства РФ. Кроме этого, безопасность в решении представлена Etoken, FIDO 2 и КриптоПро.

Гибкая стоимость решения, основанная на количестве сотрудников и сроках действия лицензии, позволяет выбрать нужные параметры и не переплачивать, сэкономив бюджет компании.

Запишитесь на бесплатный демо-тест уже сегодня, и оцените все преимущества WSS Consultant O365:

Реализуем часть протокола

Что бы в реализации сервера была какая то цель, нужно эту цель придумать. Целью кода данной статьи будет написание WebSocket сервера, который реализует часть протокола сокетов и позволяет переписываться нескольким клиентам из консоли браузера. Для начала нужно реализовать функционал опроса клиента с помощью управляющих фреймов Ping. Нам нужно знать, что клиент еще жив и готов принимать данные с сервера. Фрейм Ping, управляющий фрейм, но он так же может содержать данные. Когда клиент получит такое сообщение по сокету, он должен отправить на сервер фрейм Pong с теми данными, которые были во фрейме Ping. До реализации этого функционала, давайте пропишем в класс сервера необходимые константы

Далее реализуем наш метод по формированию фрейма Ping

По большому счету, в данном случае, это не требуется. Нам совершенно не обязательно пересылать какие-то данные клиенту вместе с управляющим фреймом Ping. Поэтому этот метод можно удалить, а вместо него в класс добавить еще одну константу. Также для того, чтобы реализовать функционал чата, нам потребуется хранить объекты подключений. Заведем под это отдельную коллекцию в классе сервера.

Модицифируем конструктор, добавим отправку фрейма Ping подключившимся клиентам с интервалом в 5 секунд, а также добавляем новых клиентов в коллекцию.

Теперь мы можем принимать соединения по сокетам и поддерживать его с помощью пингов. Осталось научить наш сервер маршрутизировать сообщения от клиентов. В спецификации к протоколу написано, что клиенты всегда должны отправлять сообщения на сервер в маскированном виде, а сообщения сервера всегда без маски. Из этого следует, что нам нужно раскодировать сообщение, а для этого нужно понять, что за сообщение пришло на сервер, получить маску, длину сообщения и сами данные. Напишем для этого метод

  1. В этой строке нам нужно получить длину данных внутри фрейма. Мы делаем это с помощью операции XOR и констранты, которая представляет число 128 в двоичном виде, которое выглядит как 10000000. В данном случае мы это делаем, исходя из того, что данные от клиента всегда приходят в маскированном виде, а значит первый бит этого байта всегда будет 1.
  2. Согласно спецификации для фреймов с длиной 126, длина сообщения передаётся в двух следующих байтах
  3. Согласно спецификации для фреймов с длиной 127, длина сообщения передаётся в восьми следующих байтах

С помощью этой функции мы можем получать всю необходимую информацию для обмена сообщениями между клиентами. Напишем метод, который будет демаскировать данные

Демаскирование происходит путем применения функции XOR к каждому байту данных и соответствующему ему байту маски. Длина маски указана в спецификации и составляет 4 байта. Теперь можно написать метод для отправки коротких сообщений по сокету клиенту.

Нам осталось финализировать конструктор класса. Добавим туда рассылку полученных сообщений от клиента всем активным клиентам, а также добавим отправку всем клиентам сообщения при подключении нового клиента.

Теперь можно запустить сервер. Для проверки работоспособности можно открыть две вкладки браузера и в консоли каждой вклдаки написать следующий код.

Затем отправить сообщение в одной из вкладок

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector