Лекция
Привет, Вы узнаете о том , что такое websocket, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое websocket, вебсокет , настоятельно рекомендую прочитать все из категории Компьютерные сети.
Протокол WebSocket (стандарт RFC 6455) предназначен для решения любых задач и снятия ограничений обмена данными между браузером и сервером.
Он позволяет пересылать любые данные, на любой домен, безопасно и почти без лишнего сетевого трафика.
Для открытия соединения достаточно создать объект WebSocket, указав в нем специальный протокол ws.:
var socket = new WebSocket("ws://javascript.ru/ws");
У объекта socket есть четыре колбэка: один при получении данных и три – при изменениях в состоянии соединения:
socket.onopen = function() { alert("Соединение установлено."); }; socket.onclose = function(event) { if (event.wasClean) { alert('Соединение закрыто чисто'); } else { alert('Обрыв соединения'); // например, "убит" процесс сервера } alert('Код: ' + event.code + ' причина: ' + event.reason); }; socket.onmessage = function(event) { alert("Получены данные " + event.data); }; socket.onerror = function(error) { alert("Ошибка " + error.message); };
Для посылки данных используется метод socket.send(data). Пересылать можно любые данные.
Например, строку:
socket.send("Привет");
…Или файл, выбранный в форме:
socket.send(form.elements .file);
Просто, не правда ли? Выбираем, что переслать, и socket.send().
Для того, чтобы коммуникация была успешной, сервер должен поддерживать протокол WebSocket.
Чтобы лучше понимать происходящее – посмотрим, как он устроен.
Протокол WebSocket работает над TCP.
Это означает, что при соединении браузер отправляет по HTTP специальные заголовки, спрашивая: «поддерживает ли сервер WebSocket?».
Если сервер в ответных заголовках отвечает «да, поддерживаю», то дальше HTTP прекращается и общение идет на специальном протоколе WebSocket, который уже не имеет с HTTP ничего общего.
Пример запроса от браузера при создании нового объекта new WebSocket("ws://server.example.com/chat"):
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Origin: http://javascript.ru Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13
Описания заголовков:
GET, Host
Стандартные HTTP-заголовки из URL запроса
Upgrade, Connection
Указывают, что браузер хочет перейти на websocket.
Origin
Протокол, домен и порт, откуда отправлен запрос.
Sec-WebSocket-Key
Случайный ключ, который генерируется браузером: 16 байт в кодировке Base64.
Sec-WebSocket-Version
Версия протокола. Текущая версия: 13.
Все заголовки, кроме GET и Host, браузер генерирует сам, без возможности вмешательства JavaScript.
Такой XMLHttpRequest создать нельзя
Создать подобный XMLHttpRequest-запрос (подделать WebSocket) невозможно, по одной простой причине: указанные выше заголовки запрещены к установке методом setRequestHeader.
Сервер может проанализировать эти заголовки и решить, разрешает ли он WebSocket с данного домена Origin.
Ответ сервера, если он понимает и разрешает WebSocket-подключение:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
Здесь строка Sec-WebSocket-Accept представляет собой перекодированный по специальному алгоритму ключ Sec-WebSocket-Key. Браузер использует ее для проверки, что ответ предназначается именно ему.
Затем данные передаются по специальному протоколу, структура которого («фреймы») изложена далее. И это уже совсем не HTTP.
Также возможны дополнительные заголовки Sec-WebSocket-Extensions и Sec-WebSocket-Protocol, описывающие расширения и подпротоколы (subprotocol), которые поддерживает данный клиент.
Посмотрим разницу между ними на двух примерах:
Заголовок Sec-WebSocket-Extensions: deflate-frame означает, что браузер поддерживает модификацию протокола, обеспечивающую сжатие данных.
Это говорит не о самих данных, а об улучшении способа их передачи. Браузер сам формирует этот заголовок.
Заголовок Sec-WebSocket-Protocol: soap, wamp говорит о том, что по WebSocket браузер собирается передавать не просто какие-то данные, а данные в протоколах SOAP или WAMP («The WebSocket Application Messaging Protocol»). Стандартные подпротоколы регистрируются в специальном каталоге IANA.
Этот заголовок браузер поставит, если указать второй необязательный параметр WebSocket:
var socket = new WebSocket("ws://javascript.ru/ws", ["soap", "wamp"]);
При наличии таких заголовков сервер может выбрать расширения и подпротоколы, которые он поддерживает, и ответить с ними.
Например, запрос:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Origin: http://javascript.ru Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: deflate-frame Sec-WebSocket-Protocol: soap, wamp
Ответ:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= Sec-WebSocket-Extensions: deflate-frame Sec-WebSocket-Protocol: soap
В ответе выше сервер указывает, что поддерживает расширение deflate-frame, а из запрошенных подпротоколов – только SOAP.
Соединение WebSocket можно открывать как WS:// или как WSS://. Протокол WSS представляет собой WebSocket над HTTPS.
Кроме большей безопасности, у WSS есть важное преимущество перед обычным WS – большая вероятность соединения.
Дело в том, что HTTPS шифрует трафик от клиента к серверу, а HTTP – нет.
Если между клиентом и сервером есть прокси, то в случае с HTTP все WebSocket-заголовки и данные передаются через него. Прокси имеет к ним доступ, ведь они никак не шифруются, и может расценить происходящее как нарушение протокола HTTP, обрезать заголовки или оборвать передачу.
А в случае с WSS весь трафик сразу кодируется и через прокси проходит уже в закодированном виде. Поэтому заголовки гарантированно пройдут, и общая вероятность соединения через WSS выше, чем через WS.
Полное описание протокола содержится в RFC 6455.
Здесь представлено частичное описание с комментариями самых важных его частей. Если вы хотите понять стандарт, то рекомендуется сначала прочитать это описание.
В протоколе WebSocket предусмотрены несколько видов пакетов («фреймов»).
Они делятся на два больших типа: фреймы с данными («data frames») и управляющие («control frames»), предназначенные для проверки связи (PING) и закрытия соединения.
Фрейм, согласно стандарту, выглядит так:
С виду – не очень понятно, во всяком случае, для большинства людей.
Позвольте пояснить: читать следует слева-направо, сверху-вниз, каждая горизонтальная полоска это 32 бита.
То есть, вот первые 32 бита:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| опкод |М| Длина тела | Расширенная длина тела | |I|S|S|S|(4бита)|А| (7бит) | (1 байт) | |N|V|V|V| |С| |(если длина тела==126 или 127) | | |1|2|3| |К| | | | | | | | |А| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
Сначала идет бит FIN (вертикальная надпись на рисунке), затем биты RSV1, RSV2, RSV3 (их смысл раскрыт ниже), затем «опкод», «МАСКА» и, наконец, «Длина тела», которая занимает 7 бит. Затем, если «Длина тела» равна 126 или 127, идет «Расширенная длина тела», потом (на следующей строке, то есть после первых 32 бит) будет ее продолжение, ключ маски, и потом данные.
А теперь – подробное описание частей фрейма, то есть как именно передаются сообщения:
FIN: 1 бит
Одно сообщение, если оно очень длинное (вызовом send можно передать хоть целый файл), может состоять из множества фреймов («быть фрагментированным»).
У всех фреймов, кроме последнего, этот фрагмент установлен в 0, у последнего – в 1.
Если сообщение состоит из одного-единственного фрейма, то FIN в нем равен 1.
RSV1, RSV2, RSV3: 1 бит каждый
В обычном WebSocket равны 0, предназначены для расширений протокола. Об этом говорит сайт https://intellect.icu . Расширение может записать в эти биты свои значения.
Опкод: 4 бита
Задает тип фрейма, который позволяет интерпретировать находящиеся в нем данные. Возможные значения:
Маска: 1 бит
Если этот бит установлен, то данные фрейма маскированы. Более подробно маску и маскирование мы рассмотрим далее.
Длина тела: 7 битов, 7+16 битов, или 7+64 битов
Если значение поле «Длина тела» лежит в интервале 0-125, то оно обозначает длину тела (используется далее). Если 126, то следующие 2 байта интерпретируются как 16-битное беззнаковое целое число, содержащее длину тела. Если 127, то следующие 8 байт интерпретируются как 64-битное беззнаковое целое, содержащее длину.
Такая хитрая схема нужна, чтобы минимизировать накладные расходы. Для сообщений длиной 125 байт и меньше хранение длины потребует всего 7 битов, для бóльших (до 65536) – 7 битов + 2 байта, ну а для еще бóльших – 7 битов и 8 байт. Этого хватит для хранения длины сообщения размером в гигабайт и более.
Ключ маски: 4 байта.
Если бит Маска установлен в 0, то этого поля нет. Если в 1 то эти байты содержат маску, которая налагается на тело (см. далее).
Данные фрейма (тело)
Состоит из «данных расширений» и «данных приложения», которые идут за ними. Данные расширений определяются конкретными расширениями протокола и по умолчанию отсутствуют. Длина тела должна быть равна указанной в заголовке.
Некоторые примеры сообщений:
Нефрагментированное текстовое сообщение Hello без маски:
0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (содержит "Hello")
В заголовке первый байт содержит FIN=1 и опкод=0x1 (получается 10000001 в двоичной системе, то есть 0x81 – в 16-ричной), далее идет длина 0x5, далее текст.
Фрагментированное текстовое сообщение Hello World из трех частей, без маски, может выглядеть так:
0x01 0x05 0x48 0x65 0x6c 0x6c 0x6f (содержит "Hello") 0x00 0x01 0x20 (содержит " ") 0x80 0x05 0x57 0x6f 0x72 0x6c 0x64 (содержит "World")
А теперь посмотрим на все те замечательные возможности, которые дает этот формат фрейма.
Позволяет отправлять сообщения в тех случаях, когда на момент начала посылки полный размер еще неизвестен.
Например, идет поиск в базе данных и что-то уже найдено, а что-то еще может быть позже.
В протокол встроена проверка связи при помощи управляющих фреймов типа PING и PONG.
Тот, кто хочет проверить соединение, отправляет фрейм PING с произвольным телом. Его получатель должен в разумное время ответить фреймом PONG с тем же телом.
Эта функциональность встроена в браузерную реализацию, так что браузер ответит на PING сервера, но управлять ей из JavaScript нельзя.
Иначе говоря, сервер всегда знает, жив ли посетитель или у него проблема с сетью.
При закрытии соединения сторона, желающая это сделать (обе стороны в WebSocket равноправны) отправляет закрывающий фрейм (опкод 0x8), в теле которого указывает причину закрытия.
В браузерной реализации эта причина будет содержаться в свойстве reason события onclose.
Наличие такого фрейма позволяет отличить «чистое закрытие» от обрыва связи.
В браузерной реализации событие onclose при чистом закрытии имеет event.wasClean = true.
Коды закрытия вебсокет а event.code, чтобы не путать их с HTTP-кодами, состоят из 4 цифр:
1000
Нормальное закрытие.
1001
Удаленная сторона «исчезла». Например, процесс сервера убит или браузер перешел на другую страницу.
1002
Удаленная сторона завершила соединение в связи с ошибкой протокола.
1003
Удаленная сторона завершила соединение в связи с тем, что она получила данные, которые не может принять. Например, сторона, которая понимает только текстовые данные, может закрыть соединение с таким кодом, если приняла бинарное сообщение.
В ранних реализациях WebSocket существовала уязвимость, называемая «отравленный кэш» (cache poisoning).
Она позволяла атаковать кэширующие прокси-сервера, в частности, корпоративные.
Атака осуществлялась так:
Хакер заманивает доверчивого посетителя (далее Жертва) на свою страницу.
Страница открывает WebSocket-соединение на сайт хакера. Предполагается, что Жертва сидит через прокси. Собственно, на прокси и направлена эта атака.
Страница формирует специального вида WebSocket-запрос, который (и здесь самое главное!) ряд прокси серверов не понимают.
Они пропускают начальный запрос через себя (который содержит Connection: upgrade) и думают, что далее идет уже следующий HTTP-запрос.
…Но на самом деле там данные, идущие через вебсокет! И обе стороны вебсокета (страница и сервер) контролируются Хакером. Так что хакер может передать в них нечто похожее на GET-запрос к известному ресурсу, например http://code.jquery.com/jquery.js, а сервер ответит «якобы кодом jQuery» с кэширующими заголовками.
Прокси послушно проглотит этот ответ и закэширует «якобы jQuery».
В результате при загрузке последующих страниц любой пользователь, использующий тот же прокси, что и Жертва, получит вместо http://code.jquery.com/jquery.js хакерский код.
Поэтому эта атака и называется «отравленный кэш».
Такая атака возможна не для любых прокси, но при анализе уязвимости было показано, что она не теоретическая, и уязвимые прокси действительно есть.
Поэтому придумали способ защиты – «маску».
Для того, чтобы защититься от атаки, и придумана маска.
Ключ маски – это случайное 32-битное значение, которое варьируется от пакета к пакету. Тело сообщения проходит через XOR ^ с маской, а получатель восстанавливает его повторным XOR с ней (можно легко доказать, что (x ^ a) ^ a == x).
Маска служит двум целям:
Наложение маски требует дополнительных ресурсов, поэтому протокол WebSocket не требует ее.
Если по этому протоколу связываются два клиента (не обязательно браузеры), доверяющие друг другу и посредникам, то можно поставить бит Маска в 0, и тогда ключ маски не указывается.
Рассмотрим прототип чата на WebSocket и Node.JS.
HTML: посетитель отсылает сообщения из формы и принимает в div
Код на клиенте:
// создать подключение var socket = new WebSocket("ws://localhost:8081"); // отправить сообщение из формы publish document.forms.publish.onsubmit = function() { var outgoingMessage = this.message.value; socket.send(outgoingMessage); return false; }; // обработчик входящих сообщений socket.onmessage = function(event) { var incomingMessage = event.data; showMessage(incomingMessage); }; // показать сообщение в div#subscribe function showMessage(message) { var messageElem = document.createElement('div'); messageElem.appendChild(document.createTextNode(message)); document.getElementById('subscribe').appendChild(messageElem); }
Серверный код можно писать на любой платформе. В нашем случае это будет Node.JS, с использованием модуля ws:
var WebSocketServer = new require('ws'); // подключенные клиенты var clients = {}; // WebSocket-сервер на порту 8081 var webSocketServer = new WebSocketServer.Server({ port: 8081 }); webSocketServer.on('connection', function(ws) { var id = Math.random(); clients[id] = ws; console.log("новое соединение " + id); ws.on('message', function(message) { console.log('получено сообщение ' + message); for (var key in clients) { clients[key].send(message); } }); ws.on('close', function() { console.log('соединение закрыто ' + id); delete clients[id]; }); });
. Понадобится поставить два модуля: npm install node-static && npm install ws.
Socket - это действительно программный интерфейс. Это абстрактное понятие, которое, в большинстве случаев, используется для коммуникации программ в сети (но не только).
WebSocket - это протокол (какой-либо заранее оговоренный порядок) обмена данными (как, например, http, ftp, ssl и т.д.). Этот протокол идет поверх (передается посредством) протокола TCP.
Socket и WebSocket - это разные понятия в принципе. При работе по протоколу WebSocket вы будете использовать обычные сокеты для соединения. Так же как и при работе с другими протоколами будут использованы сокеты (и для работы с http, с ftp и др.).
Например, рассмотрим строку вида - ws://127.0.0.1:15000. В ней ws - это именно указание на то, что при обмене данными будет использован протокол WebSocket. 127.0.0.1 - ip адрес компьютера, 15000 - порт, на который производится подключение. Так вот 127.0.0.1:15000 - эта пара, если можно так выразится, и является сокетом.
Протокол WebSocket создавался для того, чтобы можно было поддерживать длительные неразрывные соединения между браузером (который является клиентом) и веб-сайтом (который является сервером).
Протокол WebSocket не похож на HTTP. Единственное, чем он напоминает HTTP - только одним самым первым запросом на подключение (так называемым рукопожатием/handshake). Это было сделано, потому что изначально протокол рассчитан на работу в браузере и необходимо было определение возможности поддержки его. После того, как соединение установлено, ничего похожего на протокол HTTP в протоколе WebSocket даже близко нет.
Сам протокол WebSocket не гарантирует никакой безопасности для передаваемых данных. Минимальное кодирование, которое он предусматривает - это банальная ксорка (XOR). При этом маска для ксорки передается вместе с сообщением. И предназначена эта ксорка для передачи данных через прокси сервера, которые ничего не знают о протоколе WebSocket. Это не защита ваших данных - это защита прокси сервера. И в обратную сторону (от сайта к браузеру) данные не кодируются ксоркой, ввиду отсутствия необходимости.
Именно отсутствие каких-либо наворотов в протоколе WebSocket и дает ему возможность быстрой работы.
Socket.IO — JavaScript-библиотека для веб-приложений и обмена данными в реальном времени. Состоит из двух частей: клиентской, которая запускается в браузере и серверной для node.js. Оба компонента имеют похожее API. Подобно node.js, Socket.IO событийно-ориентированная.
Socket.IO главным образом использует протокол WebSocket, но если нужно, использует другие технологии, например Flash Socket, AJAX Long Polling, AJAX Multipart Stream , предоставляя тот же самый интерфейс. Помимо того, что Socket.IO может быть использована как оболочка для WebSocket, она содержит много других функций, включая вещание на несколько сокетов, хранение данных, связанных с каждым клиентом, и асинхронный ввод/вывод.
Может быть установлена через npm (node package manager) .
С помощью Socket.IO можно реализовать аналитику в реальном времени, многопользовательские игры, обмен мгновенными сообщениями и совместную работу с документами в реальном времени.
Socket.IO довольно популярен, его используют Microsoft, Yammer, Zendesk, Trello и многие другие организации для создания систем реального времени.
Socket.IO работает на основе событий. Существуют несколько зарезервированных событий для объекта сокета на стороне клиента:
События для объекта сокета на стороне сервера:
События для объекта socket.io на стороне сервера:
Трансляция событий Laravel позволяет транслировать события Laravel на стороне сервера в клиентское приложение JavaScript, используя подход к WebSockets на основе драйверов. В настоящее время Laravel поставляется с Pusher Channels и Ably- драйверами. События могут быть легко обработаны на стороне клиента с помощью пакета Laravel Echo JavaScript. События транслируются по «каналам», которые могут быть общедоступными или частными. Любой посетитель вашего приложения может подписаться на общедоступный канал без какой-либо аутентификации или авторизации; однако, чтобы подписаться на частный канал, пользователь должен быть аутентифицирован и авторизован для прослушивания на этом канале. \Реализация WebSockets предоставляет три типа каналов: public — общедоступные — подписаться может каждый; private — частные — фронтенд должен аутентифицировать пользователя для бэкенда и убедиться, что у пользователя есть право на подписку на этот канал; presence — каналы присутствия — которые не позволяют отправку сообщений, а только уведомляют, присутствует ли пользователь на канале.
1) WebSockets для Laravel.
Laravel WebSockets - это пакет для Laravel 5.7 и выше, который мгновенно запустит ваше приложение с помощью WebSockets! Он имеет заменяемый Pusher API, панель отладки, статистику в реальном времени и даже позволяет создавать собственные контроллеры WebSocket.
После установки вы можете запустить его с помощью одной простой команды:
php artisan websockets:serve
2) Laravel Echo
С помощью инструмента Laravel Echo вы легко сможете использовать мощь WebSockets в своих Laravel-приложениях. Он упрощает самые необходимые и самые трудные аспекты построения сложных взаимодействий WebSockets.
Echo состоит из двух частей: набора улучшений для системы вещания сообщений Laravel (Event broadcasting system), и нового пакета JavaScript.
Бэкендовые компоненты Echo уже встроены в ядро Laravel, начиная с версии 5.3, их не надо импортировать (в этом их отличие от таких компонентов, как Cashier). Вы можете использовать эти бэкендовые улучшения с любым JavaScript-фронтендом, а не только с JavaScript-библиотекой Echo, и при этом все равно получите значительные упрощение работы с WebSockets. Но с JavaScript-библиотекой Echo они работают еще лучше.
JavaScript-библиотеку Echo можно импортировать через NPM, а затем импортировать в JavaScript вашего приложения. Это слой «сахара» поверх Pusher JS (JavaScript SDK для Pusher), либо поверх Socket.io (многие используют этот JavaScript SDK поверх архитектуры Redis WebSockets).
3) По умолчанию Laravel включает в себя два драйвера вещания на стороне сервера, из которых вы можете выбирать: Pusher Channels и Ably . Однако пакеты, управляемые сообществом, такие как laravel-websockets, предоставляют дополнительные драйверы вещания, для которых не требуются коммерческие поставщики вещания. Вам также потребуется настроить и запустить обработчик очереди . Все трансляции событий выполняются с помощью заданий в очереди, поэтому время отклика вашего приложения не сильно зависит от широковещательных событий.
Поток вещания Laravel с использованием Socket.io и Redis PubSub
WebSocket – современное средство коммуникации. Кросс-доменное, универсальное, безопасное.
На текущий момент он работает в браузерах IE10+, FF11+, Chrome 16+, Safari 6+, Opera 12.5+. В более старых версиях FF, Chrome, Safari, Opera есть поддержка черновых редакций протокола.
Там, где вебсокеты не работают – обычно используют другие транспорты, например IFRAME. Вы найдете их в других статьях этого раздела.
Есть и готовые библиотеки, реализующие функциональность COMET с использованием сразу нескольких транспортов, из которых вебсокет имеет приоритет. Как правило, библиотеки состоят из двух частей: клиентской и серверной.
Например, для Node.JS одной из самых известных библиотек является Socket.IO.
К недостаткам библиотек следует отнести то, что некоторые продвинутые возможности WebSocket, такие как двухсторонний обмен бинарными данными, в них недоступны. С другой – в большинстве случаев стандартного текстового обмена вполне достаточно.
Исследование, описанное в статье про websocket, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое websocket, вебсокет и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Компьютерные сети
Комментарии
Оставить комментарий
Компьютерные сети
Термины: Компьютерные сети