• Stars
    star
    494
  • Rank 85,749 (Top 2 %)
  • Language
    JavaScript
  • Created over 11 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Socket.IO Protocol specification

Socket.IO Protocol

This document describes the 5th version of the Socket.IO protocol.

Table of content

Introduction

The Socket.IO protocol enables full-duplex and low-overhead communication between a client and a server.

It is built on top of the Engine.IO protocol, which handles the low-level plumbing with WebSocket and HTTP long-polling.

The Socket.IO protocol adds the following features:

  • multiplexing (referred as "namespace" in the Socket.IO jargon)

Example with the JavaScript API:

Server

// declare the namespace
const namespace = io.of("/admin");
// handle the connection to the namespace
namespace.on("connection", (socket) => {
  // ...
});

Client

// reach the main namespace
const socket1 = io();
// reach the "/admin" namespace (with the same underlying WebSocket connection)
const socket2 = io("/admin");
// handle the connection to the namespace
socket2.on("connect", () => {
  // ...
});
  • acknowledgement of packets

Example with the JavaScript API:

// on one side
socket.emit("hello", "foo", (arg) => {
  console.log("received", arg);
});

// on the other side
socket.on("hello", (arg, ack) => {
  ack("bar");
});

The reference implementation is written in TypeScript:

Exchange protocol

A Socket.IO packet contains the following fields:

  • a packet type (integer)
  • a namespace (string)
  • optionally, a payload (Object | Array)
  • optionally, an acknowledgment id (integer)

Here is the list of available packet types:

Type ID Usage
CONNECT 0 Used during the connection to a namespace.
DISCONNECT 1 Used when disconnecting from a namespace.
EVENT 2 Used to send data to the other side.
ACK 3 Used to acknowledge an event.
CONNECT_ERROR 4 Used during the connection to a namespace.
BINARY_EVENT 5 Used to send binary data to the other side.
BINARY_ACK 6 Used to acknowledge an event (the response includes binary data).

Connection to a namespace

At the beginning of a Socket.IO session, the client MUST send a CONNECT packet:

The server MUST respond with either:

  • a CONNECT packet if the connection is successful, with the session ID in the payload
  • or a CONNECT_ERROR packet if the connection is not allowed
CLIENT                                                      SERVER

  │  ───────────────────────────────────────────────────────►  │
  │             { type: CONNECT, namespace: "/" }              │
  │  ◄───────────────────────────────────────────────────────  │
  │   { type: CONNECT, namespace: "/", data: { sid: "..." } }  │

If the server does not receive a CONNECT packet first, then it MUST close the connection immediately.

A client MAY be connected to multiple namespaces at the same time, with the same underlying WebSocket connection.

Examples:

  • with the main namespace (named "/")
Client > { type: CONNECT, namespace: "/" }
Server > { type: CONNECT, namespace: "/", data: { sid: "wZX3oN0bSVIhsaknAAAI" } }
  • with a custom namespace
Client > { type: CONNECT, namespace: "/admin" }
Server > { type: CONNECT, namespace: "/admin", data: { sid: "oSO0OpakMV_3jnilAAAA" } }
  • with an additional payload
Client > { type: CONNECT, namespace: "/admin", data: { "token": "123" } }
Server > { type: CONNECT, namespace: "/admin", data: { sid: "iLnRaVGHY4B75TeVAAAB" } }
  • in case the connection is refused
Client > { type: CONNECT, namespace: "/" }
Server > { type: CONNECT_ERROR, namespace: "/", data: { message: "Not authorized" } }

Sending and receiving data

Once the connection to a namespace is established, the client and the server can begin exchanging data:

CLIENT                                                      SERVER

  │  ───────────────────────────────────────────────────────►  │
  │        { type: EVENT, namespace: "/", data: ["foo"] }      │
  │                                                            │
  │  ◄───────────────────────────────────────────────────────  │
  │        { type: EVENT, namespace: "/", data: ["bar"] }      │

The payload is mandatory and MUST be a non-empty array. If that's not the case, then the receiver MUST close the connection.

Examples:

  • with the main namespace
Client > { type: EVENT, namespace: "/", data: ["foo"] }
  • with a custom namespace
Server > { type: EVENT, namespace: "/admin", data: ["bar"] }
  • with binary data
Client > { type: BINARY_EVENT, namespace: "/", data: ["baz", <Buffer <01 02 03 04>> ] }

Acknowledgement

The sender MAY include an event ID in order to request an acknowledgement from the receiver:

CLIENT                                                      SERVER

  │  ───────────────────────────────────────────────────────►  │
  │   { type: EVENT, namespace: "/", data: ["foo"], id: 12 }   │
  │  ◄───────────────────────────────────────────────────────  │
  │    { type: ACK, namespace: "/", data: ["bar"], id: 12 }    │

The receiver MUST respond with an ACK packet with the same event ID.

The payload is mandatory and MUST be an array (possibly empty).

Examples:

  • with the main namespace
Client > { type: EVENT, namespace: "/", data: ["foo"], id: 12 }
Server > { type: ACK, namespace: "/", data: [], id: 12 }
  • with a custom namespace
Server > { type: EVENT, namespace: "/admin", data: ["foo"], id: 13 }
Client > { type: ACK, namespace: "/admin", data: ["bar"], id: 13 }
  • with binary data
Client > { type: BINARY_EVENT, namespace: "/", data: ["foo", <buffer <01 02 03 04> ], id: 14 }
Server > { type: ACK, namespace: "/", data: ["bar"], id: 14 }

or

Server > { type: EVENT, namespace: "/", data: ["foo" ], id: 15 }
Client > { type: BINARY_ACK, namespace: "/", data: ["bar", <buffer <01 02 03 04>], id: 15 }

Disconnection from a namespace

At any time, one side can end the connection to a namespace by sending a DISCONNECT packet:

CLIENT                                                      SERVER

  │  ───────────────────────────────────────────────────────►  │
  │           { type: DISCONNECT, namespace: "/" }             │

No response is expected from the other side. The low-level connection MAY be kept alive if the client is connected to another namespace.

Packet encoding

This section details the encoding used by the default parser which is included in Socket.IO server and client, and whose source can be found here.

The JavaScript server and client implementations also supports custom parsers, which have different tradeoffs and may benefit to certain kind of applications. Please see socket.io-json-parser or socket.io-msgpack-parser for example.

Please also note that each Socket.IO packet is sent as a Engine.IO message packet (more information here), so the encoded result will be prefixed by the character "4" when sent over the wire (in the request/response body with HTTP long-polling, or in the WebSocket frame).

Format

<packet type>[<# of binary attachments>-][<namespace>,][<acknowledgment id>][JSON-stringified payload without binary]

+ binary attachments extracted

Note: the namespace is only included if it is different from the main namespace (/)

Examples

Connection to a namespace

  • with the main namespace

Packet

{ type: CONNECT, namespace: "/" }

Encoded

0
  • with a custom namespace

Packet

{ type: CONNECT, namespace: "/admin", data: { sid: "oSO0OpakMV_3jnilAAAA" } }

Encoded

0/admin,{"sid":"oSO0OpakMV_3jnilAAAA"}
  • in case the connection is refused

Packet

{ type: CONNECT_ERROR, namespace: "/", data: { message: "Not authorized" } }

Encoded

4{"message":"Not authorized"}

Sending and receiving data

  • with the main namespace

Packet

{ type: EVENT, namespace: "/", data: ["foo"] }

Encoded

2["foo"]
  • with a custom namespace

Packet

{ type: EVENT, namespace: "/admin", data: ["bar"] }

Encoded

2/admin,["bar"]
  • with binary data

Packet

{ type: BINARY_EVENT, namespace: "/", data: ["baz", <Buffer <01 02 03 04>> ] }

Encoded

51-["baz",{"_placeholder":true,"num":0}]

+ <Buffer <01 02 03 04>>
  • with multiple attachments

Packet

{ type: BINARY_EVENT, namespace: "/admin", data: ["baz", <Buffer <01 02>>, <Buffer <03 04>> ] }

Encoded

52-/admin,["baz",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1}]

+ <Buffer <01 02>>
+ <Buffer <03 04>>

Please remember that each Socket.IO packet is wrapped in a Engine.IO message packet, so they will be prefixed by the character "4" when sent over the wire.

Example: { type: EVENT, namespace: "/", data: ["foo"] } will be sent as 42["foo"]

Acknowledgement

  • with the main namespace

Packet

{ type: EVENT, namespace: "/", data: ["foo"], id: 12 }

Encoded

212["foo"]
  • with a custom namespace

Packet

{ type: ACK, namespace: "/admin", data: ["bar"], id: 13 }

Encoded

3/admin,13["bar"]`
  • with binary data

Packet

{ type: BINARY_ACK, namespace: "/", data: ["bar", <Buffer <01 02 03 04>>], id: 15 }

Encoded

61-15["bar",{"_placeholder":true,"num":0}]

+ <Buffer <01 02 03 04>>

Disconnection from a namespace

  • with the main namespace

Packet

{ type: DISCONNECT, namespace: "/" }

Encoded

1
  • with a custom namespace
{ type: DISCONNECT, namespace: "/admin" }

Encoded

1/admin,

Sample session

Here is an example of what is sent over the wire when combining both the Engine.IO and the Socket.IO protocols.

  • Request n°1 (open packet)
GET /socket.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           => Engine.IO "open" packet type
{"sid":...  => the Engine.IO handshake data

Note: the t query param is used to ensure that the request is not cached by the browser.

  • Request n°2 (namespace connection request):
POST /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
40

Details:

4           => Engine.IO "message" packet type
0           => Socket.IO "CONNECT" packet type
  • Request n°3 (namespace connection approval)
GET /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
40{"sid":"wZX3oN0bSVIhsaknAAAI"}
  • Request n°4

socket.emit('hey', 'Jude') is executed on the server:

GET /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
42["hey","Jude"]

Details:

4           => Engine.IO "message" packet type
2           => Socket.IO "EVENT" packet type
[...]       => content
  • Request n°5 (message out)

socket.emit('hello'); socket.emit('world'); is executed on the client:

POST /socket.io/?EIO=4&transport=polling&t=N8hzxke&sid=lv_VI97HAXpY6yYWAAAC
> Content-Type: text/plain; charset=UTF-8
42["hello"]\x1e42["world"]
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
ok

Details:

4           => Engine.IO "message" packet type
2           => Socket.IO "EVENT" packet type
["hello"]   => the 1st content
\x1e        => separator
4           => Engine.IO "message" packet type
2           => Socket.IO "EVENT" packet type
["world"]   => the 2nd content
  • Request n°6 (WebSocket upgrade)
GET /socket.io/?EIO=4&transport=websocket&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 101 Switching Protocols

WebSocket frames:

< 2probe                                        => Engine.IO probe request
> 3probe                                        => Engine.IO probe response
> 5                                             => Engine.IO "upgrade" packet type
> 42["hello"]
> 42["world"]
> 40/admin,                                     => request access to the admin namespace (Socket.IO "CONNECT" packet)
< 40/admin,{"sid":"-G5j-67EZFp-q59rADQM"}       => grant access to the admin namespace
> 42/admin,1["tellme"]                          => Socket.IO "EVENT" packet with acknowledgement
< 461-/admin,1[{"_placeholder":true,"num":0}]   => Socket.IO "BINARY_ACK" packet with a placeholder
< <binary>                                      => the binary attachment (sent in the following frame)
... after a while without message
> 2                                             => Engine.IO "ping" packet type
< 3                                             => Engine.IO "pong" packet type
> 1                                             => Engine.IO "close" packet type

History

Difference between v5 and v4

The 5th revision (current) of the Socket.IO protocol is used in Socket.IO v3 and above (v3.0.0 was released in November 2020).

It is built on top of the 4th revision of the Engine.IO protocol (hence the EIO=4 query parameter).

List of changes:

  • remove the implicit connection to the default namespace

In previous versions, a client was always connected to the default namespace, even if it requested access to another namespace.

This is not the case anymore, the client must send a CONNECT packet in any case.

Commits: 09b6f23 (server) and 249e0be (client)

  • rename ERROR to CONNECT_ERROR

The meaning and the code number (4) are not modified: this packet type is still used by the server when the connection to a namespace is refused. But we feel the name is more self-descriptive.

Commits: d16c035 (server) and 13e1db7c (client).

  • the CONNECT packet now can contain a payload

The client can send a payload for authentication/authorization purposes. Example:

{
  "type": 0,
  "nsp": "/admin",
  "data": {
    "token": "123"
  }
}

In case of success, the server responds with a payload contain the ID of the Socket. Example:

{
  "type": 0,
  "nsp": "/admin",
  "data": {
    "sid": "CjdVH4TQvovi1VvgAC5Z"
  }
}

This change means that the ID of the Socket.IO connection will now be different from the ID of the underlying Engine.IO connection (the one that is found in the query parameters of the HTTP requests).

Commits: 2875d2c (server) and bbe94ad (client)

  • the payload CONNECT_ERROR packet is now an object instead of a plain string

Commits: 54bf4a4 (server) and 0939395 (client)

Difference between v4 and v3

The 4th revision of the Socket.IO protocol is used in Socket.IO v1 (v1.0.3 was released in June 2014) and v2 (v2.0.0 was released in May 2017).

The details of the revision can be found here: https://github.com/socketio/socket.io-protocol/tree/v4

It is built on top of the 3rd revision of the Engine.IO protocol (hence the EIO=3 query parameter).

List of changes:

  • add a BINARY_ACK packet type

Previously, an ACK packet was always treated as if it may contain binary objects, with recursive search for such objects, which could hurt performance.

Reference: https://github.com/socketio/socket.io-parser/commit/ca4f42a922ba7078e840b1bc09fe3ad618acc065

Difference between v3 and v2

The 3rd revision of the Socket.IO protocol is used in early Socket.IO v1 versions ([email protected]) (released in May 2014).

The details of the revision can be found here: https://github.com/socketio/socket.io-protocol/tree/v3

List of changes:

  • remove the usage of msgpack to encode packets containing binary objects (see also 299849b)

Difference between v2 and v1

List of changes:

  • add a BINARY_EVENT packet type

This was added during the work towards Socket.IO 1.0, in order to add support for binary objects. The BINARY_EVENT packets were encoded with msgpack.

Initial revision

This first revision was the result of the split between the Engine.IO protocol (low-level plumbing with WebSocket / HTTP long-polling, heartbeat) and the Socket.IO protocol. It was never included in a Socket.IO release, but paved the way for the next iterations.

Test suite

The test suite in the test-suite/ directory lets you check the compliance of a server implementation.

Usage:

  • in Node.js: npm ci && npm test
  • in a browser: simply open the index.html file in your browser

For reference, here is expected configuration for the JavaScript server to pass all tests:

import { Server } from "socket.io";

const io = new Server(3000, {
  pingInterval: 300,
  pingTimeout: 200,
  maxPayload: 1000000,
  cors: {
    origin: "*"
  }
});

io.on("connection", (socket) => {
  socket.emit("auth", socket.handshake.auth);

  socket.on("message", (...args) => {
    socket.emit.apply(socket, ["message-back", ...args]);
  });

  socket.on("message-with-ack", (...args) => {
    const ack = args.pop();
    ack(...args);
  })
});

io.of("/custom").on("connection", (socket) => {
  socket.emit("auth", socket.handshake.auth);
});

License

MIT

More Repositories

1

socket.io

Realtime application framework (Node.JS server)
TypeScript
59,936
star
2

socket.io-client

Realtime application framework (client)
TypeScript
10,492
star
3

socket.io-client-java

Full-featured Socket.IO Client Library for Java, which is compatible with Socket.IO v1.0 and later.
Java
5,244
star
4

socket.io-client-swift

Swift
5,128
star
5

engine.io

The engine used in the Socket.IO JavaScript server, which manages the low-level transports such as HTTP long-polling and WebSocket.
JavaScript
4,569
star
6

socket.io-redis-adapter

Adapter to enable broadcasting of events to multiple separate socket.io server nodes.
TypeScript
2,712
star
7

socket.io-client-cpp

C++11 implementation of Socket.IO client
C++
2,181
star
8

socket.io-p2p

JavaScript
1,022
star
9

engine.io-client

The engine used in the Socket.IO JavaScript client, which manages the low-level transports such as HTTP long-polling, WebSocket and WebTransport.
JavaScript
738
star
10

socket.io-redis-emitter

The Socket.IO Redis emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process.
TypeScript
712
star
11

engine.io-client-java

Engine.IO Client Library for Java
Java
356
star
12

socket.io-admin-ui

Admin UI for Socket.IO
Vue
322
star
13

socket.io-website

Socket.IO website and blog
JavaScript
311
star
14

engine.io-protocol

Engine.IO protocol
JavaScript
229
star
15

socket.io-adapter

The Socket.IO in-memory adapter
TypeScript
195
star
16

engine.io-server-java

Engine.IO Server Library for Java
Java
161
star
17

socket.io-parser

JavaScript
133
star
18

socket.io-deno

Socket.IO server for Deno
TypeScript
88
star
19

engine.io-parser

Parser for the engine.io protocol, used by client and server
TypeScript
75
star
20

socket.io-msgpack-parser

Socket.IO parser based on msgpack
JavaScript
62
star
21

socket.io-sticky

A simple and performant way to use Socket.IO within a cluster.
JavaScript
37
star
22

socket.io-mongo-adapter

The Socket.IO MongoDB adapter, allowing to broadcast events between several Socket.IO servers
TypeScript
21
star
23

socket.io-chat-platform

A basic chat platform based on Socket.IO
JavaScript
19
star
24

socket.io-postgres-adapter

The Socket.IO Postgres adapter, allowing to broadcast events between several Socket.IO servers
TypeScript
18
star
25

socket.io-redis-streams-adapter

The Socket.IO adapter based on Redis Streams, allowing to broadcast events between several Socket.IO servers.
TypeScript
17
star
26

socket.io-cluster-adapter

The Socket.IO official cluster adapter, allowing to broadcast events between several Socket.IO servers.
TypeScript
14
star
27

socket.io-json-parser

socket.io parser based on JSON.stringify / JSON.parse
JavaScript
12
star
28

socket.io-minimal-example

Socket.IO minimal example
HTML
8
star
29

socket.io-benchmarks

Benchmarks for Socket.IO
JavaScript
8
star
30

socket.io-compression-demo

JavaScript
7
star
31

get-started-drawing

7
star
32

lz77-compression-demo

JavaScript
7
star
33

socket.io-mongo-emitter

The Socket.IO MongoDB emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process
TypeScript
6
star
34

socket.io-postgres-emitter

The Socket.IO Postgres emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process.
TypeScript
5
star
35

socket.io-echo-server

Socket.IO echo server
JavaScript
4
star
36

socket.io-sample-playbook

This repository contains an Ansible playbook to set up a basic Socket.IO application.
JavaScript
4
star
37

tap-to-android

3
star
38

socket.io-swift-fiddle

Swift
2
star
39

socket.io-browsers

A reusable list of browsers to test when using defunctzombie/zuul
JavaScript
1
star