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

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

Клиент и Сервер

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

Как это работает?

Давайте проделаем следующие простые шаги: -

  1. Клиент отправляет запрос на сервер.
  2. Установлено соединение.
  3. Сервер отправляет ответ.
  4. Клиент получает ответ.
  5. Связь закрыта.

Это простой обзор того, как работает стандартная связь между клиентом и сервером. Теперь внимательно рассмотрите шаг №. 5.

Соединение закрыто.

HTTP-запрос выполнил свою задачу и больше не нужен, поэтому соединение закрыто.

Что делать, если сервер хочет отправить сообщение клиенту

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

Как клиент узнает, что сервер хочет отправить сообщение?

Рассмотрим следующий пример:
Клиент голодает и заказал еду через Интернет. Он делает один запрос в секунду, чтобы проверить, готов ли заказ.

0 сек: еда готова? (Клиент)
0 сек: Нет, подожди. (Сервер)
1 сек: Еда готова? (Клиент)
1 сек: Нет, подожди. (Сервер)
2 секунды: Еда готова? (Клиент)
2 сек: Нет, подожди. (Сервер)
3 секунды: Еда готова? (Клиент)
3 sec: Да, сэр, вот ваш заказ. (Сервер)

Это то, что вы называете HTTP-опросом. Клиент делает повторные запросы к серверу и проверяет, есть ли какие-либо сообщения для получения.

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

Есть ли способ решить эту проблему

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

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

Рассмотрим версию приведенного выше примера с длительным опросом: -

0 сек: еда готова? (Клиент)
3 сек: Да, сэр, вот ваш заказ. (Сервер)

Ура, проблема решена.
Не совсем так. Хотя длинный опрос работает, это очень дорого с точки зрения ЦП, памяти и полосы пропускания (поскольку мы блокируем ресурсы, удерживая соединение открытым).

Что же нам теперь делать? Похоже, дела выходят из-под контроля. Вернемся к нашему спасителю: WebSocket.

Почему WebSockets

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

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

Вы можете просто прослушать сервер, и он отправит вам сообщение, когда станет доступен.

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

Потребление ресурсов

На приведенной ниже диаграмме показаны различия в потреблении полосы пропускания между WebSockets и Long Polling в относительно распространенном случае использования:

Разница огромна и растет экспоненциально с увеличением количества запросов.

Скорость

Вот результаты для 1, 10 и 50 запросов, обслуживаемых на одно соединение за одну секунду:

Как видите, при использовании Socket.io выполнение одного запроса на соединение примерно на 50% медленнее, так как соединение должно быть установлено в первую очередь. Эти накладные расходы меньше, но все же заметны для десяти запросов. При 50 запросах от одного и того же соединения Socket.io уже на 50% быстрее. Чтобы получить лучшее представление о пиковой пропускной способности, мы посмотрим на тест с более обширным числом (500, 1000 и 2000) запросов на соединение:

Здесь вы можете видеть, что пики теста HTTP составляют около 950 запросов в секунду, в то время как Socket.io обслуживает около 3900 запросов в секунду. Эффективно, правда?

Примечание: Socket.io - это библиотека JavaScript для веб-приложений реального времени. Он внутренне реализует WebSocket. Считайте это оболочкой для WebSocket, которая предоставляет гораздо больше возможностей (в следующем сообщении в блоге этой серии подробно рассматривается Socket.io) .

Как работают WebSockets

Это шаги, необходимые для установления соединения WebSocket.

  1. Клиент (браузер) отправляет серверу HTTP-запрос.
  2. Соединение устанавливается по протоколу HTTP.
  3. Если сервер поддерживает протокол WebSocket, он соглашается обновить соединение. Это называется рукопожатием.
  4. Теперь, когда рукопожатие завершено, первоначальное HTTP-соединение заменяется соединением WebSocket, которое использует тот же базовый протокол TCP / IP.
  5. На этом этапе данные могут свободно перемещаться между клиентом и сервером.

Мы собираемся создать два файла: один сервер и один клиент.
Сначала создадим простой <html> документ с именем client.html, содержащий тег <script>. Посмотрим, как это выглядит: -

Client.html

<html>

<script>
    // Our code goes here
</script>

<body>
    <h1>This is a client page</h1>
</body>

</html>

Server.js

Теперь создайте еще один файл server.js. Импортируйте модуль HTTP и создайте сервер. Заставьте его слушать port 8000.

Это будет работать как простой http сервер, слушающий port 8000. Посмотрим и на это:

//importing http module
const http = require('http');

//creating a http server
const server = http.createServer((req, res) => {
    res.end("I am connected");
});

//making it listen to port 8000
server.listen(8000);

Запустите команду node server.js, чтобы начать прослушивание port 8000. Вы можете выбрать любой порт, какой захотите, я просто выбрал 8000 без особой на то причины.

Наша основная настройка клиента и сервера завершена. Это было просто, правда? А теперь перейдем к хорошему.

Настройка клиента

Чтобы создать WebSocket, используйте конструктор WebSocket(), который возвращает объект websocket. Этот объект предоставляет API для создания и управления подключением WebSocket к серверу.

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

Посмотрим, как:

<html>

<script>
    //calling the constructor which gives us the websocket object: ws
    let ws = new WebSocket('url'); 
</script>

<body>
    <h1>This is a client page</h1>
</body>

</html>

Конструктор WebSocket ожидает, что URL-адрес будет прослушиваться. В нашем случае это 'ws://localhost:8000', потому что здесь работает наш сервер.
Теперь это может немного отличаться от того, к чему вы привыкли. Мы не используем протокол HTTP, мы используем протокол WebSocket. Это сообщит клиенту, что «Эй, мы используем протокол веб-сокета», следовательно, 'ws://' вместо 'http://'. Достаточно просто? Теперь давайте фактически создадим сервер WebSocket в server.js.

Настройка сервера

Нам понадобится сторонний модуль ws на нашем сервере узлов, чтобы использовать и настроить сервер WebSocket.

Сначала мы импортируем модуль ws. Затем мы создадим сервер websocket и передадим ему HTTP сервер, слушающий port 8000.

HTTP-сервер слушает порт 8000, а WebSocket-сервер слушает этот HTTP-сервер. По сути, он слушает слушателя.

Теперь наш веб-сокет отслеживает трафик на port 8000. Это означает, что он попытается установить соединение, как только клиент станет доступен. Наш server.js файл будет выглядеть так:

const http = require('http');
//importing ws module
const websocket = require('ws');

const server = http.createServer((req, res) => {
    res.end("I am connected");
});
//creating websocket server
const wss = new websocket.Server({ server });

server.listen(8000);

Как мы уже обсуждали ранее, конструктор WebSocket() возвращает объект websocket, предоставляющий API для создания и управления подключением WebSocket к серверу.

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

Посмотрим, как слушать сообщения:

const http = require('http');
const websocket = require('ws');

const server = http.createServer((req, res) => {
    res.end("I am connected");
});
const wss = new websocket.Server({ server });
//calling a method 'on' which is available on websocket object
wss.on('headers', (headers, req) => {
    //logging the header
    console.log(headers);
});

server.listen(8000);

Метод 'on' ожидает два аргумента: имя события и обратный вызов. Имя события - это то, что мы хотим прослушать / испустить, а обратный вызов указывает, что с ним делать. Здесь мы просто регистрируем событие headers. Посмотрим, что у нас получится:

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

  • Первое, что вы заметите, это то, что мы получили код состояния 101. Возможно, вы видели код состояния 200, 201, 404, но он выглядит иначе. 101 - это фактически код состояния протоколов коммутации. Появляется сообщение "Эй, я хочу перейти на новую версию".
  • Во второй строке отображается информация об обновлении. Он указывает, что хочет перейти на протокол websocket.
  • Это действительно то, что происходит во время рукопожатия. Браузер использует HTTP соединение, чтобы установить соединение с использованием HTTP/1.1 протокола, а затем Upgrade это websocket протокол.

Теперь в этом будет больше смысла.

Событие Headers генерируется до того, как заголовки ответа записываются в сокет как часть рукопожатия. Это позволяет вам проверять / изменять заголовки перед их отправкой. Это означает, что вы можете изменить заголовки, чтобы принять, отклонить или что-то еще по своему усмотрению. По умолчанию он принимает запрос.

Точно так же мы можем добавить еще одно событие connection, которое генерируется после завершения рукопожатия. Мы отправим клиенту сообщение об успешном установлении соединения. Посмотрим, как:

const http = require('http');
const websocket = require('ws');

const server = http.createServer((req, res) => {
    res.end("I am connected");
});
const wss = new websocket.Server({ server });

wss.on('headers', (headers, req) => {
    //console.log(headers); Not logging the header anymore
});

//Event: 'connection'
wss.on('connection', (ws, req) => {
    ws.send('This is a message from server, connection is established');
    //receive the message from client on Event: 'message'
    ws.on('message', (msg) => {
        console.log(msg);
    });
});

server.listen(8000);

Мы также слушаем событие message, исходящее от клиента. Давайте создадим это: -

<html>

<script>
    let ws = new WebSocket('url'); 
    //logging the websocket property properties
    console.log(ws);
    //sending a message when connection opens
    ws.onopen = (event) => ws.send("This is a message from client");
    //receiving the message from server
    ws.onmessage = (message) => console.log(message);
</script>

<body>
    <h1>This is a client page</h1>
</body>

</html>

Вот как это выглядит в браузере: -

Первый журнал - это WebSocket список всех свойств объекта websocket, а второй журнал - это MessageEvent, который имеет свойство data. Если вы присмотритесь, вы увидите, что мы получили сообщение с сервера.

Журнал сервера будет выглядеть примерно так:

Мы правильно получили сообщение клиента. Это означает, что наше соединение было успешно установлено. Ваше здоровье!

Заключение

Подводя итог, давайте рассмотрим то, что мы узнали:

  • Мы рассмотрели, как работает HTTP-сервер, что такое опрос, длинный опрос.
  • Что такое WebSockets и зачем они нам нужны.
  • Мы рассмотрели, как они работают за сценой, и получили лучшее представление об используемых заголовках.
  • Мы создали собственный клиент и сервер и успешно установили соединение между ними.

Это основы WebSockets и принципы их работы. В следующем посте этой серии socket.io и его использование будет рассмотрено более подробно. Мы также увидим, зачем нам именно socket.io, когда все работает нормально с единственным родным WebSocket(). Зачем использовать сильно раздутую библиотеку, если мы можем успешно отправлять и получать сообщения?

Поделитесь этим постом, если он окажется для вас полезным, и ждите следующего.
Шад.

Ссылка

Первоначально опубликовано на https://iamshadmirza.hashnode.dev.