Engine.IO protocol
https://github.com/socketio/engine.io-protocol.git
This document describes the Engine.IO protocol. For a reference JavaScript implementation, take a look at engine.io-parser, engine.io-client and engine.io.
Table of Contents:
This is revision 4 of the Engine.IO protocol.
The revision 2 can be found here: https://github.com/socketio/engine.io-protocol/tree/v2
The revision 3 can be found here: https://github.com/socketio/engine.io-protocol/tree/v3
open packet with JSON-encoded handshake data:sid session id (String)upgrades possible transport upgrades (Array of String)pingTimeout server configured ping timeout, used for the clientNumber)
pingInterval server configured ping interval, used for the clientNumber)
maxPayload server configured maximum number of bytes per chunk, used by the client to aggregate packets into payloads (Number)ping packets sent by the serverpong packets.
message packets at will.close packet to close the socket, sinceGET /engine.io/?EIO=4&transport=polling&t=N8hyd6w
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
0{"sid":"lv_VI97HAXpY6yYWAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000,"maxPayload":1000000}
Details:
0 => "open" packet type
{"sid":... => the handshake data
Note: the t query param is used to ensure that the request is not cached by the browser.
socket.send('hey') is executed on the server:
GET /engine.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
4hey
Details:
4 => "message" packet type
hey => the actual message
Note: the sid query param contains the sid sent in the handshake.
socket.send('hello'); socket.send('world'); is executed on the client:
POST /engine.io/?EIO=4&transport=polling&t=N8hzxke&sid=lv_VI97HAXpY6yYWAAAC
> Content-Type: text/plain; charset=UTF-8
4hello\x1e4world
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
ok
Details:
4 => "message" packet type
hello => the 1st message
\x1e => separator
4 => "message" message type
world => the 2nd message
GET /engine.io/?EIO=4&transport=websocket&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 101 Switching Protocols
WebSocket frames:
< 2probe => probe request
> 3probe => probe response
< 5 => "upgrade" packet type
> 4hello => message (not concatenated)
> 4world
> 2 => "ping" packet type
< 3 => "pong" packet type
> 1 => "close" packet type
In that case, the client only enables WebSocket (without HTTP polling).
GET /engine.io/?EIO=4&transport=websocket
< HTTP/1.1 101 Switching Protocols
WebSocket frames:
< 0{"sid":"lv_VI97HAXpY6yYWAAAC","pingInterval":25000,"pingTimeout":5000,"maxPayload":1000000}} => handshake
< 4hey
> 4hello => message (not concatenated)
> 4world
< 2 => "ping" packet type
> 3 => "pong" packet type
> 1 => "close" packet type
An Engine.IO url is composed as follows:
/engine.io/[?<query string>]
engine.io pathname should only be changed by higher-leveltransport: indicates the transport name. Supported ones by default arepolling, websocket.
j: if the transport is polling but a JSONP response is required, jsid: if the client has been given a session id, it must be includedEIO: the version of the protocolt: a hashed-timestamp used for cache-busting/engine.io portion modifiable?
Provided the server is customized to intercept requests under a different path segment, yes.
FAQ: What determines whether an option is going to be part of the path
versus being encoded as part of the query string? In other words, why
is the transport not part of the URL?
It's convention that the path segments remain only that which allows to
disambiguate whether a request should be handled by a given Engine.IO
server instance or not. As it stands, it's only the Engine.IO prefix
(/engine.io) and the resource (default by default).
There's two distinct types of encodings
An encoded packet can be UTF-8 string or binary data. The packet encoding format for a string is as follows
<packet type id>[<data>]
example:
4hello
For binary data the packet type is not included, since only "message" packet type can include binary.
Sent from the server when a new transport is opened (recheck)
Request the close of this transport but does not shutdown the connection itself.
Sent by the server. Client should answer with a pong packet.
example
2%%CODEBLOCK13%%3%%CODEBLOCK14%%4HelloWorld%%CODEBLOCK15%%socket.on('message', function (data) { console.log(data); });%%CODEBLOCK16%%4HelloWorld%%CODEBLOCK17%%socket.on('message', function (data) { console.log(data); });%%CODEBLOCK18%%2probe%%CODEBLOCK19%%3probe%%CODEBLOCK20%%5%%CODEBLOCK21%%
<packet1>\x1e<packet2>\x1e<packet3>
%%CODEBLOCK22%%
<packet1>\x1eb<packet2 data in b64>[...]
%%CODEBLOCK23%%
[
{
"type": "message",
"data": "hello"
},
{
"type": "message",
"data": "โฌ"
}
]
%%CODEBLOCK24%%
4hello\x1e4โฌ
%%CODEBLOCK25%%
[
{
"type": "message",
"data": "โฌ"
},
{
"type": "message",
"data": buffer <01 02 03 04>
}
]
%%CODEBLOCK26%%
4โฌ\x1ebAQIDBA==
with
4 => "message" packet type
โฌ
\x1e => record separator
b => indicates a base64 packet
AQIDBA== => buffer content encoded in base64
%%CODEBLOCK27%%
___eio <encoded payload> "" rel="noopener"> <j> ;
%%CODEBLOCK28%%
___eio[4]("packet data");
%%CODEBLOCK29%%
d=<escaped packet payload>
%%CODEBLOCK30%%js
import { listen } from "engine.io";
const server = listen(3000, {
pingInterval: 300,
pingTimeout: 200,
maxPayload: 1e6,
cors: {
origin: "*"
}
});
server.on("connection", socket => {
socket.on("data", (...args) => {
socket.send(...args);
});
});
``