Разное

Сокет веб: WebSockets — полноценный асинхронный веб / Хабр

HTML5 | Веб-сокеты

89

Веб-программирование — HTML5 — Веб-сокеты

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

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

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

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

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



Поддержка браузерами веб-сокетов
БраузерIEFirefoxChromeSafariOperaSafari iOSAndroid
Минимальная версия10614612.16

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

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

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

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

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

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

Сказ о том как я свой REST фреймворк с веб-сокетами писал / Хабр

Эта статья посвящена очередному REST фреймворку (для Python 3), особенностью которого является использование веб-сокетов для обмена данными между клиентом и сервером. О том откуда пришла идея, с чем мне пришлось столкнулся при написании своей первой библиотеки для Python и что из этого в итоге получилось, я расскажу далее.
Для тех, кому интересна эта статья — пожалуйста, заходите под кат.

1. Идея проекта

Идея зародилась примерно в середине Апреле 2015, когда я задержался с коллегой на работе, с которым мы числимся на одном проекте в своей конторе. Чтобы как-то минимально себя развлечь, пока занимались непосредственно программированием, мы решили поговорить о различных интересных питоновских проектах. В процессе общения как-то спонтанно подошли к теме о собственных проектах и того, что можно было бы интересно использовать далее в своих проектах (не обязательно связанных с работой). При обсуждении непосредственно и возникла идея того, что было бы классно иметь достаточно «гибкий» фреймворк, который использует веб-сокеты, через которые можно передавать данные в обе стороны без каких-либо проблем. Каждый запрос приходит в JSON формате и содержит некоторые заголовки, которые привычны при использовании REST и HTTP протокола. И в качестве приятного дополнения предоставляет возможность передачи уведомлений (нотификаций) со стороны сервера клиенту из коробки по какому-то событию/тайм-ауту.

Естественно после столь продолжительного обсуждения я решился воплотить эту идею в жизнь (а почему бы и да?). Собственный интерес, энтузиазм и желание сделать что-нибудь полезное для развития экосистемы третьего Python’а только давало лишнюю мотивацию побыстрее приступить к делу.

2. Постановка целей

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

  • Постараться использовать asyncio при обработке клиентских запросов
  • Не более 1-2 зависимых модулей (чем меньше, тем лучше)
  • Не должна быть слишком сложной для понимания
  • Легкость в использовании (см. фреймворки Django REST, Flask, которые достаточно простые и гибкие)
  • Программист может заменить практически любой компонент, тогда, когда ему это необходимо

Естественно, выпустить в первой же версии все вышеупомянутое для меня было совсем чем-то нереальным, поскольку из процесса разработки я просто бы не выходил, поэтому в целях небольшого упрощения было принято решение разбить все на небольшие «кусочки». Их сделать, протестировать, пустить в релиз, а затем уже делать по аналогичной схеме все оставшееся. Сначала пишем то, что является наиболее критичным для библиотеки (роутинг, вьюхи, аутентификация, и т.д.), а позднее, по мере возможностей, добавляем новый функционал.

3. Подготовка к разработке: выбор между Aiohttp vs Gevent vs Autobahn.ws

Разработка началась примерно в конце Апреля 2015. Чтобы как-то облегчить себе работу при реализации проекта, начались поиски каких-либо уже готовых решений (или уже существующих библиотек, о которых ранее не предполагал). Библиотек, которые бы имели схожую идею с моей или хотя бы минимально имели из коробки то, что предполагается сделать – не нашлось. Это привело к усложнению задачи, поскольку большую часть необходимых компонентов потребуется написать самостоятельно, исходя из собственного понимания всех происходящих процессов.

Я решил начать непосредственно с библиотек, которые дают возможность использовать веб-сокеты. На тот момент времени было найдено несколько таких пакетов: aiohttp, gevent и autobahn.ws. У каждой библиотеки есть свои достоинства и недостатки, но, в первую очередь, я исходил из их возможностей и возможного дальнейшего переиспользования кода, чтобы не приходилось в очередной раз городить свои велосипеды, особенно там, где это не нужно.

Aiohttp – библиотека для веб-разработки, базирующая на стандартной библиотеке asyncio и разработанная svetlov. Не сказать, что у меня был какой-то большой реальный опыт использования этой библиотеки, хотя стоит отметить, что сделано множество вещей очень классно. Однако, предлагаемое решение с веб-сокетами показалось мне несколько низкоуровневым (хотя, в ряде случаев это действительно может быть удобно). Хотелось какого-то большего уровня абстракции (например, как в gevent-websocket или autobahn.ws, где в клиенте/сервере есть методы вроде onMessage и sendMessage, столь похожие на методы из событийно-ориентированного фреймворка Twisted). В остальном же – библиотека прекрасна.

Gevent при первом рассмотрении был одним из тех первых пакетов, на которые было заострено внимания. И также быстро идея о использовании её была отклонена: на момент времени начала проекта (Апрель 2015) gevent не был портирован под третью ветку языка Python. Хотя, если бы все же она была портирована, то я использовал бы именно её, взяв при этом еще расширение gevent-websocket и все могло бы выйти очень даже неплохо. На момент написания статьи данная библиотека уже имеет поддержку третьей ветки, но переходить на нее сейчас я не вижу никакого смысла.

Autobahn.ws – это та библиотека, с которой мне уже ранее приходилось неоднократно сталкиваться при написании своих небольших pet-проектов и с которой у меня уже имеется некий минимальный опыт использования. Достаточно неплохое коммьюнити, плюс автор библиотеки всегда готов помочь в случае возникших проблем (например, когда у меня не получалось совместить ее с Twisted + wxPython, Тобиас очень хорошо объяснил мне как это можно сделать). Последние версии совместимы с asyncio, достаточно добавить декораторы в требуемых местах. Приятной особенностью еще было соответствие документу RFC6455 и наличие проверки входящих/исходящих данных (поступили/отправлены ли они в UTF-8 кодировке, что я считаю достаточно удобно). Поэтому было принято решение использовать именно её в качестве основы для будущей библиотеки.

4. Проблемы, возникшие при разработке

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

1) Получили запрос

2) Проверили что пришли необходимые данные, на основе которых станет понятно каким образом обработать запрос (тип операции, куда обращаемся, и т.д.)

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

4) Сформированный ответ привели к определенному формату (JSON, XML, и т.д.)

5) Вернули ответ клиенту

В теории все звучит довольно просто, на деле все оказалось все в точности наоборот. Единственное, что мне приходило в голову, это идти от высокого уровня абстракции к нижним. То есть я шел следующим образом, когда мы работаем с Autobahn.ws и asyncio loop:

1) Создаем экземпляр «фабрики», который будет использовать asyncio loop и принимать входящие подключения и обслуживать их. После выполненного «процесса рукопожатия» мы готовы получать запросы от клиента и выполнять их обработку.

2) Получили запрос от клиента в определенном формате. В нашем случае мы будем получать его в виде JSON следующим образом:

{
   'method': 'POST', 
   'url': '/users/create',
   'args': {
       'token': 'aGFicmFoYWJyX2FkbWlu'
   },
   'data': {
       'username': 'habrahabr',
       'password': 'mysupersecretpassword',
   }
}

Этот JSON имеет достаточно простую структуру. Клиент достаточно определить несколько важных для нас параметров:

  • method – тип операции над ресурсом (подобно тому, как это сделано в HTTP).
  • url – путь к ресурсу, с которым хотим работать.
  • args (опционально) – набор параметров, отсылаемых серверу. Наиболее близкая аналогия это определяемые параметры в URL’е HTTP запроса с помощью «?» и «&» символов, вроде «habrahabr.ru/?page=2&paginate_by=25». Это может быть какой-то список готовых данных (например, идентификаторы пользователей, которым надо назначить определенную группу) или просто набор аргументов для каких-либо фильтров, используемых на стороне сервера в процессе обработки запроса.
  • data (опционально) – набор данных, используемых при работе с ресурсом. В целом, можете считать, что это некий аналог телу HTTP запроса.
  • event_name (опционально) — некий уникальный идентификатор, с помощью которого можно понять от какого endpoint’а вернулись данные.

Примерно такого вида запросы будет ожидать получить наш сервер. Если какого-либо из обязательных аргументов нету – говорим об этом сразу (например, забыли добавить method). В противном случае идем далее по нашему списку.

3) Итак, запрос доставлен серверу, он правильном формате и корректен. Теперь мы хотим его обработать соответствующим образом и вернуть ответ. Однако, что нам для этого необходимо? С моей точки зрения, на первое время будет достаточно наличие системы роутинга, позволяющей зарегистрировать на определенный URL требуемый обработчик, который бы формировал соответствующий ответ, преобразовывал его в JSON, XML (или любой другой формат) и возвращал его клиенту.

В этом пункте хочу я обратить ваше внимание на роутинг. Это достаточно важный момент, поскольку нам хотелось бы предоставлять доступ по некоторому фиксированному URL, чтобы получать, например, список текущих пользователей (вроде «/users/»). С другой стороны получать доступ и по URL имеющих вид «/users//», по которым требуется получать детальную информацию о пользователе. То есть роутинг первого вида мы будем рассматривать как простой, статический, а второй – как динамический, поскольку в пути к ресурсу присутствует параметр, меняющийся от запроса к запросу.

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

router = SimpleRouter()
router.register('/auth/login', LogIn, 'POST')
router.register('/users/{pk}', UserDetail, ['GET', 'PATCH'])

Мы будем выполнять анализ пути к такому ресурсу. И создавать endpoint, который будет обрабатывать запросы только определенного типа и только по указанному пути. Когда придет запрос на этот ресурс, нам будет достаточно пройтись по словарю, где ключом будет путь, а значением – обработчик. В случае, если обнаружен динамический путь, в момент получения запроса, и мы нашли требуемый обработчик, то будем пробрасывать обнаруженный динамический параметр в место обработки запроса, чтобы было возможным получить объект по ключу либо сделать какую-то иную операцию с использованием этого параметра.

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

4) Здорово, теперь кое что прояснилось. Умеем находить требуемые пути, обработчики для них, а с помощью регулярок находить и пробрасывать параметры (для случая если попался динамический путь). Далее мы смотрим на параметр method, указанный в JSON и стараемся найти соответствующий метод класса с вьюшки. Если он отсутствует – говорим об этом сразу и не выполняем каких-либо операций. В противном случае делаем вызов обнаруженного метода и формируем ответ.

5) Далее выполняем сериализацию данных (в том числе и для случаев с ошибками) в некоторый формат. По умолчанию все преобразуется в JSON формат.

6) Передаем сформированный ответ клиенту обратно по веб-сокету.

И вот по этому примерному плану я следовал до релиза 1.0. Было достаточно интересно написать свои вьюшки, систему роутинга и прочий интересный функционал. Хотя в процессе написания первого релиза, по ходу развития этого pet-проекта, потребовались модули с конфигурациями (в нашем случае это был модуль аналогичный тому, что есть в Django). Или, например, столь необходимая мне аутентификация медленно привела к реализации поддержки middleware и JSON Web Token модулей. Как и упоминалось ранее – делаем всевозможные модули самостоятельно, не стараемся тянуть что-то лишнее.

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

Если при написании первой версии написание кода и его отладка шла достаточно неплохо, то при реализации версии 1.1 я просто надолго повяз в отладке. Написание и портирование кода не занимало столь много времени, сколько поиск и детальный анализ того что происходит, например:

1) Анализ исходной кодовой базы Django REST фреймворка на предмет того, что и как происходит «под капотом»: что делаем когда хотим записать или прочитать определенный объект; когда и каким образом понимаем, что за поля были получены (и имеют ли они вообще какие-то связи с другими моделями) и во что требуется их сериализовать/десериализовать.

2) Сериализация моделей SQLAlchemy по аналогии с тем, как это происходит между Django REST кодом и Django ORM.

3) Иметь такую возможность работы с роутингом, чтобы можно было получить путь до некоторого объекта через уже написанный API (так, чтобы можно было и прочитать, и записать какие-то данные по полученным URL).

При разработке этой части функционала мне весьма сильно помогли исходные коды библиотеки как Django REST (которая во многом являлась основой для следующей версии), так и исходники SQLAlchemy + marshmallow-sqlalchemy библиотек, которые во многом помогли воплотить все задумки в жизнь.

Хоть и было затрачено очень много ресурсов, но конечный результат полностью оправдал все затраты – теперь мы имеем возможность работать с SQLAlchemy так, как мы привыкли это делать в Django REST. Работа с данными осуществляется одинаково и практически не имеет сильных отличий. Здорово, даже практически переучиваться нет необходимости: доступный API во многом идентичен тому, что используется в Django REST.

5. Текущее состояние проекта

На текущий момент времени библиотека предоставляет следующие возможности:

  • Роутинг
  • Поддержка function- и class-based вьюшек
  • Аутентификация через JSON Web Token (хоть и немного ограничено)
  • Поддержка файла с конфигурацией, подобной той, что есть в Django Framework
  • Сжатие передаваемых сообщений (если поддерживается браузером и установлено нужное расширение)
  • Сериализация моделей Django и SQLAlchemy ORM
  • Поддержка SSL

6. Пример использования

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

# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, validates

Base = declarative_base()


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True)
    fullname = Column(String(50), default='Unknown')
    password = Column(String(512))
    addresses = relationship("Address", back_populates="user")

    @validates('name')
    def validate_name(self, key, name):
        assert '@' not in name
        return name

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (self.name, self.fullname, self.password)


class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))
    user = relationship("User", back_populates="addresses")

    def __repr__(self):
        return "<Address(email_address='%s')>" % self.email_address

Теперь опишем соответствующие сериализаторы для этих двух моделей:

# -*- coding: utf-8 -*-
from app.db import User, Address
from aiorest_ws.db.orm.sqlalchemy import serializers

from sqlalchemy.orm import Query


class AddressSerializer(serializers.ModelSerializer):

    class Meta:
        model = Address
        fields = ('id', 'email_address')


class UserSerializer(serializers.ModelSerializer):
    addresses = serializers.PrimaryKeyRelatedField(queryset=Query(Address), many=True, required=False)

    class Meta:
        model = User

Как многие из успели заметить, в месте, где мы определили класс для сериализации пользователей, указано поле addresses, с аргументом queryset=Query(Address) в конструкторе класса PrimaryKeyRelatedField. Это сделано для того, чтобы сериализатор для SQLAlchemy ORM мог выстроить связь между полем addresses и таблицей, передавая в этот класс при сериализации первичные ключи. В какой-то степени это аналогично QuerySet из Django фреймворка.

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

# -*- coding: utf-8 -*-
from aiorest_ws.conf import settings
from aiorest_ws.db.orm.exceptions import ValidationError
from aiorest_ws.views import MethodBasedView

from app.db import User
from app.serializers import AddressSerializer, UserSerializer


class UserListView(MethodBasedView):

    def get(self, request, *args, **kwargs):
        session = settings.SQLALCHEMY_SESSION()
        users = session.query(User).all()
        data = UserSerializer(users, many=True).data 
        session.close()
        return data

    def post(self, request, *args, **kwargs):
        if not request.data:
            raise ValidationError('You must provide arguments for create.')

        if not isinstance(request.data, list):
            raise ValidationError('You must provide a list of objects.')

        serializer = UserSerializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return serializer.data


class UserView(MethodBasedView):

    def get(self, request, id, *args, **kwargs):
        session = settings.SQLALCHEMY_SESSION()
        instance = session.query(User).filter(User.id == id).first()
        data = UserSerializer(instance).data 
        session.close()
        return data

    def put(self, request, id, *args, **kwargs):
        if not request.data:
            raise ValidationError('You must provide an updated instance.')

        session = settings.SQLALCHEMY_SESSION()
        instance = session.query(User).filter(User.id == id).first()
        if not instance:
            raise ValidationError('Object does not exist.')

        serializer = UserSerializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        session.close()
        return serializer.data


class CreateUserView(MethodBasedView):

    def post(self, request, *args, **kwargs):
        serializer = UserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return serializer.data


class AddressView(MethodBasedView):

    def get(self, request, id, *args, **kwargs):
        session = settings.SQLALCHEMY_SESSION()
        instance = session.query(User).filter(User.id == id).first()
        session.close()
        return AddressSerializer(instance).data


class CreateAddressView(MethodBasedView):

    def post(self, request, *args, **kwargs):
        serializer = AddressSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        session.close()
        return serializer.data

На текущий момент времени мы пишем отдельно вьюшки для работы с объектами и отдельно со списком объектов. В каждом из таких подклассов, унаследованных от MethodBasedView, реализуются конкретные обработчики, которые будут использоваться. Для каждого типа запроса (get/post/put/patch/ и т.п.) пишется свой обработчик.

Последним шагом является регистрация этого API, и чтобы он был доступен нам извне:

# -*- coding: utf-8 -*-
from aiorest_ws.routers import SimpleRouter

from app.views import UserListView, UserView, CreateUserView, AddressView, \
    CreateAddressView

router = SimpleRouter()
router.register('/user/list', UserListView, 'GET')
router.register('/user/{id}', UserView, ['GET', 'PUT'], name='user-detail')
router.register('/user/', CreateUserView, ['POST'])
router.register('/address/{id}', AddressView, ['GET', 'PUT'], name='address-detail')
router.register('/address/', CreateAddressView, ['POST'])

Вообщем-то здесь все готово, остается только запустить сервер и подключиться через какой-нибудь клиент (Python + Autobahn.ws, используя JavaScript, и так далее, вариантов множество). Для примера я просто покажу парочку простых запросов с использованием Python + Authobahn.ws (оговорюсь заранее, пример с клиентом не идеален, здесь задача просто продемонстировать как мы можем это делать):

# -*- coding: utf-8 -*-
import asyncio
import json

from hashlib import sha256
from autobahn.asyncio.websocket import WebSocketClientProtocol, \
    WebSocketClientFactory


def hash_password(password):
    return sha256(password.encode('utf-8')).hexdigest()


class HelloClientProtocol(WebSocketClientProtocol):

    def onOpen(self):
        # Create new address
        request = {
            'method': 'POST',
            'url': '/address/',
            'data': {
                "email_address": '[email protected]'
            },
            'event_name': 'create-address'
        }
        self.sendMessage(json.dumps(request).encode('utf8'))

        # Get users list
        request = {
            'method': 'GET',
            'url': '/user/list/',
            'event_name': 'get-user-list'
        }
        self.sendMessage(json.dumps(request).encode('utf8'))

        # Create new user with address
        request = {
            'method': 'POST',
            'url': '/user/',
            'data': {
                'name': 'Neyton',
                'fullname': 'Neyton Drake',
                'password': hash_password('123456'),
                'addresses': [{"id": 1}, ]
            },
            'event_name': 'create-user'
        }
        self.sendMessage(json.dumps(request).encode('utf8'))

        # Trying to create new user with same info, but we have taken an error
        self.sendMessage(json.dumps(request).encode('utf8'))

        # Update existing object
        request = {
            'method': 'PUT',
            'url': '/user/6/',
            'data': {
                'fullname': 'Definitely not Neyton Drake',
                'addresses': [{"id": 1}, {"id": 2}]
            },
            'event_name': 'partial-update-user'
        }
        self.sendMessage(json.dumps(request).encode('utf8'))


    def onMessage(self, payload, isBinary):
        print("Result: {0}".format(payload.decode('utf8')))


if __name__ == '__main__':
    factory = WebSocketClientFactory("ws://localhost:8080")
    factory.protocol = HelloClientProtocol

    loop = asyncio.get_event_loop()
    coro = loop.create_connection(factory, '127.0.0.1', 8080)
    loop.run_until_complete(coro)
    loop.run_forever()
    loop.close()

Более детально посмотреть весь исходный код примера можно здесь.

7. Дальнейшее развитие

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

  • Поддержка уведомлений
  • Просмотр через браузер документации к API (возможно в виде плагина для Swagger)
  • Модули для тестирования API
  • Клиенты для Python и JavaScript
  • Поддержка Pony и Peewee ORM’ов

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

8. И в заключении…

Мне кажется получилось достаточно неплохо для первого раза, не смотря на отсутствие какого-либо опыта в написании собственных библиотек. А внести свой вклад (пусть даже и небольшой) в развитие языка Python – хочется достаточно сильно. Не удивляйтесь тому, сколько времени было на это было затрачено: все делалось (и продолжает делаться) в свободное время и периодическими перерывами (поскольку регулярная работа с одним проектом очень утомляет, а развиваться хочется в нескольких направлениях одновременно).

Так или иначе, буду рад услышать все ваши предложения, идеи и улучшения по данной библиотеке в комментариях (или в виде пул реквестов у меня на GitHub). Не стесняйтесь задавать какие-либо вопросы относительно библиотеки и каких-то особенностей реализации – буду рад любому фидбеку.

Весь вышеприведенный код, а также исходники библиотеки aiorest-ws, можно посмотреть на GitHub. Примеры расположены в корне проекта, в каталоге examples. Документацию можно посмотреть здесь.

Spring WebSocket. How it works? / Хабр

Доброго времени суток уважаемые хабравчане. На моем текущем месте работы было принято решение перевести взаимодействие с web клиентом на WebSocket. Серверная часть написана на Java с использованием фреймворка Spring. В данной статье я хотел поделиться особенностью устройства Spring WebSocket.

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

Протокол состоит из двух фаз:


Для Handshake запроса используется HTTP GET запрос, в результате которого происходит обновление соединения до WebSocket.

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

В разборе будем использовать Spring-WebSocket и Annotation based конфигурацию.

Создание конфигурационного класса

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

Для использования WebSocket в конфигурационном классе нам необходимо использовать аннотацию @EnableWebSocket. Из описания данной аннотации следует необходимость реализовать интерфейс WebSocketConfigurer нашим конфигурационным классом. Интерфейс WebSocketConfigurer содержит единственный метод registerWebSocketHandlers(WebSocketHandlerRegistry registry). Используя входной параметр WebSocketHandlerRegistry мы осуществляем добавление обработчиков (WebSocketHandler) входящих сообщений на определенный url.

Рассмотрим работу аннотации @EnableWebSocket более подробно.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebSocketConfiguration.class)
public @interface EnableWebSocket {
}

Основная задача данной аннотации — импорт конфигурационного класса DelegatingWebSocketConfiguration, который при помощи @Autowired получит экземпляры интерфейса WebSocketConfigurer (наш конфигурационный класс). Данные экземпляры WebSocketConfigurer используются в родительском классе WebSocketConfigurationSupport для создания бина HandlerMapping.

Создание бина HandlerMapping необходимо чтобы в дальнейшем DispatcherServlet смог определить обработчик для данного url.


Для преобразования WebSocketHandler в экземпляр HandlerMapping нам потребуются адаптеры WebSocketHttpRequestHandler и WebSocketHandlerMapping.

При помощи WebSocketHttpRequestHandler мы произведем приведение WebSocketHandler к HttpRequestHandler. А далее используя связку url, на который мы ждем запроса по открытию WebSocket, и HttpRequestHandler создадим экземпляр WebSocketHandlerMapping, который и будет зарегистрирован в DispatcherServlet для обработки HTTP запроса.


Когда клиент посылает запрос на открытие WebSocket соединения, запрос через DispatcherServlet приходит на метод handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) нашего декоратора WebSocketHttpRequestHandler.

В данном методе происходит вызов Interceptors, объявленных в конфигурационном классе при задании WebSocketHandlers, и вызов метода doHandshake, дефолтного или пользовательского экземпляра HandshakeHandler.

Обработка Handshake запроса

На работе метода doHandshake остановимся чуть подробнее. Здесь и происходит вся магия преобразования соединения к WebSockets. Но для начала давайте разберемся с параметрами пользовательского запроса и серверного ответа.

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

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

  • Origin — содержит url, с которого производится запрос. Используется для верификации допустимых адресов.
  • Sec-WebSocket-Protocol — определяет набор под-протоколов, к примеру, STOMP, который будет рассмотрен в следующих статьях.
  • Sec-WebSocket-Key — случайный ключ, который генерируется браузером: 16 байт в кодировке Base64.
  • Sec-WebSocket-Version — версия протокола.
  • Sec-WebSocket-Extensions — дополнительные расширения, например, permessage-deflate говорит о том, что сообщения будут передавать в сжатом виде.

Типичный ответ от сервера:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Код ответа HTTP 101 говорит об переключении протокола, в нашем случае на WebSocket.
Sec-WebSocket-Accept — рассчитанное значение на основе переданного Sec-WebSocket-Key и константы «258EAFA5-E914-47DA-95CA-C5AB0DC85B11» — по сути это подтверждение от сервера о готовности инициировать WebSocket соединение.

Работу метода doHandshake можно схематично представить в таком виде:

Стратегия обновления запроса до WebSocket

На данный момент доступны стратегии обновления соединения

  • TomcatRequestUpgradeStrategy
  • JettyRequestUpgradeStrategy
  • UndertowRequestUpgradeStrategy
  • GlassFishRequestUpgradeStrategy
  • WebLogicRequestUpgradeStrategy
  • WebSphereRequestUpgradeStrategy

Процесс обновления стратегии состоит из следующих шагов:
* Экземпляр EndPoint создается путем оборачивания WebSocketHandler в StandardWebSocketHandlerAdapter, который и является наследником EndPoint. Для создания сессии используется StandardWebSocketSession.

** Для обновления HttpServletRequest используется стандартный метод данного интерфейса update, в который передаем реализацию HttpUpgradeHandler,

в случае работы с Tomcat это WsHttpUpgradeHandler. При инициализации экземпляра HttpUpgradeHandler происходит создание и регистрация EndPonit в WebSocketContainer.

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

Спасибо большое за внимание. В следующих статьях рассмотрим работу fallback механизма при помощи SockJS и возможности под-протокола STOMP.

Использованные источники:

→ The WebSocket Protocol

→ Spring WebSocket

Используем вебсокеты в своем iOS приложении / Хабр

Добрый день, уважаемые читатели Хабрахабра!

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

Этот подход может пригодиться для:

  • Реализации чата в мобильном клиенте под iOS
  • Использовании информации чата для обучения нейронных сетей

Заинтересовавшихся прошу под кат!

Добавляем SocketRocket в проект

На гитхабе описано несколько вариантов установки, я опишу тот, которым пользуюсь сам.

  1. Добавляем в наш проект все файлы из группы «SocketRocket»
  2. Добавляем следующие фреймворки в проект:
    • libicucore.dylib
    • CFNetwork.framework
    • Security.framework
    • Foundation.framework
  3. Добавляем в проект расшифровщик HTML символов от Google (позже поймете, зачем)

Ничего сложного! Все готово для использования всей мощи вебсокетов.

Инициализируем сокеты

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

NSURL *url = [NSURL URLWithString:@"wss://ws.pusherapp.com/app/4e0ebd7a8b66fa3554a4?protocol=6&client=js&version=2.0.0&flash=false"];

Создаем сам объект сокета с нужным запросом, открываем его и делаем себя делегатом:

NSURLRequest *request = [NSURLRequest requestWithURL:url];
SRWebSocket *rusSocket = [[SRWebSocket alloc] initWithURLRequest:request];
rusSocket.delegate = self;
[rusSocket open];

Полный код метода setupSockets

- (void)setupSockets
{
    NSURL *url = [NSURL URLWithString:@"wss://ws.pusherapp.com/app/4e0ebd7a8b66fa3554a4?protocol=6&client=js&version=2.0.0&flash=false"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    SRWebSocket *rusSocket = [[SRWebSocket alloc] initWithURLRequest:request];
    rusSocket.delegate = self;
    [rusSocket open];
}
Методы SRWebSocketDelegate

В каждом уважающем себя фреймворке под Objective-C обязательно должны быть делегаты — а в вебсокетах и подавно. Мы реализуем два метода делегата — первый, который вызывается после установки соединения и открытия сокета, и второй, вызываемый по получению сообщения.

- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{
    NSString *helloMsg = @"{\"event\":\"pusher:subscribe\",\"data\":{\"channel\":\"chat_ru\"}}";
    [webSocket send:helloMsg];
}

Здесь все предельно просто: как только сокеты открываются, мы подписываемся на оповещения из русского чата. По аналогии, можно прописать и «chat_en».

Дальше мы описываем метод получения сообщения от вебсокета:

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
{
    message = [[message stringByReplacingOccurrencesOfString:@"///" withString:@""] stringByReplacingOccurrencesOfString:@"\\\\\\" withString:@""];
    message = [message gtm_stringByUnescapingFromHTML];
}

Опять же, все просто. В первой строке метода мы избавляемся от мусора, во второй мы используем категорию от Google для того, чтобы преобразовать HTML символы в читаемые пользователем. Message и есть наше сообщение — дальше его можно парсить так, как душе угодно.Пример сообщения

{«event»:«msg»,«data»:»\»{«uid»:«467754»,«login»:«BTCalexxx»,«msg»:«anyone in for a short ltc pump to 15?»,«msg_id»:12268748,«date»:«04.03.14 07:37:50»,«usr_clr»:»#8da0b9″}\»»,«channel»:«chat_en»}

Закрываем соединение

Всегда закрывайте соединение, когда оно вам уже не нужно! Сделать это можно следующим методом:

[rusSocket close];

Заключение

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

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

Nginx и Websockets / Хабр

В Nginx наконец добавили долгожданную функциональность по проксированию Websockets.
В связи с этим спешу поделиться конфигами и небольшими подробностями.

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

Теперь на одном порту можно проксировать http и ws траффик, более того, например под одним SSL сертификатом, и все это с родным знакомым синтаксисом.

Ws доступны начиная с версии 1.3.13, а буквально сегодня добавили поддержку еще в модули ngx_http_uwsgi_module и ngx_http_scgi_module для 1.3.14

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

Для превращения соединения между клиентом и сервером из HTTP/1.1 в WebSocket используется доступный в HTTP/1.1 механизм смены протокола.

Но есть сложность: поскольку “Upgrade” является hop-by-hop заголовком, то он не передаётся от клиента к проксируемому серверу. При прямом проксировании клиенты могут использовать метод CONNECT, чтобы обойти эту проблему. Однако при обратном проксировании такой подход не работает, так как клиент ничего о проксирующем сервере не знает, и требуется специальная обработка на проксирующем сервере.

Начиная с версии 1.3.13, в nginx предусмотрен особый режим работы, который позволяет установить туннель между клиентом и проксируемым сервером, если проксируемый сервер вернул ответ с кодом 101 (Switching Protocols), и клиент попросил сменить протокол с помощью заголовка “Upgrade” в запросе.

Как уже отмечалось выше, hop-by-hop заголовки, включая “Upgrade” и “Connection”, не передаются от клиента к проксируемому серверу, поэтому, для того чтобы проксируемый сервер узнал о намерении клиента сменить протокол на WebSocket, эти заголовки следует передать явно:

Конфиг.

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

location /ws/ {
    proxy_pass http://localhost:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

}

Более сложный пример, в котором значение поля “Connection” в заголовке запроса к проксируемому серверу зависит от наличия поля “Upgrade” в заголовке запроса клиента:

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        ...

        location /ws/ {
            proxy_pass http://localhost:8080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }

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

Поэтому, мы добавили:

http {
      ...
      proxy_read_timeout 950s;
      ...
}

Скорее всего вашему приложению нужны будут другие цифры таймаута, поэтому не копипастите бездумно;)

WebSockets | MDN

WebSocket Chrome
Полная поддержка

4
Край
Полная поддержка

12
Firefox
Полная поддержка

11


Полная поддержка

11

Нет поддержки
7 — 11 С префиксом Замечания
С префиксом Требует вендорный префикс: Moz
Замечания Размер сообщения ограничен 16 МБ (см. Ошибку 711205).

Нет поддержки
4 — 6
Замечания Размер сообщения ограничен 16 МБ (см. Ошибку 711205).
IE
Полная поддержка

10
Опера
Полная поддержка

12.1
Safari
Полная поддержка

5
WebView Android
Полная поддержка

≤37
Chrome Android
Полная поддержка

18
Firefox Android
Полная поддержка

14


Полная поддержка

14
Замечания См. Ошибку 695635.

Нет поддержки
7 — 14 С префиксом Замечания
С префиксом Требует вендорный префикс: Moz
Замечания Размер сообщения ограничен 16 МБ (см. Ошибку 711205).

Нет поддержки
4 — 6
Замечания Размер сообщения ограничен 16 МБ (см. Ошибку 711205).
Опера Android
Полная поддержка

12.1
Safari iOS
Полная поддержка

4.2
Samsung Интернет Android
Полная поддержка

1.0
WebSocket () конструктор Chrome
Полная поддержка

Да
Кромка
Полная поддержка

≤79
Firefox
Полная поддержка

7


Полная поддержка

7

Нет поддержки
4 — 7
Замечания Параметр протоколы не поддерживаются.
IE
?
Опера
Полная поддержка

Да
Safari
?
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

7
Опера Android
?
Safari iOS
?
Samsung Интернет Android
Полная поддержка

Да
двоичный Тип Хром
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
с буферизацией Количество Хром
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
закрыть Хром
Полная поддержка

4
Край
Полная поддержка

12
Firefox
Полная поддержка

8


Полная поддержка

8

Нет поддержки
4 — 8
Замечания Параметры не поддерживаются, см. Ошибку 674716.
IE
Полная поддержка

10
Опера
Полная поддержка

12.1
Safari
Полная поддержка

5
WebView Android
Полная поддержка

≤37
Chrome Android
Полная поддержка

18
Firefox Android
Полная поддержка

8


Полная поддержка

8

Нет поддержки
4 — 8
Замечания Параметры не поддерживаются, см. Ошибку 674716.
Опера Android
Полная поддержка

12.1
Safari iOS
Полная поддержка

4.2
Samsung Интернет Android
Полная поддержка

1.0
закрыть событие Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
?
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
?
Safari iOS
?
Samsung Интернет Android
Полная поддержка

Да
ошибка событие Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
?
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
?
Safari iOS
?
Samsung Интернет Android
Полная поддержка

Да
насадки Хром
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

8
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

8
Опера Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
сообщение событие Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
?
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
?
Safari iOS
?
Samsung Интернет Android
Полная поддержка

Да
вкл. Хром
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
ошибка Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
в сообщении Хром
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
onopen Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
открыто событие Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
?
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
?
Safari iOS
?
Samsung Интернет Android
Полная поддержка

Да
протокол Хром
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
Поддерживает протокол согласно RFC 6455 Chrome
Полная поддержка

16
Край
Полная поддержка

12
Firefox
Полная поддержка

11
IE
Полная поддержка

10
Опера
Полная поддержка

15
Safari
Полная поддержка

6
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

18
Firefox Android
Полная поддержка

14
Опера Android
Полная поддержка

14
Safari iOS
Полная поддержка

6
Samsung Интернет Android
Полная поддержка

1.0
Готово Состояние Хром
Полная поддержка

43
Край
Полная поддержка

12
Firefox
Полная поддержка

19
IE
Полная поддержка

10
Опера
Полная поддержка

30
Safari
Полная поддержка

10
WebView Android
Полная поддержка

43
Chrome Android
Полная поддержка

43
Firefox Android
Полная поддержка

19
Опера Android
Полная поддержка

30
Safari iOS
Полная поддержка

10
Samsung Интернет Android
Полная поддержка

4.0
отправить Хром
Полная поддержка

4
Край
Полная поддержка

12
Firefox
Полная поддержка

18


Полная поддержка

18
Замечания См. Ошибку 775368.

Нет поддержки
11 — 18
Замечания Поддерживаются только параметры типа ArrayBuffer и String .

Нет поддержки
8 — 11
Замечания Поддерживается только параметр типа String .

Нет поддержки
4 — 8
Замечания Поддерживается только параметр типа String . Возвращает логическое значение .
IE
Полная поддержка

10
Опера
Полная поддержка

12.1
Safari
Полная поддержка

5
WebView Android
Полная поддержка

≤37
Chrome Android
Полная поддержка

18
Firefox Android
Полная поддержка

18


Полная поддержка

18
Замечания См. Ошибку 775368.

Нет поддержки
14 — 18
Замечания Поддерживается только параметр типа ArrayBuffer и String .

Нет поддержки
8 — 14
Замечания Поддерживается только параметр типа String .

Нет поддержки
4 — 8
Замечания Поддерживается только параметр типа String .Возвращает логическое значение .
Опера Android
Полная поддержка

12.1
Safari iOS
Полная поддержка

4.2
Samsung Интернет Android
Полная поддержка

1.0
url Chrome
Полная поддержка

Да
Кромка
Полная поддержка

12
Firefox
Полная поддержка

Да
IE
?
Опера
Полная поддержка

Да
Safari
Полная поддержка

Да
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

Да
Opera Android
Полная поддержка

Да
Safari iOS
Полная поддержка

Да
Samsung Internet Android
Полная поддержка

Да
В наличии у рабочих Хром
Полная поддержка

Да
Кромка
Полная поддержка

≤18
Firefox
Полная поддержка

37
IE
?
Opera
?
Safari
?
WebView Android
Полная поддержка

Да
Chrome Android
Полная поддержка

Да
Firefox Android
Полная поддержка

37
Опера Android
?
Safari iOS
?
Samsung Интернет Android
Полная поддержка

Да

.

HTML5 | Веб-сокеты

89

Веб-программирование — HTML5 — Веб-сокеты

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

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

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

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

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

Поддержка браузерами веб-сокетов
Браузер IE Firefox Хром Safari Opera Safari iOS Android
Минимальная версия 10 6 14 6 12.1 6

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

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

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

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

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

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

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

.

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

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