Как это работает
Двунаправленный канал между сервером 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, больше информации здесь).
Handshake
В начале соединения Engine.IO сервер отправляет некоторую информацию:
{
"sid": "FSDjX-WRwSA4zTZMALqx",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 5000
} |
sid— это идентификатор сессии, он должен быть включен вsidпараметр запроса во всех последующих HTTP-запросах- массив
upgradesсодержит список всех «лучших» передач, поддерживаемых сервером - значения
pingIntervalиpingTimeoutиспользуются в механизме проверки работоспособности соединения
Механизм обновления
По умолчанию клиент устанавливает соединение с помощью передачи HTTP-долгое опроса.
Но почему?
Хотя WebSocket является лучшим способом для двусторонней связи, опыт показал, что не всегда возможно установить соединение WebSocket из-за корпоративных прокси-серверов, личных брандмауэров, антивирусного программного обеспечения...
С точки зрения пользователя неудачное соединение WebSocket может привести к ожиданию до 10 секунд, прежде чем приложение начнёт обмен данными в реальном времени. Это негативно сказывается на пользовательском опыте.
Подводя итог, Engine.IO фокусируется на надёжности и пользовательском опыте в первую очередь, а на незначительных потенциальных улучшениях UX и увеличении производительности сервера — во вторую.
Для обновления клиент выполнит следующие действия:
- убедится, что его буфер вывода пуст
- переведёт текущую передачу в режим только для чтения
- попытается установить соединение с другой передачей
- при успехе закроет первую передачу
Вы можете проверить это в мониторе сети своего браузера:
- handshake (содержит идентификатор сессии — здесь,
zBjrh...AAAK— который используется в последующих запросах) - отправка данных (HTTP-долгое опроса)
- получение данных (HTTP-долгое опроса)
- обновление (WebSocket)
- получение данных (HTTP-долгое опроса, закрывается после того, как соединение WebSocket в пункте 4. успешно установлено)
Обнаружение отключения
Соединение Engine.IO считается закрытым, когда:
- один HTTP-запрос (GET или POST) завершается неудачно (например, при остановке сервера)
- соединение WebSocket закрывается (например, при закрытии вкладки в браузере)
-
socket.disconnect()вызывается на стороне сервера или клиента
Также существует механизм проверки работоспособности соединения, который проверяет, что соединение между сервером и клиентом всё ещё активно:
С заданным интервалом (значение pingInterval отправленное в handshake) сервер отправляет пакет 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/v3/how-it-works