Как это работает
Двунаправленный канал между сервером Socket.IO (Node.js) и клиентом Socket.IO (браузер, Node.js или другой язык программирования) устанавливается с помощью соединения WebSocket, если это возможно, а в качестве резервного варианта используется HTTP-долговое опрос.
Код Socket.IO разделен на два отдельных уровня:
- нижний уровень: то, что мы называем Engine.IO, движок внутри Socket.IO
- высокоуровневый API: сам Socket.IO
Engine.IO
Engine.IO отвечает за установление соединения низкого уровня между сервером и клиентом. Он обрабатывает:
Исходный код можно найти здесь:
- сервер: https://github.com/socketio/engine.io
- клиент: https://github.com/socketio/engine.io-client
- парсер: https://github.com/socketio/engine.io-parser
- описание протокола: https://github.com/socketio/engine.io-protocol
Транспортные средства
В настоящее время реализованы два транспортных средства:
HTTP-долговое опрос
Транспорт HTTP-долгового опроса (также просто «опрос») состоит из последовательных HTTP-запросов:
- долгоработающие
GETзапросы для получения данных с сервера - кратковременные
POSTзапросы для отправки данных на сервер
В силу особенностей транспортного средства, последовательные отправки могут быть конкатенированы и отправлены в одном HTTP-запросе.
WebSocket
Транспорт WebSocket состоит из соединения WebSocket, обеспечивающего двунаправленный канал связи с низкой задержкой между сервером и клиентом.
Из-за особенностей транспортного средства каждая отправка отправляется в отдельной WebSocket-рамке (некоторые отправки могут даже привести к двум отдельным WebSocket-рамкам, дополнительная информация здесь).
Установление соединения
В начале соединения Engine.IO сервер отправляет некоторую информацию:
{
"sid": "FSDjX-WRwSA4zTZMALqx",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 20000
}
- значение
sidявляется идентификатором сессии, оно должно быть включено вsidпараметр запроса во всех последующих HTTP-запросах - массив
upgradesсодержит список всех «лучших» транспортных средств, поддерживаемых сервером - значения
pingIntervalиpingTimeoutиспользуются в механизме «сердцебиения»
Механизм обновления
По умолчанию клиент устанавливает соединение с транспортным средством HTTP-долгового опроса.
Но почему?
Хотя WebSocket является очевидно лучшим способом установления двунаправленного соединения, опыт показал, что не всегда можно установить соединение WebSocket из-за корпоративных прокси-серверов, личных брандмауэров, антивирусного программного обеспечения...
С точки зрения пользователя, неудачное подключение WebSocket может привести к ожиданию до как минимум 10 секунд, прежде чем приложение реального времени начнет обмен данными. Это существенно негативно сказывается на пользовательском опыте.
Подводя итог, Engine.IO фокусируется на надежности и пользовательском опыте в первую очередь, второстепенное значение придается потенциальному улучшению UX и увеличению производительности сервера.
Для обновления клиент:
- убедится, что его буфер вывода пуст
- переведет текущий транспорт в режим только для чтения
- попытается установить соединение с другим транспортным средством
- если это удалось, закроет первый транспорт
Вы можете проверить это в мониторе сети вашего браузера:
- установление соединения (содержит идентификатор сессии — здесь,
zBjrh...AAAK— который используется в последующих запросах) - отправка данных (HTTP-долговое опрос)
- получение данных (HTTP-долговое опрос)
- обновление (WebSocket)
- получение данных (HTTP-долговое опрос, закрывается, как только соединение WebSocket в пункте 4 успешно установлено)
Обнаружение отключения
Соединение Engine.IO считается закрытым, когда:
- один HTTP-запрос (GET или POST) завершается неудачно (например, при остановке сервера)
- соединение WebSocket закрывается (например, при закрытии вкладки пользователем в браузере)
-
socket.disconnect()вызывается на стороне сервера или клиента
Также существует механизм «сердцебиения», который проверяет, что соединение между сервером и клиентом по-прежнему активно:
С заданным интервалом (значение pingInterval отправленное при установлении соединения) сервер отправляет пакет PING, а у клиента есть несколько секунд (значение pingTimeout ) чтобы отправить пакет PONG обратно. Если сервер не получает пакет PONG обратно, он считает, что соединение закрыто. Аналогично, если клиент не получает пакет PING в течение pingInterval + pingTimeout, он считает, что соединение закрыто.
Причины отключения перечислены здесь (на стороне сервера) и здесь (на стороне клиента).
Socket.IO
Socket.IO предоставляет дополнительные функции над соединением Engine.IO:
- автоматическое переподключение
- буферизация пакетов
- подтверждения
- вещание всем клиентам или подмножеству клиентов (то, что мы называем «Комнатой»)
- мультиплексирование (то, что мы называем «Пространством имен»)
Исходный код можно найти здесь:
- сервер: https://github.com/socketio/socket.io
- клиент: https://github.com/socketio/socket.io-client
- парсер: https://github.com/socketio/socket.io-parser
- описание протокола: https://github.com/socketio/socket.io-protocol
© 2014–2021 Automattic
Licensed under the MIT License.
https://socket.io/docs/v4/how-it-works