Введение
Что такое Socket.IO
Socket.IO — это библиотека, которая позволяет осуществлять реальное время, двустороннюю и основанную на событиях коммуникацию между браузером и сервером. Она состоит из:
- сервера Node.js: Исходный код | API
- библиотеки JavaScript-клиента для браузера (которая также может быть запущена из Node.js): Исходный код | API
Также существует несколько реализаций клиента на других языках, которые поддерживаются сообществом:
- Java: https://github.com/socketio/socket.io-client-java
- C++: https://github.com/socketio/socket.io-client-cpp
- Swift: https://github.com/socketio/socket.io-client-swift
- Dart: https://github.com/rikulo/socket.io-client-dart
- Python: https://github.com/miguelgrinberg/python-socketio
- .Net: https://github.com/Quobject/SocketIoClientDotNet
Как это работает?
Клиент попытается установить соединение WebSocket, если это возможно, и в противном случае перейдет на HTTP long polling.
WebSocket — это протокол связи, который обеспечивает полнодуплексный и канал с низкой задержкой между сервером и браузером. Дополнительную информацию можно найти здесь.
Итак, в идеальном случае, при условии:
- браузер поддерживает WebSocket (97% всех браузеров в 2020 году)
- нет элементов (прокси, брандмауэр и т. д.), препятствующих подключению WebSocket между клиентом и сервером
клиент Socket.IO можно рассматривать как «небольшой» обертка над API WebSocket. Вместо написания:
const socket = new WebSocket('ws://localhost:3000');socket.onopen(() => { socket.send('Hello!');});socket.onmessage(data => { console.log(data);}); |
На стороне клиента у вас будет:
const socket = io('ws://localhost:3000');socket.on('connect', () => { // either with send() socket.send('Hello!'); // or with emit() and custom event names socket.emit('salutations', 'Hello!', { 'mr': 'john' }, Uint8Array.from([1, 2, 3, 4]));});// handle the event sent with socket.send()socket.on('message', data => { console.log(data);});// handle the event sent with socket.emit()socket.on('greetings', (elem1, elem2, elem3) => { console.log(elem1, elem2, elem3);}); |
API на стороне сервера аналогичен, вы также получите объект socket, который расширяет класс Node.js EventEmitter:
const io = require('socket.io')(3000);io.on('connection', socket => { // either with send() socket.send('Hello!'); // or with emit() and custom event names socket.emit('greetings', 'Hey!', { 'ms': 'jane' }, Buffer.from([4, 3, 3, 1])); // handle the event sent with socket.send() socket.on('message', (data) => { console.log(data); }); // handle the event sent with socket.emit() socket.on('salutations', (elem1, elem2, elem3) => { console.log(elem1, elem2, elem3); });}); |
Socket.IO предоставляет дополнительные функции по сравнению с обычным объектом WebSocket, которые перечислены ниже.
Но сначала давайте подробно рассмотрим, чем библиотека Socket.IO не является.
Чем Socket.IO не является
Socket.IO — это НЕ реализация WebSocket. Хотя Socket.IO действительно использует WebSocket в качестве транспорта, когда это возможно, он добавляет дополнительную метаданные к каждому пакету. Вот почему клиент WebSocket не сможет успешно подключиться к серверу Socket.IO, и клиент Socket.IO не сможет подключиться к простому серверу WebSocket.
// WARNING: the client will NOT be able to connect!const socket = io('ws://echo.websocket.org'); |
Если вам нужен обычный сервер WebSocket, ознакомьтесь с ws или uWebSockets.js.
Также ведутся обсуждения о включении сервера WebSocket в ядро Node.js.
На стороне клиента вы можете быть заинтересованы в пакете robust-websocket.
Минимальный рабочий пример
Если вы новичок в экосистеме Node.js, ознакомьтесь с руководством Начало работы, которое идеально подходит для новичков.
В противном случае давайте начнем сразу! Библиотеку сервера можно установить из NPM:
$ npm install socket.io |
Дополнительную информацию об установке можно найти на странице Установка сервера.
Затем давайте создадим файл index.js, содержащий следующий контент:
const content = require('fs').readFileSync(__dirname + '/index.html', 'utf8');const httpServer = require('http').createServer((req, res) => { // serve the index.html file res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Length', Buffer.byteLength(content)); res.end(content);});const io = require('socket.io')(httpServer);io.on('connection', socket => { console.log('connect');});httpServer.listen(3000, () => { console.log('go to http://localhost:3000');}); |
Здесь запускается классический сервер Node.js HTTP для обслуживания файла index.html, и к нему присоединяется сервер Socket.IO. Подробный способ создания сервера см. на странице Инициализация сервера.
Давайте создадим файл index.html рядом с ним:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Minimal working example</title></head><body> <ul id="events"></ul> <script src="/socket.io/socket.io.js"></script> <script> const $events = document.getElementById('events'); const newItem = (content) => { const item = document.createElement('li'); item.innerText = content; return item; }; const socket = io(); socket.on('connect', () => { $events.appendChild(newItem('connect')); }); </script></body></html> |
Наконец, запустим наш сервер:
$ node index.js |
И вуаля!
Объект socket с обеих сторон расширяет класс EventEmitter, поэтому:
- отправка события выполняется с помощью:
socket.emit() - получение события выполняется путем регистрации обработчика:
socket.on(<event name>, <listener>)
Отправка события с сервера на клиент
Обновим файл index.js (серверная сторона):
io.on('connection', socket => { let counter = 0; setInterval(() => { socket.emit('hello', ++counter); }, 1000);}); |
И файл index.html (клиентская сторона):
const socket = io();socket.on('connect', () => { $events.appendChild(newItem('connect'));});socket.on('hello', (counter) => { $events.appendChild(newItem(`hello - ${counter}`));}); |
Демо:
Отправка сообщения с клиента на сервер
Обновим файл index.js (серверная сторона):
io.on('connection', socket => { socket.on('hey', data => { console.log('hey', data); });}); |
И файл index.html (клиентская сторона):
const socket = io();socket.on('connect', () => { $events.appendChild(newItem('connect'));});let counter = 0;setInterval(() => { ++counter; socket.emit('hey', { counter }); // the object will be serialized for you}, 1000); |
Демо:
Теперь давайте подробно рассмотрим возможности, предоставляемые Socket.IO.
Функциональные возможности
Основные функции:
Надёжность
Соединения устанавливаются даже при наличии:
- прокси и балансировщиков нагрузки.
- персональных брандмауэров и антивирусного ПО.
Для этого используется Engine.IO, который сначала устанавливает соединение long-polling, а затем пытается переключиться на более лучшие транспорты, которые «тестируются» со стороны, такие как WebSocket. Подробнее об этом можно прочитать в разделе Цели.
Автоматическое восстановление соединения
Если не указано иное, отключенный клиент будет пытаться подключиться снова постоянно, пока сервер снова не станет доступен. Доступные параметры повторного подключения см. здесь.
Обнаружение отключения
На уровне Engine.IO реализован механизм проверки состояния, позволяющий как серверу, так и клиенту определять, когда другой перестает отвечать.
Это достигается с помощью таймеров, установленных на сервере и клиенте, с значениями тайм-аута (параметры pingInterval и pingTimeout), которые обмениваются во время рукопожатия при подключении. Эти таймеры требуют, чтобы все последующие вызовы клиента были направлены на тот же сервер, поэтому требуется склейка сеанса (sticky-session) при использовании нескольких узлов.
Поддержка двоичных данных
Можно передавать любые сериализуемые структуры данных, включая:
- ArrayBuffer и Blob в браузере
- ArrayBuffer и Buffer в Node.js
Поддержка множественного подключения
Чтобы обеспечить разделение задач в вашем приложении (например, по модулям или на основе разрешений), Socket.IO позволяет создавать несколько пространств имен, которые будут действовать как отдельные каналы связи, но будут использовать одно и то же базовое соединение.
© 2014–2020 Automattic
Licensed under the MIT License.
https://socket.io/docs/v2