Wss docs

От сокета к веб-сокету

Теперь, когда модуль сокетов активирован в PHP, нам предстоит путь от написания простого сокет скрипта, до скрипта способного корректно общаться по протоколу веб-сокет. Дело в том, что PHP умеет только технически работать с сокетами: принимать соедние, заводить очередь, отправлять/принимать данные, находиться в режиме ожидания и другие технические вещи. Но в нём совершенно отсутствуют готовые заголовки и другие метаданные необходимые для веб-сокетов. Обычное сокет-соединение отличается от веб-сокетов тем, что мы можем отправлять любые сообщения в произвольном формате, в то время, как веб-сокет требует обязательной отправки заголовков вида “GET / HTTP/1.1\r\nUpgrade: websocket\r\n” и соблюдение достаточного количества других правил.

Есть еще один важный нюанс, теперь касающийся PHP — скрипт обрабатывающий сокет-соединения отличается от обычного скрипта PHP, который многократно выполняется от начала до конца при разгрузке страницы обычно менее чем за 0,1 секунды. Отличаются скрипты работы с WebSocket-ами тем, что длительность их выполнения должна быть бесконечной. Т.е. мы должны инициировать выполнение PHP-скрипта содержащего бесконечный цикл, в котором происходит получение/отправка сообщений по протоколу веб-сокет. На самом деле с этим не должно быть никаких проблем, т.к. существует большое количество способов снять ограничение по времени выполнения PHP скрипта, помимо этого можно запустить скрипт в бэкграунде пользуясь консолью сервера. На PHP вполне возможно даже создание полноценного демона. Существуют готовые решения, о которых я упоминал выше, например PhpDaemon, но сегодня речь не о них.

Для начала, чтобы убедиться что мне не мешают файрволлы, всё настроено правильно и связь между клиентом и сервером может быть установлена, я решил написать и протестировать небольшой PHP скрипт выполняющий роль сокет-сервера (именно сокет, а не веб-сокет!), устанавливающий соединение и отвечающий всем фразой “Hello, Client!”. Но для его тестирования нужно несколько клиентов (веб-сокет клиент, чтобы понять базовое отличие от простого сокета и обычный telnet).

Добавление веб-сокетов в Денвер

Теперь задача настроить Денвер. Мне довелось работать с разными сборками Денвера. Последняя из них Denwer3_Base_2013-06-02_a2.2.22_p5.3.13_m5.5.25_pma3.5.1_xdebug.exe, к сожалению, она и другие имеющиеся в настоящий момент сборки Денвера(дело в PHP) не поддерживают сокеты по умолчанию.

Но эта проблема решается путём поиска и установки подходящей php_sockets.dll. Для того, чтобы всё заработало, достаточно разместить dll файл в каталоге Денвера \usr\local\php5\ext\php_sockets.dll и отредактировать файл \usr\local\php5\php.ini убрав точку с запятой перед строкой

extension=php_sockets.dll

перезагрузить Денвер и всё, или почти всё: в некоторых случаях при перезагрузке Денвера может возникнуть ошибка

это значит что вы используете не подходящую версию файла php_sockets.dll. Чтобы облегчить поиски предлагаю две версии – одна гарантированно подходит для PHP 5.2.12 (скачать), а вторая для гарантированно подходит для PHP 5.3.13 (скачать). Но если вам не подошел ни один из этих файлов, предлагаю скачать полный архив соответствующей версии php для windows с веб-сайта php.net и найти в архиве файл php_sockets.dll, который точно подойдёт.

Теперь можете запустить файлик sockettest.php через браузер и увидеть заветную строку “WebSockets OK”, означающую что всё необходимое для работы с Web-Socket установлено и работает хорошо.

Node.js Socket Example

Let’s say you need to write a server application, and you chose Node.js as your programming language. Your users can be any type of client, like a web browser, mobile app, IoT device, Node.js client, or anything that knows TCP.

You need to serve assets to your users over HTTP, but you also need to provide them with streams for bi-directional messaging. We can accomplish this in a single Node.js server app!

The code from the video, and also this article is available in my Node.js WebSocket Examples GitHub Repository.

First we’ll go over some plain socket code, followed by WebSocket code. If you already serve assets with something like Express.js, Hapi, or the native Node.js HTTP library, we can jump into the socket code.

Socket Server JavaScript Code

// Node.js socket server script
const net = require('net');

// Create a server object
const server = net.createServer((socket) => {
  socket.on('data', (data) => {
    console.log(data.toString());
  });

  socket.write('SERVER: Hello! This is server speaking.');
  socket.end('SERVER: Closing connection now.');
}).on('error', (err) => {
  console.error(err);
});

// Open server on port 9898
server.listen(9898, () => {
  console.log('opened server on', server.address().port);
});

This script runs a Node.js socket server on port 9898. Whenever a client connects to this server app (IP_ADDRESS:9898) the server sends the client a string over the open socket. It says “SERVER: Hello! This is server speaking.” Whenever the client sends some data to the server, the Node.js script logs the contents in the ‘data’ event handler.

Socket Client JavaScript Code

Here we have our Node.js socket client script, which can connect to the Node.js socket server above.

// Node.js socket client script
const net = require('net');

// Connect to a server @ port 9898
const client = net.createConnection({ port: 9898 }, () => {
  console.log('CLIENT: I connected to the server.');
  client.write('CLIENT: Hello this is client!');
});

client.on('data', (data) => {
  console.log(data.toString());
  client.end();
});

client.on('end', () => {
  console.log('CLIENT: I disconnected from the server.');
});

The client script attempts to connect to localhost:9898. If the connection succeeds, then the client sends a string to the server over the open socket. It says “CLIENT: Hello this is client!” Whenever the server sends some data to the client, the client logs it in the ‘data’ event handler.

This is what the output looks like for client and server Node.js socket scripts running on the command line.

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

С точки зрения веб-страницы функциональность веб-сокетов легко понять и использовать. Первый шаг — это создать объект 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. Эти заполнители применяют метод опроса, чтобы эмулировать подключение веб-сокетов.

Opening handshake

Before starting the exchange of messages, the client and server negotiate the parameters of the establishing connection. WebSocket reuses the existing HTTP Upgrade mechanism with custom Sec-WebSocket-* headers to perform the connection negotiation.

An example of an HTTP to WebSocket upgrade request:

GET /socket HTTP/1.1Host: server.comConnection: UpgradeUpgrade: websocket Origin: http://example.comSec-WebSocket-Version: 8, 13Sec-WebSocket-Key: 7c0RT+Z1px24ypyYfnPNbw==Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stompSec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

An example of an HTTP to WebSocket upgrade response:

HTTP/1.1 101 Switching Protocols Connection: UpgradeUpgrade: websocketAccess-Control-Allow-Origin: http://example.comSec-WebSocket-Accept: O1a/o0MeFzoDgn+kCKR91UkYDO4=Sec-WebSocket-Protocol: v12.stompSec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15

The opening handshake consists of the following parts: protocol upgrade, origin policies negotiation, protocol negotiation, subprotocol negotiation, extensions negotiation.

To pass the protocol upgrade:

  • the client sends a request with the Connection and Upgrade headers
  • the server confirms the protocol upgrade with 101 Switching Protocols response line and the same Connection and Upgrade headers

To pass the origin policies negotiation:

  • the client sends the Origin header (scheme, host name, port number)
  • the server confirms that the client from this origin is allowed to access the resource via the Access-Control-Allow-Origin header

To pass the protocol negotiation:

  • the client sends the Sec-WebSocket-Version (a list of protocol versions, 13 for RFC 6455) and Sec-WebSocket-Key (an auto-generated key) headers
  • the server confirms the protocol by returning the Sec-WebSocket-Accept header
Base64       .getEncoder()       .encodeToString(               MessageDigest                       .getInstance("SHA-1")                       .digest((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")                               .getBytes(StandardCharsets.UTF_8)));

To pass subprotocol negotiation:

  • the client sends a list of subprotocols via the Sec-WebSocket-Protocol header
  • the server select one of the subprotocols via the Sec-WebSocket-Protocol header (if the server does not support any subprotocol, then the connection is canceled)

To pass the extensions negotiation:

  • the client sends a list of extensions via the Sec-WebSocket-Extensions header
  • the server confirms one or more extensions via the Sec-WebSocket-Extensions header (if the server does not support some extensions, then the connection proceeds without them)

After a successful handshake, the client and the server switch from text HTTP protocol to binary WebSocket message framing and can perform full-duplex communication.

A simple example

To open a websocket connection, we need to create using the special protocol in the url:

There’s also encrypted protocol. It’s like HTTPS for websockets.

Always prefer

The protocol is not only encrypted, but also more reliable.

That’s because data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see “strange” headers and abort the connection.

On the other hand, is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at sender and decrypts at the receiver. So data packets are passed encrypted through proxies. They can’t see what’s inside and let them through.

Once the socket is created, we should listen to events on it. There are totally 4 events:

  • – connection established,
  • – data received,
  • – websocket error,
  • – connection closed.

…And if we’d like to send something, then will do that.

Here’s an example:

For demo purposes, there’s a small server server.js written in Node.js, for the example above, running. It responds with “Hello from server, John”, then waits 5 seconds and closes the connection.

So you’ll see events → → .

That’s actually it, we can talk WebSocket already. Quite simple, isn’t it?

Now let’s talk more in-depth.

Методы

Закрывает WebSocket — подключение или заканчивает попытку подключения. Если подключение уже закрыто, этот метод не делает ничего.

void close(
  in optional unsigned short code,
  in optional DOMString reason
);
Параметры
Необязательный
Числовое значение, обозначающее статус-код, описывающий почему подключение будет закрыто. Если параметр не указан, значение по умолчанию равно 1000(обозначает «обмен завершён»). Смотрите  для , чтобы узнать разрешённые значения.
Необязательный
Читаемая человеком строка, объясняющая, почему подключение закрывается. Строка должна быть не длиннее, чем 123 байта UTF-8 текста (не символов). 
Возможные исключения
Указан неверный .
Строка  слишком длинные или содержит непарные суррогаты.
Примечания

В Gecko этот метод не поддерживает никакие параметры включительно до Gecko 8.0 (Firefox 8.0 / Thunderbird 8.0 / SeaMonkey 2.5).

Передаёт данные на сервер через WebSocket — соединение.

void send(
  in DOMString data
);

void send(
  in ArrayBuffer data
);

void send(
  in Blob data
);
Параметры
Текстовая строка, которая будет отправлена на сервер.
Возможные исключения
Соединение ещё не открыто.
Строка  содержит непарные суррогаты

Заметьте: Gecko — реализация метода  несколько отличается от специфицированной в Gecko 6.0; Gecko возвращает , обозначающий, открыто ли соединение до сих пор (и, в дополнение, были ли доставлены данные); это было исправлено в Gecko 8.0.

Начиная с Gecko 11.0, поддержка  была реализована, но не  типы данных.

Обработка разрывов соединения

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

Реализация образца сервера

Если клиент такой простой, возможно, сложной окажется реализация сервера? Обработчик сервера для SSE может выглядеть следующим образом:

function handler(response)
{
// настраиваем заголовки для ответа с целью получить постоянное HTTP-соединение
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});

// составляем сообщение
response.write('id: UniqueIDn');
response.write("data: " + data + 'nn'); // каждый раз, когда мы вводим два символа новой строки, сообщение отправляется автоматически
}

Определяем функцию, которая будет обрабатывать ответ:

  1. Устанавливать заголовки;
  2. Создавать сообщение;
  3. Отправлять.

Обратите внимание, что здесь нет вызов метода send() или метода push(). Стандарт определяет: сообщение будет отправлено, как только в него будет добавлено два символа n n, как например: response.write(«data: » + data + ‘nn’);

В результате сообщение будет немедленно отправлено клиенту.

Составление сообщений

Сообщение может содержать несколько свойств:

1. ID

Если значение этого поля не содержит U + 0000 NULL, устанавливаем для буфера последнего идентификатора события значение поля. Иначе игнорируем поле.

2. Data

Добавляем значение поля в буфер, затем добавляем в буфер один символ U + 000A LINE FEED (LF).

3. Event

Устанавливаем для буфера тип события и значение поля. Это приводит к тому, что для event.type задается пользовательское имя события.

4. Retry

Если значение поля состоит только из цифр ASCII, тогда интерпретируем значение поля как целое число в десятичной системе исчисления. А также устанавливаем для времени повторного соединения потока событий это целое число. В противном случае игнорируем поле.

Все остальное будет проигнорировано. Мы не можем вводить собственные поля.

Пример с добавленным event:

response.write('id: UniqueIDn');    response.write('event: addn');    response.write('retry: 10000n');    response.write("data: " + data + 'nn');

В клиенте это обрабатывается с помощью addEventListener следующим образом:

source.addEventListener("add", function(event) {        // выполняем действия с данными        event.data;    });

Вы можете отправлять несколько сообщений, разделенных символом новой строки, а также использовав для них разные идентификаторы.

...    id: 54    event: add    data: ""     id: 55    event: remove    data: JSON.stringify(some_data)     id: 56    event: remove    data: {    data: "msg" : "JSON data"n    data: "field": "value"n    data: "field2": "value2"n    data: }nn

Это значительно упрощает то, что мы можем сделать с нашими данными.

Простой пример

Чтобы открыть веб-сокет-соединение, нам нужно создать объект , указав в url-адресе специальный протокол :

Также существует протокол , использующий шифрование. Это как HTTPS для веб-сокетов.

Всегда предпочитайте

Протокол не только использует шифрование, но и обладает повышенной надёжностью.

Это потому, что данные не зашифрованы, видны для любого посредника. Старые прокси-серверы не знают о WebSocket, они могут увидеть «странные» заголовки и закрыть соединение.

С другой стороны, – это WebSocket поверх TLS (так же, как HTTPS – это HTTP поверх TLS), безопасный транспортный уровень шифрует данные от отправителя и расшифровывает на стороне получателя. Пакеты данных передаются в зашифрованном виде через прокси, которые не могут видеть, что внутри, и всегда пропускают их.

Как только объект создан, мы должны слушать его события. Их всего 4:

  • – соединение установлено,
  • – получены данные,
  • – ошибка,
  • – соединение закрыто.

…А если мы хотим отправить что-нибудь, то вызов сделает это.

Вот пример:

Для демонстрации есть небольшой пример сервера server.js, написанного на Node.js, для запуска примера выше. Он отвечает «Привет с сервера, Джон», после ожидает 5 секунд и закрывает соединение.

Так вы увидите события → → .

В общем-то, всё, мы уже можем общаться по протоколу WebSocket. Просто, не так ли?

Теперь давайте поговорим более подробно.

Закрытие соединений

Когда вы закончите работу с веб-сокетом, нужно разорвать соединение, используя метод close().

socket.close();

После того, как соединение будет разорвано, браузер вызовет событие close. Добавление обработчика события close позволит выполнить любую «уборку», которая потребуется.

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

// Показываем сообщение «disconnected», когда соединение разорвано.
socket.onclose = function(event) {
socketStatus.innerHTML = 'Disconnectedfrom WebSocket.';
socketStatus.className = 'closed';
};

Чтобы завершить приложение, нужно добавить обработчик события, который будет вызываться при нажатии кнопки «Close Connection». Он должен вызывать метод close() объекта WebSocket.

// Закрываем соединение при нажатии кнопки «close»
closeBtn.onclick = function(e) {
e.preventDefault();

  // Закрываем веб-сокет.
  socket.close();

  return false;
};

Наше приложение готово!

Откройте файл index.html в браузере и попробуйте отправить несколько сообщений. Вы увидите, что сервер отправляет сообщения обратно.

Передача данных

Поток данных в WebSocket состоит из «фреймов», фрагментов данных, которые могут быть отправлены любой стороной, и которые могут быть следующих видов:

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

В браузере мы напрямую работаем только с текстовыми и бинарными фреймами.

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

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

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

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

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

Peer-to-Peer Message Passing

The above samples are intuitive code implementations for real-time application development. What if we want to stream data from user to user? What if we want to do this without implementing the complicated routing logic on our server to connect two or more users? We can accomplish all of this cheaply, securely, and without having to spin-up expensive infrastructure.

We have access to awesome, hosted, real-time infrastructure with PubNub. We can infinitely scale to any user volume. PubNub offers low cost, transaction-based pricing with easy to use messaging APIs. Let’s take a look at an example for peer-to-peer messaging. By the way, this can also be implemented to stream data from client to server, or server to client, in addition to peer-to-peer.

Before you try the following scripts you must get your free PubNub API keys. They work for up to 1 million free transactions per month, forever. Use this form to get free API keys right now.

PubNub Data Streaming

We can bi-directionally send data between services, clients, or server apps using the PubNub global data stream network. There is an SDK for every popular language and device. Here is the basic browser JS or Node.js code for utilizing the Pub/Sub network. This publishes a message whenever the user hits the return key.

const PubNub = require('pubnub'); // or in browser: <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.24.4.js"></script>

const pubnub = new PubNub({
    publishKey: 'your_pubnub_publish_api_key_here',
    subscribeKey: 'your_pubnub_subscribe_api_key_here'
});

pubnub.subscribe({
    channels: 
});

pubnub.addListener({
    message: (pubnubMessage) => {
        console.log('New Message:', pubnubMessage.message);
    }
});

// Use Control + C to end the program
process.stdin.on('data', (key) => {
    // When the user presses the return key
    if (key.toString() === '') {
        pubnub.publish({
            message: 'Hello from client 1!',
            channel: 'my_channel'
        });
    }
});

Note that the “process” object cannot be accessed in web browser JS, but a similar key press event handler can accomplish the same functionality.

object.addEventListener('keypress',(event) => {});

PubNub Node.js SDK

Here is the documentation for the PubNub JavaScript SDK.

When we execute the above script in 2 terminal windows, we can see real-time Pub/Sub in action. I made a second script with the same code, except the published message indicates that it comes from client number “2.” Be sure to install the PubNub Node.js SDK before you try executing the scripts on your command line.

npm install pubnub

That’s all there is to it. I hope you were able to glean some valuable insights from this peer-to-peer, client-to-server, and server-to-client WebSocket data streaming tutorial. Please reach out to us at devrel@pubnub.com if you need assistance or have some questions about building a real-time application. We love hearing your ideas!

For more on implementing JavaScript with PubNub, take a look at our React Native blog post tutorials for real-time geolocation tracking, a real-time chat app, and more! There is also a blog post about socket programming in Python. Thanks for reading! If you liked this post, subscribe to our newsletter!

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

В нашем случае лучше всего использовать сервер на основе цикла событий. Например, 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 также могут поддерживаться форматы фреймворка событий, определенные другими спецификациями.

License¶

Copyright (c) 2013-2015 Aymeric Augustin and contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of websockets nor the names of its contributors may
      be used to endorse or promote products derived from this software without
      specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Важные API

  • DataReader
  • DataWriter
  • DataWriter.DetachStream
  • MessageWebSocket
  • MessageWebSocket.Closed
  • MessageWebSocket.ConnectAsync
  • MessageWebSocket.Control
  • MessageWebSocket.Information
  • MessageWebSocket.MessageReceived
  • MessageWebSocket.OutputStream
  • MessageWebSocketControl
  • MessageWebSocketControl.MessageType
  • MessageWebSocketInformation
  • MessageWebSocketMessageReceivedEventArgs
  • SocketMessageType
  • StreamWebSocket
  • StreamWebSocket.Closed
  • StreamSocket.ConnectAsync
  • StreamWebSocket.Control
  • StreamWebSocket.Information
  • StreamWebSocket.InputStream
  • StreamWebSocket.OutputStream
  • StreamWebSocketControl
  • StreamWebSocketInformation
  • WebErrorStatus
  • WebSocketError.GetStatus
  • Windows.Networking.Sockets

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

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

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

Что я хочу: Без установки phpDaemon (phpd), NodeJS и прочих вещей на локальную машину и хостинг, продолжить разработку своего проекта, но теперь с WebSocket, в этой статье разберем простой WebSocket эхо сервер.

Чего я не хочу: Говоря о NodeJS, не хочется переписывать серерную логику с PHP на другой язык, тем более устанавливать NodeJS, хотя и люблю JavaScript больше чем PHP.

Важно: не путайте демона написанного на php, с фреймворком асинхронных приложений phpDaemon. Который, конечно же, обязательно потребуется в случае развития проекта и многократного роста нагрузки на хостинг

Но для начала работы с WebSocket на дешевом хостинге можно обойтись и без него.

Заключение

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

Вот как выглядит окончательная рабочая установка:

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

NGINX дает нам следующее:

  • Прокси-сервер для конечных точек API в разных местах;
  • HTTP / 2 и все его преимущества, такие как мультиплексирование для соединений;
  • Балансировка нагрузки;
  • SSL.

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

Основные преимущества этого подхода:

  • Эффективность данных;
  • Упрощенная реализация;
  • Соединения автоматически мультиплексируются через HTTP / 2;
  • Механизм экономии заряда аккумулятора пользовательского устройства путем разгрузки соединения с прокси-сервером.

SSE позволяет обойтись без дополнительных затрат ресурсов, в отличие от альтернатив. Что касается серверной реализации, она не сильно отличается от поллинга. Но в клиенте все намного проще, чем при поллинге, поскольку для него требуется первоначальная подписка и назначение обработчиков событий. Это очень похоже на управление WebSockets.

Здесь вы можете найти демо-версию кода простой реализации клиента и сервера.

Пожалуйста, оставьте свои отзывы по текущей теме материала. За комментарии, отклики, подписки, дизлайки, лайки огромное вам спасибо!

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

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

Adblock
detector