Разработка высоконагруженного websocket-сервиса

チャットのサンプル

ブラウザの WebSocket API と Node.js WebSocket モジュール https://github.com/websockets/ws を使用してチャットのサンプルを見てみましょう。主にクライアントサイドに注目しますが、サーバも簡単です。

HTML: メッセージを送信するための と受信メッセージ用の が必要です:

JavaScript から次の3つのことをします:

  1. 接続をオープンします
  2. フォームの送信 – メッセージに対して をします
  3. メッセージの受信 – に追加していきます

これはそのコードです:

サーバサイドのコードは、少し今回のスコープを超えています。ここでは Node.js を使っていますが、そうでなくてもOKです。他のプラットフォームにも WebSocket でやり取りする手段があります。

サーバサイドのアルゴリズムは次の通りです:

  1. socket の集合 を作成します。
  2. 受け入れられた各 websocket を で clients に追加し、 イベントリスナーを設定し、メッセージを取得します。
  3. メッセージを受信すると、clients をイテレートし全員に送信します。
  4. 接続を閉じられると、 をします。

動作するサンプルです:

ダウンロード(iframeの右上のボタン)をして、ローカルで実行することもできます。実行前に Node.js のインストールと をするのを忘れないでください。

Connection close

Normally, when a party wants to close the connection (both browser and server have equal rights), they send a “connection close frame” with a numeric code and a textual reason.

The method for that is:

  • is a special WebSocket closing code (optional)
  • is a string that describes the reason of closing (optional)

Then the other party in event handler gets the code and the reason, e.g.:

Most common code values:

  • – the default, normal closure (used if no supplied),
  • – no way to set such code manually, indicates that the connection was lost (no close frame).

There are other codes like:

  • – the party is going away, e.g. server is shutting down, or a browser leaves the page,
  • – the message is too big to process,
  • – unexpected error on server,
  • …and so on.

The full list can be found in .

WebSocket codes are somewhat like HTTP codes, but different. In particular, any codes less than are reserved, there’ll be an error if we try to set such a code.

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

Теперь задача настроить Денвер. Мне довелось работать с разными сборками Денвера. Последняя из них 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 установлено и работает хорошо.

Метод первый, напрямую использовать Workerman для открытия SSL

Готов к работе:

1. Версия Workerman не ниже 3.3.7

2. PHP устанавливает расширение openssl

3. Сертификат (файл pem / crt и файл ключа) был подан и помещен в любой каталог на диске.

Код:

С помощью приведенного выше кода Workerman отслеживает протокол wss, а клиент может подключаться к workerman через протокол wss для реализации безопасного обмена мгновенными сообщениями.

тест

Откройте браузер Chrome, нажмите F12, чтобы открыть консоль отладки, введите в столбце Console (или поместите следующий код на страницу html для запуска с js)

нота:

1. Если не запускается, порт 443 обычно занят. Переключитесь на другой порт

Обратите внимание, что после перехода на другой порт клиенту необходимо указать номер порта при подключении. Адрес при подключении клиента похож на wss: //domain.com: xxx, xxx — номер порта

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

2. К порту wss можно получить доступ только через протокол wss, а ws не может получить доступ к порту wss.

3. Сертификат обычно привязан к имени домена, поэтому при тестировании клиента используйте для подключения имя домена вместо ip.

4. Если вы не можете получить доступ, проверьте брандмауэр сервера.

5. Для этого метода требуется версия PHP> = 5.6, поскольку апплет WeChat требует tls1.2, а версии ниже PHP5.6 не поддерживают tls1.2.

Или 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 на дешевом хостинге можно обойтись и без него.

Все определения WSS

Акроним Определение
WSS Web Security Suite
WSS Web Services симпозиум
WSS Windaroo Государственная школа
WSS WindowsSoundSystem
WSS Woongoolba Государственная школа
WSS Worongary Государственная школа
WSS Безопасность веб-служб
WSS Безопасность системы водоснабжения
WSS Ван станции наборы
WSS Веб службы Планировщик
WSS Веб-самообслуживания
WSS Вес воспринимая систему
WSS Вестсайдская история
WSS Взвешенный сигнал подпространство
WSS Воде и санитарным услугам
WSS Водоснабжение и санитария
WSS Войны экономия штамп
WSS Всемирная встреча на высшем уровне
WSS Выбор состояния ожидания
WSS Датчик скорости колеса
WSS Длина волны селективный выключатель
WSS Еженедельного статистического приложения
WSS Западная Государственная школа
WSS Мир общество позвоночника
WSS Моделирование Спутниковое WDS
WSS Ну стимуляции услуги
WSS Оптовая доставка система
WSS Оружие системы моделирования
WSS Почему так грустно
WSS Рабочая станция-сервер
WSS Сайт для хранения оружия
WSS Самообслуживание на рабочем месте
WSS Сварные специальные разделы
WSS Свинг западного общества
WSS Секция стандартизации оружия
WSS Сервер подписки Погода
WSS Синдром морщинистой кожи
WSS Система беспроводного абонента
WSS Система поддержки вафельные
WSS Система поддержки войны/оружие
WSS Система программного обеспечения рабочих станций
WSS Системы Уолл-стрит
WSS Системы веб-хранилища
WSS Службы Windows Sharepoint Services
WSS Смарт-стандарты работы
WSS Смотреть станции стандартов
WSS Средневзвешенная склоне спектральной
WSS Стандарта сервера приложений WebSphere
WSS Уайт Салфер Спрингс
WSS Уивер — синдром Смит
WSS Уинстон-Сейлем круизной железнодорожная компания
WSS Услуги во всем мире ценных бумаг
WSS Что она сказала
WSS Широкий космических систем
WSS Широкий экран сигнализации
WSS Широком смысле стационарные
WSS Широкополосные стерео сигнала
WSS Эскадрилья Погода

Что означает WSS в тексте

В общем, WSS является аббревиатурой или аббревиатурой, которая определяется простым языком. Эта страница иллюстрирует, как WSS используется в обмена сообщениями и чат-форумах, в дополнение к социальным сетям, таким как VK, Instagram, Whatsapp и Snapchat. Из приведенной выше таблицы, вы можете просмотреть все значения WSS: некоторые из них образовательные термины, другие медицинские термины, и даже компьютерные термины. Если вы знаете другое определение WSS, пожалуйста, свяжитесь с нами. Мы включим его во время следующего обновления нашей базы данных. Пожалуйста, имейте в информации, что некоторые из наших сокращений и их определения создаются нашими посетителями. Поэтому ваше предложение о новых аббревиатур приветствуется! В качестве возврата мы перевели аббревиатуру WSS на испанский, французский, китайский, португальский, русский и т.д. Далее можно прокрутить вниз и щелкнуть в меню языка, чтобы найти значения WSS на других 42 языках.

Cheat sheet¶

Server

  • Write a coroutine that handles a single connection. It receives a websocket
    protocol instance and the URI path in argument.

    • Call and
      to receive and
      send messages at any time.
    • You may or
      if you wish
      but it isn’t needed in general.
  • Create a server with which is similar to
    asyncio’s .

    • The server takes care of establishing connections, then lets the handler
      execute the application logic, and finally closes the connection after
      the handler returns.
    • You may subclass and
      pass it in the keyword argument for advanced customization.

Client

  • Create a server with which is similar to
    asyncio’s .
  • Call and
    to receive and
    send messages at any time.
  • You may or
    if you wish but it
    isn’t needed in general.
  • Call to terminate
    the connection.

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

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

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

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

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

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

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

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

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

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

Вот пример:

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

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

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

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

Secure example¶

Secure WebSocket connections improve confidentiality and also reliability
because they reduce the risk of interference by bad proxies.

The WSS protocol is to WS what HTTPS is to HTTP: the connection is encrypted
with Transport Layer Security (TLS) — which is often referred to as Secure
Sockets Layer (SSL). WSS requires TLS certificates like HTTPS.

Here’s how to adapt the server example to provide secure connections. See the
documentation of the module for configuring the context securely.

#!/usr/bin/env python

# WSS (WS over TLS) server example, with a self-signed certificate

import asyncio
import pathlib
import ssl
import websockets

async def hello(websocket, path):
    name = await websocket.recv()
    print(f"< {name}")

    greeting = f"Hello {name}!"

    await websocket.send(greeting)
    print(f"> {greeting}")

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
ssl_context.load_cert_chain(localhost_pem)

start_server = websockets.serve(
    hello, "localhost", 8765, ssl=ssl_context
)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Here’s how to adapt the client.

#!/usr/bin/env python

# WSS (WS over TLS) client example, with a self-signed certificate

import asyncio
import pathlib
import ssl
import websockets

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
localhost_pem = pathlib.Path(__file__).with_name("localhost.pem")
ssl_context.load_verify_locations(localhost_pem)

async def hello():
    uri = "wss://localhost:8765"
    async with websockets.connect(
        uri, ssl=ssl_context
    ) as websocket
        name = input("What's your name? ")

        await websocket.send(name)
        print(f"> {name}")

        greeting = await websocket.recv()
        print(f"< {greeting}")

asyncio.get_event_loop().run_until_complete(hello())

This client needs a context because the server uses a self-signed certificate.

WebSocket compression

The extension is disabled by default on the server and enabled by default on the
client. It adds a significant overhead in terms of performance and memory
consumption so we suggest to enable it only if it is really needed.

Note that Node.js has a variety of issues with high-performance compression,
where increased concurrency, especially on Linux, can lead to catastrophic
memory fragmentation and slow performance. If you intend to use
permessage-deflate in production, it is worthwhile to set up a test
representative of your workload and ensure Node.js/zlib will handle it with
acceptable performance and memory usage.

See for more options.

import WebSocket, { WebSocketServer } from 'ws';

const wss = new WebSocketServer({
  port: 8080,
  perMessageDeflate: {
    zlibDeflateOptions: {
      // See zlib defaults.
      chunkSize: 1024,
      memLevel: 7,
      level: 3
    },
    zlibInflateOptions: {
      chunkSize: 10 * 1024
    },
    // Other options settable:
    clientNoContextTakeover: true, // Defaults to negotiated value.
    serverNoContextTakeover: true, // Defaults to negotiated value.
    serverMaxWindowBits: 10, // Defaults to negotiated value.
    // Below options specified as default values.
    concurrencyLimit: 10, // Limits zlib concurrency for perf.
    threshold: 1024 // Size (in bytes) below which messages
    // should not be compressed.
  }
});

The client will only use the extension if it is supported and enabled on the
server. To always disable the extension on the client set the
option to .

import WebSocket from 'ws';

const ws = new WebSocket('ws://www.host.com/path', {
  perMessageDeflate: false
});

Установление 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, обрезать заголовки или оборвать передачу.

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

High-level API¶

Server

The module defines a simple WebSocket server API.

(ws_handler, host=None, port=None, *, klass=WebSocketServerProtocol, origins=None, **kwds)

This coroutine creates a WebSocket server.

It’s a thin wrapper around the event loop’s create_server method.
host, port as well as extra keyword arguments are passed to
create_server.

ws_handler is the WebSocket handler. It must be a coroutine accepting
two arguments: a and
the request URI. If provided, origin is a list of acceptable Origin HTTP
headers. Include if the lack of an origin is acceptable.

It returns a Server object with a close method to stop the server.

Whenever a client connects, the server accepts the connection, creates a
, performs the opening
handshake, and delegates to the WebSocket handler. Once the handler
completes, the server performs the closing handshake and closes the
connection.

Since there’s no useful way to propagate exceptions triggered in handlers,
they’re sent to the websockets.server logger instead. Debugging is much
easier if you configure logging to print them:

import logging
logger = logging.getLogger('websockets.server')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())
class (ws_handler, *, origins=None, host=None, port=None, secure=None, timeout=10, max_size=2 ** 20, loop=None)

Complete WebSocket server implementation as an asyncio protocol.

This class inherits most of its methods from
.

For the sake of simplicity, this protocol doesn’t inherit a proper HTTP
implementation. Its support for HTTP responses is very limited.

(origins=None)

Perform the server side of the opening handshake.

If provided, is a list of acceptable HTTP Origin values.
Include if the lack of an origin is acceptable.

Return the URI of the request.

Client

The module defines a simple WebSocket client API.

(uri, *, klass=WebSocketClientProtocol, origin=None, **kwds)

This coroutine connects to a WebSocket server.

It accepts an keyword argument to set the Origin HTTP header.

It’s a thin wrapper around the event loop’s create_connection method.
Extra keyword arguments are passed to create_server.

It returns a which can
then be used to send and receive messages.

It raises if uri is invalid and
if the handshake fails.

Clients shouldn’t close the WebSocket connection. Instead, they should
wait until the server performs the closing handshake by yielding from the
protocol’s attribute.

implements the sequence called “Establish a WebSocket
Connection” in RFC 6455, except for the requirement that “there MUST be no
more than one connection in a CONNECTING state.”

class (*, host=None, port=None, secure=None, timeout=10, max_size=2 ** 20, loop=None)

Complete WebSocket client implementation as an asyncio protocol.

This class inherits most of its methods from
.

(wsuri, origin=None)

Perform the client side of the opening handshake.

If provided, sets the HTTP Origin header.

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

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

Example¶

Here’s a WebSocket server example. It reads a name from the client and sends a
message.

#!/usr/bin/env python

import asyncio
import websockets

@asyncio.coroutine
def hello(websocket, path):
    name = yield from websocket.recv()
    print("< {}".format(name))
    greeting = "Hello {}!".format(name)
    yield from websocket.send(greeting)
    print("> {}".format(greeting))

start_server = websockets.serve(hello, 'localhost', 8765)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Here’s a corresponding client example.

#!/usr/bin/env python

import asyncio
import websockets

@asyncio.coroutine
def hello():
    websocket = yield from websockets.connect('ws://localhost:8765/')
    name = input("What's your name? ")
    yield from websocket.send(name)
    print("> {}".format(name))
    greeting = yield from websocket.recv()
    print("< {}".format(greeting))

asyncio.get_event_loop().run_until_complete(hello())

Метод второй: используйте nginx / apache для прокси-сервера wss

В дополнение к использованию собственного SSL Workerman вы также можете использовать nginx / apache в качестве прокси-сервера wss для перенаправления на workerman (обратите внимание, что часть workerman этого метода не должна устанавливать ssl, иначе он не сможет подключиться). Принцип и процесс коммуникации:

Принцип и процесс коммуникации:

1. Клиент инициирует соединение wss для подключения к nginx / apache.

2. nginx / apache преобразует данные протокола wss в данные протокола ws и пересылает их на порт протокола WebSocket Workerman.

3. Workerman выполняет обработку бизнес-логики после получения данных.

4. Когда Workerman отправляет сообщение клиенту, происходит обратный процесс. Данные преобразуются в протокол wss с помощью nginx / apache и затем отправляются клиенту.

データ転送

WebSocket 通信は “フレーム” (データフラグメント、どちら側からでも送信でき、いくつかの種類があります)で構成されます。 :

  • “text frames” – 関係者が互いに送信するテキストデータを含んでいます。
  • “binary data frames” – 関係者が互いに送信するバイナリデータを含んでいます。
  • “ping/pong frames” は接続確認に使用されます。サーバから送信され、ブラウザは自動でそれらに応答します。
  • “connection close frame” やその他いくつかのサービスフレームもあります。

ブラウザでは、テキストフレームまたはバイナリフレームのみを直接扱います。

WebSocket メソッドはテキストまたはバイナリデータを送信できます。

呼び出しは、文字列または や などを含むバイナリ形式の が許可されます。設定は必要ありません。任意のフォーマットで送信するだけでOKです。

データを受信したとき、テキストは常に文字列として来ます。また、バイナリデータの場合は 、 形式のいずれかを選択することができます。

これは プロパティで設定されます。デフォルトは なので、バイナリデータは オブジェクトで来ます。

Blob は高レベルのバイナリオブジェクトで、, 等といったタグと直接統合されます。そのため、これは妥当なデフォルト値です。ただし、バリナリ処理の場合に個々のバイトデータにアクセスする必要があれば、 に変更することができます。

Client Example using the W3C WebSocket API

var W3CWebSocket = require('websocket').w3cwebsocket;

var client = new W3CWebSocket('ws://localhost:8080/', 'echo-protocol');

client.onerror = function() {
    console.log('Connection Error');
};

client.onopen = function() {
    console.log('WebSocket Client Connected');

    function sendNumber() {
        if (client.readyState === client.OPEN) {
            var number = Math.round(Math.random() * 0xFFFFFF);
            client.send(number.toString());
            setTimeout(sendNumber, 1000);
        }
    }
    sendNumber();
};

client.onclose = function() {
    console.log('echo-protocol Client Closed');
};

client.onmessage = function(e) {
    if (typeof e.data === 'string') {
        console.log("Received: '" + e.data + "'");
    }
};

Протокол работы с фреймами

  • (1 бит): указывает на то, является ли этот фрейм последним фреймом, завершающим передачу сообщения. Чаще всего для передачи сообщения достаточно одного фрейма и этот бит всегда оказывается установленным. Эксперименты показали, что Firefox создаёт второй фрейм после того, как размер сообщения превысит 32 Кб.
  • , , (каждое по 1-му биту): эти поля должны быть установлены в 0, только если не было достигнуто договорённости о расширениях, которая и определит смысл их ненулевых значений. Если в одном из этих полей будет установлено ненулевое значение и при этом не было достигнуто договорённости о смысле этого значения, получатель должен признать соединение недействительным.
  • (4 бита): здесь закодированы сведения о содержимом фрейма. В настоящее время используются следующие значения:

    • : в этом фрейме находится следующая часть передаваемого сообщения.
    • : в этом фрейме находятся текстовые данные.
    • : в этом фрейме находятся бинарные данные.
    • : этот фрейм завершает соединение.
    • : это ping-фрейм.
    • : это pong-фрейм.
  • (1 бит): указывает на то, что фрейм замаскирован. Сейчас дело обстоит так, что каждое сообщение от клиента к серверу должно быть замаскировано, в противном случае спецификации предписывают разрывать соединения.
  • (7 битов): длина полезной нагрузки. Фреймы WebSocket поддерживают следующие методы указания размеров полезной нагрузки. Значение 0-125 указывает на длину полезной нагрузки. 126 означает, что следующие два байта означают размер. 127 означает, что следующие 8 байтов содержат сведения о размере. В результате длина полезной нагрузки может быть записана примерно в 7 битах, или в 16, или в 64-х.
  • (32 бита): все фреймы, отправленные с клиента на сервер, замаскированы с помощью 32-битного значения, которое содержится во фрейме.
  • : передаваемые во фрейме данные, которые, наверняка, замаскированы. Их длина соответствует тому, что задано в .

вот интересное обсуждение

Changelog¶

2.6

  • Added and attributes on protocols.
  • Closed open connections with code 1001 when a server shuts down.
  • Avoided TCP fragmentation of small frames.

2.5

  • Improved documentation.
  • Provided access to handshake request and response HTTP headers.
  • Allowed customizing handshake request and response HTTP headers.
  • Supported running on a non-default event loop.
  • Returned a 403 error code instead of 400 when the request Origin isn’t
    allowed.
  • Cancelling no
    longer drops the next message.
  • Clarified that the closing handshake can be initiated by the client.
  • Set the close status code and reason more consistently.
  • Strengthened connection termination by simplifying the implementation.
  • Improved tests, added tox configuration, and enforced 100% branch coverage.

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.

Оптимизации потребления RAM

Формула Камардина

Миллион WebSocket и pub/sub

  1. Две горутины (одна читает, другая пишет);
  2. HTTP-буферы на чтение и на запись от стандартной библиотеки Go, которая аллоцируется, потому что WebSocket-соединение начинается с HTTP;
  3. Буферы, которые мы в приложении используем для ввода-вывода.

Going Infinite, handling 1 millions websockets connections in Go

Gobwas/ws: beyond std lib

Библиотека

  1. . Мы убираем использование стандартного HTTP-сервера Go и сами парсим HTTP, при этом не аллоцируя дополнительной памяти.
  2. Возможность использовать epoll/kqueue (см. netpoll, gnet, evio, gaio), используя syscalls и минуя рантайм и Go netpoller. Тем самым можно даже отойти от двух горутин, одна из которых пишет, а другая читает. Также можно переиспользовать все буферы, о которых шла речь.

#15735 net: add mechanism to wait for readability on a TCPConn

  • JSON
  • Protobuf
  • Msgpack, CBOR
  • Свой протокол

gogo/protobufне рекомендовал

Инвалидация соединения

  • Push. Вы можете подписаться на события, если у вас есть какая-то шина данных. Например, у нас в Авито есть Kafka как шина данных. Мы подписались на такие события и отключаем WebSocket.
  • Pull. Но если такой шины данных нет, можно просто периодически проверять, что WebSocket-соединение активно (периодическая валидация). Это дороже, но тут нужно найти trade-off между производительностью и тем интервалом, с которым вы проверяете каждое висящее соединение.

Итого

WebSocket – современное средство коммуникации. Кросс-доменное, универсальное, безопасное.

На текущий момент он работает в браузерах IE10+, FF11+, Chrome 16+, Safari 6+, Opera 12.5+. В более старых версиях FF, Chrome, Safari, Opera есть поддержка черновых редакций протокола.

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

Есть и готовые библиотеки, реализующие функциональность COMET с использованием сразу нескольких транспортов, из которых вебсокет имеет приоритет. Как правило, библиотеки состоят из двух частей: клиентской и серверной.

Например, для Node.JS одной из самых известных библиотек является Socket.IO.

К недостаткам библиотек следует отнести то, что некоторые продвинутые возможности WebSocket, такие как двухсторонний обмен бинарными данными, в них недоступны. С другой – в большинстве случаев стандартного текстового обмена вполне достаточно.

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

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

Adblock
detector