WebSocket — это сетевой коммуникационный протокол, необходимый для многих дополнительных функций.

В этой статье описывается, как использовать протокол WebSocket.

Зачем мне WebSocket?

Люди, которые плохо знакомы с WebSocket, задают один и тот же вопрос: зачем нам нужен еще один протокол, когда у нас уже есть протокол HTTP? Какую пользу это приносит?

Ответ прост, потому что протокол HTTP имеет изъян: связь может инициировать только клиент.

Например, если мы хотим узнать сегодняшнюю погоду, клиент может только отправить запрос на сервер, и сервер вернет результат запроса; Протокол HTTP не может активно передавать информацию клиенту.

Эта функция одностороннего запроса предопределяет, что если сервер имеет непрерывные изменения состояния, для клиента очень проблематично быть информированным. Мы можем использовать только «опрос»: время от времени мы отправляем запрос, чтобы узнать, есть ли на сервере какая-либо новая информация. Самый типичный сценарий — чат.

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

Введение

Протокол WebSocket был создан в 2008 году и стал международным стандартом в 2011 году. Он уже поддерживается всеми браузерами.

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

Другие функции включают в себя.

  1. Построенный на протоколе TCP, реализация на стороне сервера относительно проста.
  2. Хорошая совместимость с протоколом HTTP. Порты по умолчанию также 80 и 443, а на этапе рукопожатия используется протокол HTTP, поэтому его непросто заблокировать во время рукопожатия, и он может проходить через различные прокси-серверы HTTP.
  3. Формат данных относительно легкий, с низким уровнем производительности и эффективной связью.
  4. Текстовые и двоичные данные могут быть отправлены.
  5. Никаких ограничений по омологации, клиент может общаться с любым сервером.
  6. Идентификатор протокола — ws (или wss, если он зашифрован), а URL-адрес сервера — это URL-адрес.
ws://example.com:80/some/path

Простой пример клиента

Использование WebSocket довольно просто.

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

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};

Клиентский API

API клиента WebSocket выглядит следующим образом.

1. Конструкторы веб-сокетов

Объект WebSocket используется в качестве конструктора для создания нового экземпляра WebSocket.

var ws = new WebSocket('ws://localhost:8080');

После выполнения вышеуказанного оператора клиент подключится к серверу.

Список всех свойств и методов экземпляра объекта смотрите здесь.

2. веб-сокет.readyState

Свойство readyState возвращает текущее состояние экземпляра объекта, которых всего четыре.

  • ПОДКЛЮЧЕНИЕ: Значение 0 указывает на то, что соединение находится в процессе.
  • OPEN: значение 1 указывает, что соединение установлено успешно и связь возможна.
  • ЗАКРЫТИЕ: Значение 2 указывает, что соединение закрывается.
  • ЗАКРЫТО: Значение 3 указывает, что соединение закрыто или открытое соединение не удалось.

Вот пример.

switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}

3. веб-сокет.onopen

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

ws.onopen = function () {
  ws.send('Hello Server!');
}

Если вы хотите указать несколько функций обратного вызова, вы можете использовать метод addEventListener.

ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});

4. веб-сокет.onclose

Свойство onclose объекта экземпляра, используемое для указания функции обратного вызова после закрытия соединения.

ws.onmessage = function(event) {
  var data = event.data;
};

ws.addEventListener("message", function(event) {
  var data = event.data;
});

Обратите внимание, что данные сервера могут быть текстовыми или двоичными данными (объекты BLOB-объектов или объекты Arraybuffer).

ws.onmessage = function(event){
  if(typeof event.data === String) {
    console.log("Received data string");
  }

  if(event.data instanceof ArrayBuffer){
    var buffer = event.data;
    console.log("Received arraybuffer");
  }
}

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

// The data received is the blob data
ws.binaryType = "blob";
ws.onmessage = function(e) {
  console.log(e.data.size);
};

// The data received is the ArrayBuffer data
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
  console.log(e.data.byteLength);
};

6. веб-сокет.отправить()

Метод send() объекта экземпляра используется для отправки данных на сервер.

Пример отправки текста.

ws.send('your message');

Пример отправки объекта Blob.

var file = document
  .querySelector('input[type="file"]')
  .files[0];
ws.send(file);

Пример отправки объекта ArrayBuffer.

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);

7. webSocket.bufferedAmount

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

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // Sending completed
} else {
  // Sending is not over yet
}

8. webSocket.onerror

Свойство onerror объекта экземпляра, используемое для указания функции обратного вызова при сообщении об ошибке.

socket.onerror = function(event) {
  // handle error event
};

socket.addEventListener("error", function(event) {
  // handle error event
});

Серверная реализация

Реализации сервера WebSocket см. в списке Википедии.

Обычно используются следующие три реализации Node.

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

WebSocketd

Здесь я хотел бы порекомендовать очень специальный сервер WebSocket: Websocketd.

Наиболее важной особенностью является то, что внутренний скрипт не зависит от языка, стандартный ввод (stdin) — это ввод WebSocket, а стандартный вывод (stdout) — вывод WebSocket.

В качестве примера приведем Bash-скрипт counter.sh.

#!/bin/bash

echo 1
sleep 1

echo 2
sleep 1

echo 3

Запуск этого скрипта из командной строки выведет 1, 2 и 3 с интервалом в 1 секунду между каждым значением.

$ bash ./counter.sh
1
2
3

Теперь запустите websocketd и укажите этот скрипт как сервис.

$ websocketd --port=8080 bash ./counter.sh

Приведенная выше команда запускает сервер WebSocket на порту 8080. всякий раз, когда клиент подключается к этому серверу, выполняется сценарий counter.sh, и его выходные данные передаются клиенту.

var ws = new WebSocket('ws://localhost:8080/');

ws.onmessage = function(event) {
  console.log(event.data);
};

Выше приведен клиентский код JavaScript, который выводит 1, 2 и 3 по порядку в консоли после запуска.

При этом легко отправить вывод из командной строки в браузер.

$ websocketd --port=8080 ls

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

Больше использования можно найти в официальном примере.

websocketd по сути является прокси-сервером WebSocket для командной строки. Любая программа, которую можно запустить из командной строки, может общаться с браузером через WebSocket через нее. Вот реализация эхо-сервиса greeter.js на узле.

process.stdin.setEncoding('utf8');

process.stdin.on('readable', function() {
  var chunk = process.stdin.read();
  if (chunk !== null) {
    process.stdout.write('data: ' + chunk);
  }
});

Команда для запуска этого скрипта выглядит следующим образом.

$ websocketd --port=8080 node ./greeter.js

В официальном репозитории также есть примеры на разных других языках.

Справочные ссылки

(конец)