Веб-сокеты. Асинхронный веб, или что такое веб-сокеты

Your browser may not support the functionality in this article.

Проблема: время реакции при обмене данными между сервером и клиентом

Интернет создавался главным образом на основе так называемой парадигмы запросов и ответов, реализованной с помощью протокола HTTP. Она заключается в том, что пока клиент не отправит запрос на открытие следующей страницы, она не загрузится. Примерно с 2005 г. с появлением технологии AJAX работа в Интернете стала более динамичной. При этом обмен данными по протоколу HTTP все равно инициировался клиентом, и это требовало от пользователя выполнения определенных действий или периодического опроса сервера для загрузки обновленной информации.

Технологии, позволяющие серверу отправлять клиенту новые данные в момент их появления, существуют довольно давно. Они известны как Push или Comet . Один из распространенных способов создания иллюзии соединения, инициируемого сервером, – это так называемый метод длинного опроса. Его суть в том, что клиент создает подключение к HTTP-серверу и оно не закрывается до отправки ответа. Если на сервере действительно есть обновленные данные, он отправляет ответ (в других технологиях для этого используются Flash , multipart-запросы XHR и особые HTML-файлы). Технология длинного опроса, как и другие подобные приемы, прекрасно работает. Вы пользуетесь ими каждый день, например, в чате Gmail.

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

Знакомство с протоколом WebSocket: использование веб-сокетов в Интернете

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

Начало работы

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

Var connection = new WebSocket("ws://html5rocks.websocket.org/echo", ["soap", "xmpp"]);

Обратите внимание на ws: : это новая схема URL для веб-сокетов. Для создания безопасных веб-сокетов также используется схема wss: , аналогично протоколу https: для безопасных HTTP-соединений.

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

Второй аргумент позволяет использовать дополнительные субпротоколы. Это может быть строка или массив строк. Каждая из них должна соответствовать названию субпротокола, а сервер принимает только один из переданных с помощью массива параметров. Список поддерживаемых субпротоколов приведен в свойстве protocol объекта WebSocket.

Названия субпротоколов должны соответствовать реестру IANA . По состоянию на февраль 2012 г. зарегистрировано только одно такое название (soap).

// When the connection is open, send some data to the server connection.onopen = function () { connection.send("Ping"); // Send the message "Ping" to the server }; // Log errors connection.onerror = function (error) { console.log("WebSocket Error " + error); }; // Log messages from the server connection.onmessage = function (e) { console.log("Server: " + e.data); };

Обмен данными с сервером

После установки соединения с сервером (срабатывания события open) ему можно отправлять данные с помощью метода send("сообщение") объекта подключения. Ранее она принимала только строки, однако в последней версии спецификации поддерживаются и двоичные сообщения. Для отправки таких данных используются объекты Blob и ArrayBuffer .

// Sending String connection.send("your message"); // Sending canvas ImageData as ArrayBuffer var img = canvas_context.getImageData(0, 0, 400, 320); var binary = new Uint8Array(img.data.length); for (var i = 0; i

Сервер также может в любой момент отправить нам сообщение. Когда это происходит, срабатывает обратный вызов onmessage . Этот вызов получает объект события, а поступившее сообщение можно увидеть в свойстве data .

Согласно последней версии спецификации, объект WebSocket также способен принимать двоичные сообщения. Двоичные фреймы можно получать в форматах Blob или ArrayBuffer . Чтобы указать формат полученного двоичного фрейма, необходимо задать для свойства binaryType объекта WebSocket значение blob или arraybuffer. Форматом по умолчанию является blob. (Проверять параметр binaryType при отправке не требуется.)

// Setting binaryType to accept received binary as either "blob" or "arraybuffer" connection.binaryType = "arraybuffer"; connection.onmessage = function(e) { console.log(e.data.byteLength); // ArrayBuffer object if binary };

Еще одной новой функцией WebSocket являются расширения. С их помощью можно отправлять сжатые , мультиплексированные фреймы и т. п. Список поддерживаемых сервером расширений приведен в свойстве extensions объекта WebSocket после срабатывания события open. По состоянию на февраль 2012 г. официальных спецификаций для расширений нет.

// Determining accepted extensions console.log(connection.extensions);

Обмен данными между разными доменами

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

Прокси-серверы

В любой новой технологии есть свои подводные камни. Для протокола WebSocket проблемой является совместимость с прокси-серверами – посредниками в HTTP-соединениях в большинстве корпоративных сетей. Протокол WebSocket использует систему модернизации HTTP (она обычно применяется для HTTP/SSL) для "модернизации" HTTP-соединения по данному протоколу. Некоторые прокси-серверы не поддерживают ее, и происходит сбой подключения. Таким образом, даже если клиент использует протокол WebSocket, установить соединение будет невозможно. Поэтому следующий раздел крайне важен.

Преимущества веб-сокетов уже сегодня

Технология WebSocket появилась совсем недавно и не полностью реализована во всех браузерах. Несмотря на это, можно воспользоваться упомянутыми выше библиотеками для реализации в случаях, когда протокол WebSocket не поддерживается. Одной из самых популярных библиотек является socket.io , в которой возможности протокола реализованы как на стороне сервера, так и на стороне клиента, а также обеспечена обратная совместимость (по состоянию на февраль 2012 г. socket.io не поддерживает обмен двоичными сообщениями). Существуют и коммерческие решения, например PusherApp , который легко интегрируется в любые веб-приложения и реализуют отправку сообщений WebSocket клиентам с помощью API на базе HTTP. Использование HTTP-запросов неизменно повышает потребление ресурсов в сравнении со стандартной технологией WebSocket.

На стороне сервера

Использование протокола WebSocket меняет подход к применению серверных приложений. Хотя традиционные наборы серверного программного обеспечения, например LAMP, разработаны для обычного цикла запросов и ответов на базе HTTP, они часто плохо работают в условиях большого числа WebSocket-подключений. Большое количество одновременных соединений требует принципиально новой архитектуры, которая сочетает низкую ресурсоемкость с возможностями параллельной работы. Такие архитектуры обычно создаются на основе потоковой или так называемой неблокирующей системы обмена данными.

(Web Sockets) - это передовая технология, которая позволяет создавать интерактивное соединение между клиентом (браузером) и сервером для обмена сообщениями в режиме реального времени. Веб-сокеты, в отличие от HTTP, позволяют работать с двунаправленным потоком данных, что делает эту технологию совершенно уникальной. Давайте разберемся, как работает эта технология и чем она отличается от HTTP.

Как работает HTTP?

Схема обмена сообщениями по HTTP

Вы наверняка знаете, что такое HTTP (или HTTPS), поскольку встречаетесь с этим протоколом каждый день в своём браузере. Браузер постоянно спрашивает у сервера, есть ли для него новые сообщения, и получает их.

Вы также можете знать, что HTTP позволяет использовать разные типы запросов, такие как POST, GET или PUT, каждый из которых имеет своё назначение.

Как работают веб-сокеты?

Схема обмена сообщениями при использовании веб-сокетов

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

Веб-сокеты можно использовать, если вы разрабатываете:

  • приложения реального времени;
  • чат-приложения;
  • IoT-приложения;
  • многопользовательские игры.
Когда следует избегать использования веб-сокетов?

Практически никогда. Единственный минус - это несовместимость с некоторыми браузерами, но уже 95 % браузеров поддерживают веб-сокеты.

В некоторых случаях веб-сокеты вам всё же не понадобятся. Если вы создаёте простую CMS, вам вряд ли пригодится функциональность в режиме реального времени. Также не стоит использовать веб-сокеты в REST API, поскольку вам хватит таких HTTP-запросов, как GET, POST, DELETE и PUT.

Практические примеры

В примерах ниже для клиента используется JavaScript, а для сервера - Node.js. Примеры очень просты и вряд ли пригодятся на практике, но зато позволят разобраться в сути.

Веб-сокеты

Пример чата с веб-сокетом let ws = new WebSocket("ws://localhost:8080"); // выводим новые сообщения в консоль ws.onmessage = ({data}) => { console.log(data); } // отправляем сообщение ws.onopen = () => ws.send("Text");

Const WebSocket = require("ws"); // создаём новый websocket-сервер const wss = new WebSocket.Server({ port: 8080 }); // отправляем клиентам, когда функция clientValidator возвращает true. this - это wss. wss.broadcast = function(data, clientValidator = () => true) { this.clients.forEach(client => { if (clientValidator(client)) { client.send(data); } }); } wss.on("connection", ws => { // событие будет вызвано, когда клиент отправит сообщение ws.on("message", message => { // отправляем сообщение всем, кроме автора wss.broadcast(message, client => client !== ws); }); });

Вот иллюстрация работы веб-сокетов:

Демонстрация работы веб-сокетов

Эквивалент в HTTP

Так как HTTP должен постоянно проверять канал на наличие новых сообщений, можно использовать «грязную» проверку (dirty check) - подход, при котором клиент с заданной периодичностью (допустим, каждые 200 мс) проверяет наличие новых сообщений на сервере.

Или getting started with WebSocket PHP без phpDaemon

Здравствуйте! Простите за столь длинный заголовок, но, надеюсь, что новичкам вроде меня будет легче найти эту статью, ведь мне ничего подобного найти не удалось. Несколько недель назад я принял решение переработать игровой клиент и сервер своей игры Growing Crystals с AJAX, на WebSocket, но всё оказалось не просто непросто, а очень сложно. Поэтому я и решил написать статью, которая бы помогла самым что ни на есть начинающим разработчикам на WebSocket + PHP сэкономить несколько дней времени, максимально подробно объясняя каждый свой шаг по настройке и запуску первого WebSocket скрипта на PHP.

Код сокет-сервера на PHP я приводить не буду, т.к. он есть в архиве и снабжен комментариями. Я искусственно ограничил время работы приёмки соединений 100 секундами, чтобы скрипт он не оставался в памяти, закрывал все соединения и не приходилось при внесении в него изменений постоянно перезагружать Денвер. При чем, по прошествии 100 секунд, скрипт не прекратит свою работу, а будет ждать подключения, после которого его работа будет завершена, а все сокеты благополучно закрыты. Также, для тестирования, я использую localhost и порт 889.

Socket_bind($socket, "127.0.0.1", 889);//привязываем его к указанным ip и порту

Файлы из архива можно поместить в корневую папку localhost Денвера. Алгоритм тестирования:

  • Запускаем http://localhost/socket.html , он автоматически подключится к ws echo серверу ws://echo.websocket.org, можно отправить несколько сообщений, которые сервер должен отправить обратно, ведь это же эхо-сервер. Если это работает - отлично, с клиентом всё в порядке, если нет, проверьте все ли вы файлы разместили в одном каталоге, запущен ли у вас Денвер, и есть ли соединение с сетью Интернет.
  • Откройте в этой же вкладке где у вас открыт ws-клиент JavaScript консоль (В GoogleChrome 34.8.1847.137 m и в FireFox это делается почти одинаково меню->инструменты->консоль Java Script или Ctrl+Shift+J, но лично я использую для этого консоль FireBug). Отправьте еще несколько сообщений. Вы увидите какие сообщения приходят от сервера. Вы можете нажать disconnect и после этого отправить несколько сообщений, вы убедитесь что сообщения не уходят, т.к. связь с сервером ws://echo.websocket.org разорвана.
  • Запускаем в новой вкладке браузера наш сокет-сервер http://localhost/simpleworking.php . Желательно чтобы вы сразу могли видеть и вкладку клиента с консолью и вкладку сервера. Должно появиться GO() ... socket_create ...OK socket_bind ...OK Listening socket... OK

    Что означает, что сервер готов к входящим соединениям.

  • Во вкладке клиента в поле Server address вводим ws://127.0.0.1:889 и нажимаем reconnect, мы видим что на клиенте ничего не происходит, а на сервере появляются сообщения вида Client "Resource id #3" has connected Send to client "Hello, Client!"... OK Waiting... OK

    Что говорит нам о том, что технически соединение с сокетом на сервере установлено, а на клиенте ожидается соединение по протоколу веб-сокет, и оно не установлено, т.к. браузером не получены соответствующие заголовки протокола ws. При попытке отправить сообщение с клиента, в консоли должна появиться ошибка о том, что соедние с ws не установлено. К сожалению, скрипт в GoolgeChrome остановится и для новых попыток подключения придётся перезагружать страницу с веб-клиентом. В FireFox скрипт продолжает выполняться. Обязательно сделайте reconnect спустя 100 секунд, после запуска скрипта сервера, чтобы дать ему благополучно завершиться.

    Client "Resource id #4" has connected Send to client "Hello, Client!"... OK time = 309.8900001049return go() ended Closing connection... OK

  • Чтобы окончательно убедиться в том, что сервер отвечает, что его сообщения не блокируются файрволом, запустите скрипт сервера http://localhost/simpleworking.php запустите telnet и попытайтесь подключиться к 127.0.0.1:889, только это нужно сделать не позднее 100 секунд, с момента запуска сервера, пока он не закрыл соединения и не завершил скрипт.
  • По telnet должен придти ответ «Hello, Client!», что свидетельствует о том, что всё работает в штатном режиме и связь с сервером двухсторонняя.

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

    Протокол веб-сокет

    Теперь остаётся научить наш сокет-сервер общаться как веб-сокет сервер, корректно работать в фоне (демон), и, самое главное, на дешевом хостинге. Немного теории: разрешив серверу выводить на экран сообщение, которое получаем от клиента при попытке подключения, добавив перед строкой $msg = «Hello, Client!»; код

    Echo "Client \"".$accept."\" says:
    ";
    echo socket_read($accept,512);
    echo "";

    можно увидеть, что попытка установить соединение по протоколу веб-сокет всегда сопровождается отправлением клиентом заголовка с обязательным соблюдением формата протокола WebScoket. Тег pre для вывода заголовков выбран не случайно, т.к. в заголовках большое значение играют переносы строк. Такие заголовки я получаю от браузеров, когда пытаюсь подключиться к нашему ws://127.0.0.1:889.

    GET / HTTP/1.1 Host: localhost:889 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Sec-WebSocket-Version: 13 Origin: http://localhost Sec-WebSocket-Key: ndHtpnSPk2H0qOeP6ry46A== Cookie: vc=3; __gads=ID=9eabc58c385071c7:T=1400699204:S=ALNI_Ma_g9PZBXpi_MLKDBsao3LQiGx-EA Connection: keep-alive, Upgrade Pragma:

    GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: localhost:889 Origin: http://localhost Pragma: no-cache Cache-Control: no-cache Sec-WebSocket-Key: zNMTfmc+C9UK6Ztmv4cE5g== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36

    Еще до того, как я начал изучать веб-сокеты, мне казалось, что работа с веб-сокетами будет напоминать работу с AJAX, когда мы отправляли запросы серверу используя JavaScript XMLHttpRequest(), но в реальности задача оказалась намного сложнее, и в первую очередь потому, что WebSocket это протокол находящийся на одном уровне с протоколом HTTP (но с некоторыми нюансами) в сетевой модели OSI , что означает, нам на стороне сервера, нужно реализовывать функционал похожий на обработку HTTP (который всегда выполнял Apache). Продолжая сравнения с AJAX, в AJAX нам не нужно обеспечивать работу протокола HTTP, т.к. это всё делает браузер и веб-сервер Apache. Мы просто отправляем и получаем сообщения. Но, использование ws хоть и накладывает на нас достаточно большой объём работы связанный с реализацией сервера на PHP, оно также даёт и преимущества: сокращение объёма передаваемых данных и рост производительности серверного ПО. Для успешного общения с клиентами по протоколу WebSocket сервер должен строго выполнять требования стандарта RFC6455 , подробно описывающего форматы заголовков ответов. Все сообщения отправляемые по протоколу WebSocket можно разделить на несколько видов: handsnake (рукопожатие при установлении связи), ping-pong(проверка связи) и data-transfer(передача данных). Также есть более краткое описание протокола в общих чертах на русском . Для того, чтобы обеспечить полноценное корректное общение сервера и клиента по протоколу веб-сокет, необходима реализация функций на PHP получения и разбора заголовков от клиента, составление заголовков сервером, составление ключа и ряд других. Изучив материалы представленные на хабре и других ресурсах, удалось найти реализованные годные функции , единственным смущающим различием явлилось то, что большинство авторов используют потоковые функции работы с сокетами , они считаются более компактными, но, при этом, более ресурсоёмкими в связи с использованием буфера. Для перехода на потоковые функции работы с сокетами, не требуется установка никаких дополнительных модулей кроме уже установленных, т.к. потоки встроены в PHP 5 по умолчанию. Потоковые функции работы с сокетами всегда начинаются с stream_socket_*. При использовании потоковых функций, желательно убедиться в phpinfo() в поддержке необходимого транспорта для потоков.

    Registered Stream Socket Transports: tcp, udp.

    В случае если такой информации в phpinfo() нет или в случае возникновения других проблем, обратитесь к документации , но проблема решается банальным обновлением Денвера на актуальную версию.
    Еще важным является тот факт, что большинство систем ограничивает возможность создания сокета на порту ниже чем 1024, поэтому во всех дальнейших программах для сокета будет использоваться порт 8889.
    Взяв за основу исходные коды функций кодирования и декодирования заголовков протокола WebSocket, удалось реализовать полноценный ws echo сервер. Алгоритм его работы также прост как и в предыдущем случае: создаём ws сервер используя функцию stream_socket_server, запускаем бесконечный цикл в котором проверяем наличие новых соединений и при получении нового соединения размещаем его в массиве $connects, также запускаем второй цикл, который пробегает по всем соединениям и закрывает отвалившиеся и получает сообщения от открытых соединений. Также в скрипт PHP мною добавлено три функции, которые я оформил как обработчиков событий.

    Function onOpen($connect, $info) {
    echo "open OK
    \n";
    }

    function onClose($connect) {
    echo "close OK
    \n";
    }

    function onMessage($connect, $data) {
    $f = decode($data);
    echo "Message:";
    echo $f["payload"] . "
    \n";
    fwrite($connect, encode($f["payload"]));
    }

    Которые ничего не делают, за исключением вывода сообщений о событиях на экран. И при получении сообщения от клиента, раскодируем его и отправляем его текст, предварительно закодировав, обратно. , ws клиент не изменился. Можно протестировать ws echo server скачав архив, и разместив его в корневой папке localhost-а Денвера. Подключиться клиентом к адресу ws://127.0.0.1:8889.
    Тонкости связанные с запуском ws сервера в фоне (демон), и запуск на хостинге мы разберем в . Буду рад комментариям и отзывам.

    Специально для тех, кто ищет хостинг для запуска своего проекта на веб-сокетах обсуждение .

    Мои статьи про PHP демонов и веб-сокеты
    • Простой веб-сокет на PHP или веб сокеты с абсолютного 0

    Веб-сокеты (WebSockets), возможно, самое интересное нововведение в веб-техноло­гиях со времен появления "Aсинxpoннoгo JavaScript и XML"(AJAX). Они стали популярными с выходом HTML5 и поддерживаются множеством веб-фреймворков.

    Однако потребовалось немало времени для создания стабильной и совместимой спе­цификации веб-сокетов.

    Модель протокола передачи гипертекста (НТТР) была спроектирована задол­го до того, как стал популярен Интернет, она основывается на простых специфи­кации и дизайне. В традиционной модели НТТР клиент открывает соединение с сервером прикладной части, отправляет HTTP-запрос типа GET, POST, PUT или DELETE, а HTTP-сервер возвращает соответствующий ответ.

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

    Для подобных целей стандартный подход "запрос-ответ " налагает слишком сильные ограничения. Первыми попытками обойти эти ограничения стали AJAX и Comet. Оба были основаны на так называемых длинных опросах: открытии НТТР-соединения и поддержании его в активном состоянии (сохранении соединения открытым) посредством незавершения отправки ответа.

    С помощью веб-сокетов клиент может создать "сырой " сокет для сервера и осу­ществлять полнодуплексную связь. Поддержка веб-сокетов была введена в JSR-356 .

    Пакет javax.websocket и его серверный подпакет содержат все относящиеся к веб­ сокетам классы, интерфейсы и аннотации.

    Чтобы реализовать веб-сокеты на платформе Java ЕЕ, вам необходимо создать класс конечной точки с методами жизненного цикла веб-сокета, как показано ниже:

    // Пример конечной точки package com.devchronicles.websockets; public class HelloEndpoint extends Endpoint { @Override public void onOpen(final Session session, EndpointConfig config) { session.addMessageHandler(new MessageHandler.Whole() { @Override public void onMessage(String msg) { try { session.getBasicRemote().sendText("Hello " + msg); } catch (IOException e) { } } }); } }

    // Пример конечной точки

    public class HelloEndpoint extends Endpoint {

    @Override

    public void onOpen (final Session session , EndpointConfig config ) {

    session . addMessageHandler (new MessageHandler . Whole () {

    @Override

    public void onMessage (String msg ) {

    try {

    } catch (IOException e ) { }

    } ) ;

    Класс Endpoint предоставляет три метода жизненного цикла: onOpen , onClose и onError . Расширяющий его класс должен реализовать как минимум метод onOpen .

    Вы можете развернуть конечную точку двумя способами: с помощью конфигу­рации или программными средствами.

    Чтобы выполнить развертывание кода ваше при­ложение должно вызвать следующее:

    ServerEndpointConfig.Builder.create(HelloEndpoint.class, "/hello").build();

    ServerEndpointConfig . Builder . create (HelloEndpoint . class , "/hello" ) . build () ;

    Развернутый веб-сокет доступен на ws://://hello . Од­нако лучше использовать конфигурацию с помощью аннотации. При этом та же конечная точка становится кодом который приводится ниже:

    // Пример конечной точки с аннотациями package com.devchronicles.websockets; @ServerEndpoint("/hello") public class HelloEndpoint { @OnMessage public void onMessage(Session session, String msg) { try { session.getBasicRemote().sendText("Hello " + msg); } catch (IOException e) { } } }

    // Пример конечной точки с аннотациями

    package com . devchronicles . websockets ;

    @ServerEndpoint ("/hello" )

    public class HelloEndpoint {

    @OnMessage

    public void onMessage (Session session , String msg ) {

    try {

    session . getBasicRemote () . sendText ("Hello " + msg ) ;

    } catch (IOException e ) { }

    Такой подход позволяет вам использовать аннотации, придерживаясь подхода простых Jаvа-объектов в старом стиле (POJO ), поскольку вы не расширяете базо­вый класс. У аннотированной конечной точки те же методы жизненного цикла, что и в первом примере кода, но она вводит дополнительный метод жизненного цикла onMessage .

    Вместо реализации OnOpen и добавления обработчика onMessage в основанном на аннотациях подходе достаточно реализовать аннотированный метод жизненного цикла onMessage . Вы можете аннотировать с помощью @ОnМеssаgе несколько методов, чтобы получать различные типы данных, такие как String или ByteBuffer для дво­ичных данных.

    Реализация веб-сокета со стороны клиента зависит от используемого веб-фрейм­ворка. Как бы то ни было, в следующем фрагменте показана простая версия на языке JavaScript :

    var webSocket = new WebSocket("ws://127.0.0.1:8080/websockets/hello"); webSocket.send("world");

    Веб-сокеты

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

    Если вы создаете веб-приложение, в котором требуется серьезное двустороннее взаимодействие браузера с веб-сервером, лучшим подходом к его реализации (не прибегая к помощи Flash) будет, возможно, использование объекта XMLHttpRequest. В зависимости от типа создаваемого приложения этот подход может работать так, как требуется. Но здесь существует и достаточное количество возможных проблем.

    Прежде всего, объект XMLHttpRequest не очень хорошо подходит для быстрого обмена множественными сообщениями (например, в чате). Потом, в нем нет возможности связать один вызов с другим, поэтому при каждом новом запросе от веб-страницы сервер должен вычислять с самого начала, кому эта страница принадлежит. Поэтому уровень сложности кода для обработки ряда связанных запросов от веб-страницы может очень быстро вырасти до практически нереализуемой.

    Для всех этих проблем есть решение, хотя оно еще не вполне готово. Этим решением является технология веб-сокетов (web sockets) , которая позволяет браузеру удерживать открытое подключение к серверу и обмениваться сообщениями в течение любого требуемого времени.

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

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

    Получение доступа к веб-сокетам

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

    Решения на основе веб-сокетов могут быть чрезвычайно сложны. Разработать JavaScript-код для одной страницы будет достаточно простой задачей. Но для создания серверного приложения вам потребуются бешеные знания и навыки программирования, включая понимание концептов многопоточности и сетевого взаимодействия.

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

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

    Чтобы дать вам представление о масштабе сервера веб-сокетов, рассмотрите некоторые из задач, которые сервер сокетов должен выполнять:

      составить "словарь" сообщений, иными словами, решить, какие типы сообщений являются допустимыми, а какие нет;

      выявлять ошибки при отправке сообщений клиентам и прекратить попытки связаться с ними, если кажется, что их больше не существует;

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

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

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

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

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

    Var socket = new WebSocket("ws://localhost/socketServer.php");

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

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

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

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

    Socket.onopen = connectionOpen; socket.onmessage = messageReceived; socket.onerror = errorOccurred; socket.onopen = connectionClosed;

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

    Function connectionOpen() { socket.send("UserName:[email protected]"); }

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

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

    Function messageReceived(e) { messageLog.innerHTML += "

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

    Socket.disconnect();

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

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

    Примеры веб-сокетов в сети

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

    Для начала попробуйте сайт websocket.org , который предоставляет простейший сервер веб-сокетов: веб-страница отправляет ему сообщение, а он возвращает это же сообщение веб-странице:

    Хотя этот сервер веб-сокетов и не представляет ничего особенного, на нем вы можете испробовать все возможности объекта WebSocket. Более того, к этому серверу можно подключиться со страницы, расположенной как на промышленном веб-сервере, так и на тестовом веб-сервере на вашем компьютере, или даже со страницы, просто запускаемой с жесткого диска:

    Var socket = new WebSocket("ws://echo.websocket.org"); socket.onopen = connectionOpen; socket.onmessage = messageReceived; function connectionOpen() { socket.send("UserName:[email protected]"); } function messageReceived(e) { var messageLog = document.getElementById("messageLog"); messageLog.innerHTML += "
    " + "Ответ сервера: " + e.data; }

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

    • Сергей Савенков

      какой то “куцый” обзор… как будто спешили куда то