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

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

Люди начали создавать пользовательские скрипты, которые можно было использовать для динамического построения веб-сайтов; на стороне сервера скрипты получали ввод от пользователей и могли соответственно выдавать динамический ответ. Благодаря этой технологии стали появляться форумы и доски объявлений, где пользователи могли размещать свой контент, чтобы другие могли его прочитать. В какой-то момент даже такая интерактивность оказалась слишком ограниченной.

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

Прежде чем мы погрузимся в технические детали WebSockets, давайте вкратце расскажем о внутреннем устройстве классического протокола HTTP. Протокол HTTP — это традиционный подход к взаимодействию браузеров и веб-серверов. Огромным преимуществом протокола HTTP является его безэталонный дизайн. Это означает, что серверы не обязаны хранить какую-либо информацию об активных клиентах, за исключением того момента, когда запрос обслуживается. Традиционный способ использования HTTP-соединения заключается в простой доставке запрошенных данных и закрытии соединения сразу после того, как запрос был обработан. Такая конструкция была очень эффективной на заре интернета, когда простые статические веб-сайты доставлялись большому количеству пользователей.

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

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

Одним из первых подходов был опрос. Опрос работает путем постоянного инициирования новых запросов внутри бесконечного цикла на стороне клиента. Таким образом, сервер всегда получает возможность уведомить клиентов о последних событиях. Недостатком этого метода является высокая нагрузка на серверы, особенно когда сайт одновременно открывают много пользователей. Для борьбы с этой неэффективностью разработчики придумали идею длинного опроса. С помощью длинного опроса серверы пытаются замедлить работу клиентов, блокируя соединение всякий раз, когда у них нет данных для отправки. Благодаря тому, что соединение с клиентом простаивает, клиенты не будут перегружать сервер запросами с неоправданно высокой скоростью. Как только сервер получает новые данные для отправки клиенту, простаивающее соединение используется и немедленно закрывается. Затем клиент следует первоначальной стратегии опроса, немедленно открывая новое соединение, которое сервер снова держит бездействующим, пока не появятся новые данные для отправки клиенту. Эта стратегия позволила реализовать интерактивные функции, такие как чаты, живая лента новостей и т.д. через HTTP, с умеренно высокой нагрузкой на серверы. Facebook был одним из первых сайтов, который использовал эту стратегию для обновления ленты новостей без необходимости перезагрузки страниц. К счастью, в современных браузерах есть еще более совершенные методы, так что push-сообщения и интерактивность теперь поддерживаются по умолчанию.

WebSockets можно рассматривать как расширение классического протокола HTTP. Вместо отправки обычного HTTP-запроса (т.е. GET, POST, PATCH) клиенты отправляют специальный запрос CONNECT, который указывает, что должно быть инициировано WebSocket-соединение. Когда веб-сервер поддерживает WebSockets, этот запрос приводит к смене протокола: и сервер, и клиент отклоняются от стандартного поведения HTTP и вместо этого переключаются на новый протокол, используя при этом основной канал предыдущего соединения.

WebSockets — это двунаправленный полнодуплексный протокол для связи между клиентом и сервером через Интернет. Этот протокол позволяет использовать приложения реального времени, такие как чаты, уведомления, прямые трансляции, многопользовательские игры и другие функции.

Давайте рассмотрим использование WebSockets на практике:

//Normal fetch
fetch("http://localhost:3000")
.then(resp => resp.json())
.then(data => console.log(data))

//WebSocket
//create a WebSocket
const socket = new WebSocket("ws://localhost:7000/ws")
//Callback that should run once the connection has been established
socket.onopen = () => {
console.log("Connection is Open")
}
socket.onmessage = (event) => {
const obj = JSON.parse(event.data)
}
socket.onclose = () => {
console.log("Connection is Closed")
}
Вход в полноэкранный режим Выход из полноэкранного режима

Существуют библиотеки на разных языках для обработки входящих WebSockets на стороне сервера. Одним из самых простых способов работы с WebSockets на сервере, вероятно, является NodeJS. Преимущество NodeJs в том, что и клиентский, и серверный код может быть написан полностью на JavaScript. Это решает многие проблемы совместимости и позволяет разработчикам использовать единую перспективу при рассуждениях о потоке данных в своем коде. Еще одним преимуществом является наличие асинхронных функций и событийно-ориентированный дизайн Node.js, который отлично подходит для работы с обратными вызовами и настройки слушателей событий. Такой дизайн позволяет избежать дополнительной сложности многопоточного кода, который можно встретить в других языках. Проще говоря, разработчики на Node.js могут писать свой бэкенд-код на сервере практически так же, как и соответствующий JavaScript-код в браузере. Существует множество библиотек, которые можно легко импортировать в менеджер пакетов npm, чтобы запустить WebSocket-приложение за короткое время.

Сервер WebSockets

//The http raw-server using the http library
const http = require("http");

//create the websocket server - step1 - Get the class to handle events - to initiate the exchange protocol
const WebSocketServer = require("websocket").server

//create the websocket server - step3 - Global var to overwrite
let connection = null

//Persisted web-server injected into another library to allow protocol change
const httpServer = http.createServer((req, res) => {
*    console.log("We have received a request")
})

//create the websocket server - step2 - Takes a json, which is http server and you pass it httpServer - to initiate the exchange protocol
const webSocket = new WebSocketServer({
    "httpServer": httpServer
})

//create the websocket - step3 - The event, when request is sent call this function, we get a connection
webSocket.on("request", request => {
*    connection = request.accept(null, request.origin)
    connection.on("onopen", () => console.log("Open!!!"))
    connection.on("onclose", () => console.log("Closed!!!"))
    connection.on("onmessage", message => {
*        console.log(`Received message ${message}`)
    })
})

httpServer.listen(8080, () => console.log("My server is listening on port 8080"))
Вход в полноэкранный режим Выход из полноэкранного режима

Приведенный выше код показывает, как настроить сервер WebSocket для обработки входящих соединений от клиентов… (попробуйте это с помощью отладчика и консоли браузера, (*)где добавить точки останова)

Сервер — клиенту

//Console
ws = new WebSocket("ws://localhost:8080")
ws.onmessage = message => console.log(`We received a message from server ${message.data}`)
//Debugger
connection.send("Hello client! From the server")
Вход в полноэкранный режим Выход из полноэкранного режима

Клиент — серверу

//Console
ws = new WebSocket("ws://localhost:8080")
ws.onmessage = message => console.log(`We received a message from server ${message.data}`)
ws.send("Hello server, It's the client")
Войти в полноэкранный режим Выход из полноэкранного режима

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

Оцените статью
Procodings.ru
Добавить комментарий