Введение в веб-сокеты

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

Краткая история веб-приложений реального времени

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

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

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

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

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

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

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

Вот упрощённый пример первоначальных заголовков запроса.

GET ws://websocket.example.com/ HTTP/1.1

Origin: http://example.com

Connection: Upgrade

Host: websocket.example.com

Upgrade: websocket

Замечание: URL-адреса веб-сокетов используют протокол ws. Также существует протокол wss для безопасных соединений, который является эквивалентом HTTPS.

Если сервер поддерживает протокол WebSocket, он сообщает об этом с помощью заголовка Upgrade в ответе.

HTTP/1.1 101 WebSocket Protocol Handshake

Date: Wed, 16 Oct 2013 10:07:34 GMT

Connection: Upgrade

Upgrade: WebSocket

После того, как рукопожатие выполнено, первоначальное соединение HTTP заменяется соединением по веб-сокету, которое использует то же соединение TCP/IP. На этом этапе любая из сторон может начать отправку данных.

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

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

Замечание: Стоит отметить, что клиент будет уведомлен о новом сообщении только, когда сервер передаст все его фрагменты.

Создаём демо-пример

Создание приложения на основе веб-сокетов

Мы создадим простое приложение, которое соединяется с сервером по веб-сокету. Перед тем, как мы углубимся в детали API, нужно создать несколько файлов.

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

Загрузить код

Посмотреть на CodePen

Создайте файл index.html и добавьте в него следующую разметку.

<!DOCTYPE html>
<html lang="en">
<head>
<metacharset="utf-8">
<title>WebSockets Demo</title>

<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="page-wrapper">
<h1>WebSockets Demo</h1>

<div id="status">Connecting...</div>

<ul id="messages"></ul>

<form id="message-form" action="#" method="post">
<textarea id="message" placeholder="Write your message here..." required></textarea>
<button type="submit">Send Message</button>
<button type="button" id="close">Close Connection</button>
</form>
</div>

<script src="app.js"></script>
</body>
</html>

Элемент <div> предназначен для вывода сообщений о статусе соединения. Список будет использоваться для отображения сообщений, отправленных и полученных от сервера. Веб-форма предназначена для ввода сообщений.

Файл style.css, на который ссылается этот код, находится в архиве для загрузки. Далее создадим файл app.js и добавим в него следующий код.

window.onload = function() {

  // Получаем ссылки на элементы страницы.
var form = document.getElementById('message-form');
var messageField = document.getElementById('message');
var messagesList = document.getElementById('messages');
varsocketStatus = document.getElementById('status');
varcloseBtn = document.getElementById('close');

  // Остальной код из этой статьи будет добавляться ниже...
};

Мы создали несколько переменных и инициализировали их ссылками на ключевые элементы страницы.

Открытие соединений

Теперь, когда готов костяк приложения, можно начать изучать WebSocket API. Для начала узнаем, как создать новое соединение WebSocket. Для этого нужно вызвать конструктор класса WebSocket и передать ему URL сервера.

Скопируйте следующий код в файл app.js, чтобы создать новое соединение.

// Создаём новый объект WebSocket.
varsocket = new WebSocket('ws://echo.websocket.org');

После того, как соединение установлено, возникнет событие open объекта WebSocket. Добавим обработчик события, который обновит статус элемента <div> сообщением о том, что соединение установлено.

Добавьте следующий код в файл app.js.

// Показываем сообщение «connected» при открытии веб-сокета.
socket.onopen = function(event) {
socketStatus.innerHTML = 'Connected to: ' + event.currentTarget.url;
socketStatus.className = 'open';
};

Также мы добавляем класс open элементу <div>.

Обработка ошибок

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

// Обработка возникающих ошибок.
socket.onerror = function(error) {
  console.log('WebSocket Error: ' + error);
};

Отправка сообщений

Чтобы отправить сообщение по веб-сокет, нужно вызвать метод send() объекта WebSocket, передав ему данные для отправки.

socket.send(data);

Можно отправлять как текст, так и двоичные данные. В нашем приложении нужно передавать содержимое текстового поля на сервер при отправке формы. Чтобы сделать это, надо определить обработчик события отправки формы.

Добавьте следующий код в файл app.js.

// Отправка сообщения при отправке формы
form.onsubmit = function(e) {
e.preventDefault();

  // Получение сообщения из текстового поля.
var message = messageField.value;

  // Отправка сообщения по веб-сокету.
  socket.send(message);

  // Добавление сообщения в список сообщений.
messagesList.innerHTML += '<liclass="sent"><span>Sent:</span>' + message +
                            '</li>';

  // Очистка текстового поля.
messageField.value = '';

  return false;
};

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

Получение сообщений

При получении сообщения вызывается событие message. Оно включает в себя свойство data, которое можно использовать для доступа к содержимому сообщения.

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

Чтобы добиться этого, скопируйте следующий код в файл app.js.

// Обработка сообщений, отправленных сервером.
socket.onmessage = function(event) {
var message = event.data;
messagesList.innerHTML += '<liclass="received"><span>Received:</span>' +
                             message + '</li>';
};

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

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

socket.close();

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

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

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

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

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

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

  return false;
};

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

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

Мониторинг трафика веб-сокета с помощью инструментов для разработчиков в Chrome

«Инструменты разработчика», доступные в браузере Google Chrome включают в себя средства для мониторинга трафика. Чтобы использовать этот инструмент:

  • Откройте «Инструменты разработчика».
  • Перейдите на вкладку Network.
  • Кликните по записи, соответствующей вашему соединению по веб-сокету.
  • Перейдите на вкладку Frames.

Эти инструменты предоставляют общую информацию о данных, переданных через соединение.

WebSocket на сервере

В этой статье мы сфокусировали внимание на том, как использовать веб-сокеты на стороне клиента. Если вы хотите создать собственный сервер WebSocket, существует множество библиотек, которые могут в этом помочь. Одна из наиболее популярных – socket.io, библиотека Node.JS.

Другие библиотеки:

  • C++: libwebsockets;
  • Erlang: Shirasu.ws;
  • Java: Jetty;
  • Node.JS: ws;
  • Ruby: em-websocket;
  • Python: Tornado, pywebsocket;
  • PHP: Ratchet, phpws.

Поддержка браузерами

Веб-сокеты поддерживаются практически во всех современных браузерах. Единственными исключениями являются Android- браузеры и Opera Mini.

Заключительные мысли

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

WebSocket – это большой шаг в эволюции интернета. Возможность создания соединения с низкими затратами ресурсов открывает путь к новому поколению веб-приложений.

Перевод статьи “An Introduction to WebSockets” был подготовлен дружной командой проекта Сайтостроение от А до Я.