We will discuss regarding Fallbacks, Connection using Socket.IO, Events and Messages.
Fallbacks
Socket.IO has a lot of underlying transport mechanisms, which deal with various constraints arising due to cross browser issues, WebSocket implementations, firewalls, port blocking, etc.
Though W3C has a defined specification for WebSocket API, it is still lacking in implementation. Socket.IO provides us with fallback mechanisms, which can deal with such issues. If we develop apps using the native API, we have to implement the fallbacks ourselves. Socket.IO covers a large list of fallbacks in the following order −
- WebSockets
- FlashSocket
- XHR long polling
- XHR multipart streaming
- XHR polling
- JSONP polling
- iframes
Connection using Socket.IO
The Socket.IO connection begins with the handshake. This makes the handshake a special part of the protocol. Apart from the handshake, all the other events and messages in the protocol are transferred over the socket.
Socket.IO is intended for use with web applications, and therefore it is assumed that these applications will always be able to use HTTP. It is because of this reasoning that the Socket.IO handshake takes place over HTTP using a POST request on the handshake URI (passed to the connect method).
Events and messages
WebSocket native API only sends messages across. Socket.IO provides an addition layer over these messages, which allows us to create events and again helps us develop apps easily by separating the different types of messages sent.
The native API sends messages only in plain text. This is also taken care of by Socket.IO. It handles the serialization and deserialization of data for us.
We have an official client API for the web. For other clients such as native mobile phones, other application clients also we can use Socket.IO using the following steps.
- Step 1 − A connection needs to be established using the same connection protocol discussed above.
- Step 2 − The messages need to be in the same format as specified by Socket.IO. This format enables Socket.IO to determine the type of the message and the data sent in the message and some metadata useful for operation.
The message format is −
[type] : [id ('+')] : [endpoint] (: [data]
The parameters in the above command are explained below −
- Type is a single digit integer, specifying what type message it is.
- ID is message ID, an incremental integer used for acknowledgements.
- Endpoint is the socket endpoint that the message is intended to be delivered to...
- Data is the associated data to be delivered to the socket. In case of messages, it is treated as plain text, for other events, it is treated as JSON.
Dependency graph
engine.io-parser
This is the JavaScript parser for the engine.io protocol encoding, shared by both engine.io-client and engine.io.
The specification for the protocol can be found here: https://github.com/socketio/engine.io-protocol
engine.io
Engine.IO is the implementation of transport-based cross-browser/cross-device bi-directional communication layer for Socket.IO.
Its main feature is the ability to swap transports on the fly. A connection (initiated by an engine.io-client counterpart) starts with XHR polling, but can then switch to WebSocket if possible.
It uses the engine.io-parser to encode/decode packets.
engine.io-client
This is the client for Engine.IO, the implementation of transport-based cross-browser/cross-device bi-directional communication layer for Socket.IO.
It runs in both the browser (including HTML5 WebWorker) and Node.js.
It uses the engine.io-parser to encode/decode packets.
socket.io-adapter
This is the default Socket.IO in-memory adapter class.
This module is not intended for end-user usage, but can be used as an interface to inherit from from other adapters you might want to build, like socket.io-redis.
socket.io-redis
This is the adapter using the Redis Pub/Sub mechanism to broadcast messages between multiple nodes.
socket.io-parser
A socket.io encoder and decoder written in JavaScript complying with version 3 of socket.io-protocol. Used by socket.io and socket.io-client.
socket.io
Socket.IO brings some syntactic sugar over the Engine.IO “raw” API. It also brings two new concepts, Rooms and Namespaces, which introduce a separation of concern between communication channels. Please see the associated documentation there.
By default, it exposes a browser build of the client at /socket.io/socket.io.js.
It uses the socket.io-parser to encode/decode packets.
socket.io-client
This is the client for Socket.IO. It relies on engine.io-client, which manages the transport swapping and the disconnection detection.
It handles reconnection automatically, in case the underlying connection is severed.
It uses the socket.io-parser to encode/decode packets.
Connection
const client = io('https://myhost.com');
The following steps take place:
-
on the client-side, a new engine.io-client instance is created
-
the engine.io-client instance tries to establish a polling transport
GET https://myhost.com/socket.io/?EIO=3&transport=polling&t=ML4jUwU&b64=1
with:
"EIO=3" # the current version of the Engine.IO protocol
"transport=polling" # the transport being established
"t=ML4jUwU&b64=1" # a hashed timestamp for cache-busting
-
the engine.io server responds with:
{
"type": "open",
"data": {
"sid": "36Yib8-rSutGQYLfAAAD", // the unique session id
"upgrades": ["websocket"], // the list of possible transport upgrades
"pingInterval": 25000, // the 1st parameter for the heartbeat mechanism
"pingTimeout": 5000 // the 2nd parameter for the heartbeat mechanism
}
}
-
the content is encoded by the engine.io-parser as:
'96:0{"sid":"hLOEJXN07AE0GQCNAAAB","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40'
with:
"96" # the length of the first message
":" # a separator between length and content
"0" # the "open" message type
'{"sid":"hLOEJXN07AE0GQCNAAAB","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}' # the JSON-encoded handshake data
"2" # the length of the second message
":" # a separator between length and content
"4" # the "message" message type
"0" # the "open" message type in Socket.IO protocol
-
the content is then decoded by the engine.io-parser on the client-side
-
an open event is emitted at the engine.io-client level
-
a connect event is emitted at the socket.io-client level
Upgrade
Once all the buffers of the existing transport (XHR polling) are flushed, an upgrade gets tested on the side by sending a probe.
GET wss://myhost.com/socket.io/?EIO=3&transport=websocket&sid=36Yib8-rSutGQYLfAAAD
with:
"EIO=3" # again, the current version of the Engine.IO protocol
"transport=websocket" # the new transport being probed
"sid=36Yib8-rSutGQYLfAAAD" # the unique session id
-
a “ping” packet is sent by the client in a WebSocket frame, encoded as 2probe by the engine.io-parser, with 2 being the “ping” message type.
-
the server responds with a “pong” packet, encoded as 3probe, with 3 being the “pong” message type.
-
upon receiving the “pong” packet, the upgrade is considered complete and all following messages go through the new transport.
