От автора: WebSockets и Socket.IO, вероятно, являются двумя из самых популярных решений для реализации связи в реальном времени в современной сети. Но чем они отличаются?
При создании приложения реального времени наступает момент, когда вам нужно выбрать, как реализовать обмен данными в реальном времени между клиентом и сервером. WebSockets и Socket.IO, вероятно, являются двумя из самых популярных решений для реализации связи в реальном времени в современной сети. Но какой выбрать? В чем разница между этими двумя технологиями? Давайте выясним!
WebSockets
Говоря о WebSockets, мы имеем в виду протокол веб-связи, который обеспечивает полнодуплексный канал связи через одно TCP-соединение. Короче говоря, он позволяет взаимодействовать между клиентом и сервером с минимальными расходами, позволяя нам создавать приложения, использующие преимущества связи в реальном времени.
Например, представьте, что вы создаете приложение для чата: вам нужно как можно скорее получать и отправлять данные, не так ли? Что ж, это работа как раз для WebSockets! Вы можете открыть одно TCP-соединение и обмениваться данными, оставив его открытым столько, сколько вам нужно.
WebSockets впервые появился в 2010 году в Google Chrome 4, а первый RFC (RFC 6455) был опубликован годом позже, в 2011 году. Отличными вариантами использования WebSockets являются:
Приложения для чата
Многопользовательские игры
Совместное редактирование
Социальные ленты
Приложения на основе местоположения
и многое другое.
Socket.IO
Socket.IO — это библиотека JavaScript, построенная на основе WebSocket… и других технологий. Фактически, она использует WebSockets, когда они доступны, но готова использовать и другие технологии, такие как Flash Socket, AJAX Long Polling, AJAX Multipart Stream и т.д.; что позволяет использовать Socket.IO в контекстах, где не поддерживаются WebSockets. В блоге Ably есть замечательная статья, в которой подробно описаны возможности Socket.IO.
Различия между WebSocket и Socket.IO
Основные преимущества Socket.IO перед WebSockets:
В отличие от WebSocket, Socket.IO позволяет транслировать сообщение всем подключенным клиентам. Например, если вы пишете приложение для чата и хотите уведомить всех подключенных клиентов о том, что к чату присоединился новый пользователь, вы можете легко передать это сообщение всем за один раз. Используя простой WebSocket, вам понадобится список всех подключенных клиентов, а затем отправлять сообщение напрямую по одному.
Прокси-серверы и балансировщики нагрузки затрудняют реализацию и масштабирование WebSockets. Socket.IO поддерживает эти технологии из коробки.
Как было сказано ранее, Socket.IO может использовать технологии, отличные от WebSockets, если клиент не поддерживает WebSockets.
Если (по какой-то причине) соединение WebSocket разрывается, оно не будет автоматически переподключаться… а Socket.IO сделает это за вас!
API Socket.IO созданы, чтобы с ними было проще работать.
Получается, что Socket.IO — это что-то вроде «рая для общения в реальном времени», не так ли? Что ж, на самом деле есть несколько веских причин для использования веб-сокетов.
Во-первых, в наши дни каждый современный браузер поддерживает WebSockets. Socket.IO использует гораздо больше шаблонного кода и ресурсов, чтобы использовать другие технологии. В большинстве случаев такой уровень поддержки вам не нужен. Даже с точки зрения сетевого трафика Socket.IO намного дороже. Фактически, с обычными WebSockets браузеру может потребоваться выполнить всего два запроса:
GET запрос страницы HTML
UPGRADE подключение к WebSocket
Вот и все. Вы готовы начать общение со своим сервером в реальном времени! А как насчет Socket.IO?
GET запрос страницы HTML
Клиентская библиотека Socket.IO (207кб)
Ajax-запрос с тремя длинными запросами
UPGRADE подключение к WebSocket
В мире, где мы используем много кода JavaScript, а библиотеки резко уменьшают свой вес… 207 КБ — это много! А как насчет всех этих запросов? Какая трата сетевого трафика!
Есть даже npm пакет под названием websocket-vs-socket.io(см. здесь), который был создан для сравнения сетевого трафика этих двух технологий:
Сетевой трафик WebSocket:
Сетевой трафик Socket.IO:
Какая огромная разница!
Написание кода
До сих пор мы видели некоторые различия на бумаге, но чем они отличаются при написании приложения реального времени?
Реализуем WebSocket сервер в Node.js. Мы создаем сервер WebSocket, доступный через порт 3001. Каждый раз, когда клиент подключается, мы назначаем его сеансу уникальный идентификатор. Когда клиент отправляет сообщение, мы создадим ответ в следующем формате: [<client-id>]: <message> чтобы он знал, что сообщение было успешно отправлено.
1 2 3 4 5 6 7 8 9 10 11 12 |
const WebSocket = require("ws"); const UUID = require("uuid"); const wss = new WebSocket.Server({ port: 3000 }); wss.on("connection", ws => { ws.id = UUID(); ws.on("message", message => { ws.send(`[${ws.id}]: ${message}`); }); }); |
Превосходно! Но что, если я хочу передать это сообщение каждому подключенному клиенту? WebSockets по умолчанию не поддерживает рассылку сообщений! Верно, но рассылку сообщений по-прежнему легко реализовать с помощью обычных WebSockets:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const WebSocket = require("ws"); const UUID = require("uuid"); const wss = new WebSocket.Server({ port: 3000 }); function broadcast(clientId, message) { wss.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(`[${clientId}]: ${message}`); } }); } wss.on("connection", ws => { ws.id = UUID(); ws.on("message", message => broadcast(ws.id, message)); }); |
Так просто! Как видите, WebSocket.Server отслеживает каждого подключенного клиента, поэтому мы можем перебрать их и отправить желаемое сообщение всем! Мы только что реализовали самый простой чат-сервер на свете! Вы можете протестировать приведенный выше код с любым клиентом WebSocket как на компьютере, так и через расширение Chrome.
Реализуем сервер Socket.IO. Это было невероятно просто! Но Socket.IO обещает сделать это еще проще! Как нам реализовать тот же сервер с библиотекой Socket.IO?
1 2 3 4 5 6 7 8 9 10 11 |
const io = require("socket.io"); const server = io.listen(3002); server.on("connection", socket => { socket.on("message", message => { socket.emit(`[${socket.id}]: ${message}`); socket.broadcast.emit(`[${socket.id}]: ${message}`); }); }); |
Ух ты! Почти половина кода сервера WebSocket! Как видите, с помощью собственного broadcast метода Socket.IO мы не отправляем сообщение обратно отправителю; по этой причине нам нужно отправить это сообщение клиенту вручную.
Но есть проблема: вы не можете протестировать его на стандартном клиенте WebSocket (как мы видели в предыдущем примере). Это потому, что (как было сказано ранее) Socket.IO не использует простые WebSockets, а смешивает несколько технологий для поддержки как можно большего числа клиентов (и во избежание определенных проблем, как описано выше). Так как же это проверить?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<html> <head> <script src="https://cdn.jsdelivr.net/npm/socket.io-client@2.3.0/dist/socket.io.slim.js"></script> </head> <body> <script type="text/javascript"> ioClient = io.connect("http://localhost:3000"); ioClient.on("connect", socket => { ioClient.send("Hello everybody!"); ioClient.on("message", msg => console.log(msg)); }); </script> </body> </html> |
Вам нужно будет использовать клиент Socket.IO. В приведенном выше примере мы используем клиент, поставляемый с CDN, который позволяет нам проводить быстрые тесты в веб-браузере.
Как видите, эти два примера могут показаться не такими уж разными … но когда дело доходит до совместимости, вы должны помнить, что Socket.IO всегда будет работать со своей собственной клиентской библиотекой, поэтому вы не сможете ее использовать. для целей, отличных от веб-разработки. Это не относится к WebSockets, которые могут использоваться для решения большого набора проблем, таких как p2p-связь, передача данных между серверами в реальном времени и т. д.
Что нужно помнить
Как мы видели в начале, Socket.IO пытается решить множество проблем.
Горизонтальное масштабирование. Допустим, ваше приложение для чата пользуется большим успехом, и вам нужно добавить еще один сервер и балансировщик нагрузки для обработки всех запросов. Что ж, если вы открываете соединение на « server 1», но затем балансировщик нагрузки переключает вас на « server 2», вы получите следующую ошибку: «Error during WebSocket handshake: Unexpected response code: 400». Socket.IO решает эту проблему с помощью cookie (или маршрутизации соединения на основе исходного адреса), а WebSockets не предоставляет альтернативный механизм из коробки.
Производительность. Как мы уже говорили ранее, Socket.IO предоставляет несколько уровней абстракции над обычным транспортным уровнем WebSockets. Он также будет обеспечивать упаковку JSON для отправки реальных двоичных данных от клиента к серверу (и наоборот). Если вам когда-либо понадобится достичь такого уровня производительности, вам нужно будет настроить библиотеку Socket.IO, чтобы избежать этого конкретного поведения. С WebSockets у вас никогда не будет этой проблемы. Итак, что выбрать?
Что ж, на этот вопрос нет однозначного ответа. Socket.IO наверняка может немного упростить задачу; вам не нужно беспокоиться о проблемах, связанных с балансировщиком нагрузки, сбоях подключения и рассылке сообщений… но уверены ли вы, что эти функции вам действительно нужны? Одна только клиентская библиотека Socket.IO тяжелее, чем React, Redux и React-Redux, упакованные вместе. Вы уверены, что не можете использовать собственный WebSocket API браузера?
Также следует иметь в виду, что Socket.IO, реализованный на стороне сервера, представляет собой настраиваемую библиотеку / фреймворк, где большую часть времени вы не следуете логике WebSocket из-за абстракций, применяемых самим Socket.IO. Если вы реорганизуете свои микросервисы Node.js (скажем) на Go, Elixir, Java или любом другом языке, вам придется переписать большую часть логики, лежащей в основе поведения Socket.IO, чтобы добиться тех же результатов с помощью обычных WebSockets. Например, подумайте о трансляции сообщения каждому подключенному клиенту: в Socket.IO — это всего лишь один метод (.broadcast), но в обычных WebSockets вам придется реализовать его самостоятельно, поэтому вам придется переосмыслить способ его работы. Так что, возможно, стоит начать работать с обычными WebSockets с самого начала, чтобы было легче реорганизовать, расширить или реализовать новые функции на других языках (если вы пишете микросервисы / лямбда-выражения и т. д.).
Автор: Michele Riva
Источник: itnext.io
Редакция: Команда webformyself.
Читайте нас в Telegram, VK, Яндекс.Дзен