top of page

#10 Reading the Socket.io documentation

  • Aaron
  • Nov 30, 2020
  • 5 min read

Updated: Dec 2, 2020

Because I felt a bit confused and lost, I decided to read through the socket.io documentation.


I have a summary of what I believe are the most important parts, or that I may need for my project. Remember that while I was writing this article a new version of socket.io (v3) was only recently released, so this documentation is pretty new and might not be as established as the previous version(v2).


You will find my thoughts at the end of this article.

Socket.io documentation


Server:

Middleware

// I should look up some examples, might not need it except for authentication

A middleware function is a function that gets executed for every incoming connection.

Middleware functions can be useful for:

  • logging

  • authentication / authorization

  • rate-limiting

Note: this function will be executed only once per connection (even if the connection consists in multiple HTTP requests).



Namespaces

A Namespace is a communication channel that allows you to split the logic of your application over a single shared connection.


Default namespace


We call the default namespace / and it’s the one Socket.IO clients connect to by default, and the one the server listens to by default.

This namespace is identified by io.sockets or simply io


Custom namespaces

To set up a custom namespace, you can call the of function on the server-side:

const nsp = io.of('/my-namespace');
nsp.on('connection', socket => {
  console.log('someone connected');
});
nsp.emit('hi', 'everyone!');

Client-side:

const socket = io('/my-namespace');

Important note: The namespace is an implementation detail of the Socket.IO protocol, and is not related to the actual URL of the underlying transport, which defaults to /socket.io/…


Namespace middleware

A middleware is a function that gets executed for every incoming Socket, and receives as parameters the socket and a function to optionally defer execution to the next registered middleware. A Socket.IO middleware is very similar to what you can find in Express.


Handling CORS New in v3

Since Socket.IO v3, you need to explicitly enable Cross-Origin Resource Sharing (CORS).

Client

Initialization


From the same domain

const socket = io();

From a different domain

const socket = io('https://server-domain.com');

Custom namespace

In the examples above, the client will connect to the default namespace. But you can also specify the namespace:

const socket = io('https://server-domain.com/admin'); 
// or io(‘/admin’) if same origin

Transports option

By default, the client will try to establish a WebSocket connection, and fall back to XHR/JSONP polling. If you are sure the WebSocket connection will succeed, you can disable the polling transport. In that case, due to the nature of the WebSocket connection, you can have several server instances without sticky sessions.


Socket instance

socket.id, socket.rooms


Adding attributes

// You can, for example, create socket.username

Extra info events:

socket.on("disconnect",(reason)

has disconnect reason info



Events

Basic emit

you can emit events on one side and register listeners on the other

socket.on / socket.emit


Acknowledgements

Events are great, but in some cases, you may want a more classic request-response API. In Socket.IO, this feature is named acknowledgments.


Volatile events New in V3

Volatile events are events that will not be sent if the underlying connection is not ready (a bit like UDP, in terms of reliability).

This can be interesting for example if you need to send the position of the characters in an online game (as only the latest values are useful).


Listening to events


socket.on(eventName, listener)

Adds the listener function to the end of the listeners array for the event named eventName.


socket.once(eventName, listener)

Adds a one-time listener function for the event named eventName


socket.off(eventName, listener)

Removes the specified listener from the listener array for the event named eventName.


socket.removeAllListeners([eventName])

Removes all listeners, or those of the specified eventName.


Catch-all listeners New in v3

Since Socket.IO v3, a new API allows to declare catch-all listeners. (available on both the client and the server)

socket.onAny(listener), socket.prependAny(listener), socket.offAny([listener])

Validation

The validation of the event arguments is out of the scope of the Socket.IO library. Use an external validation package like joi, ajv or validatorjs.


Error handling

There is currently no built-in error handling in the Socket.IO library, which means you must catch any error that could be thrown in a listener.


Broadcasting events

Send events to all the connected clients.

Broadcasting is a server-only feature.

To all connected clients: io.emit(),

To all connected clients except the sender: socket.broadcast.emit()

Broadcasting also works with multiple Socket.IO servers.


Rooms

A room is an arbitrary channel that sockets can join and leave. It can be used to broadcast events to a subset of clients

rooms are a server-only concept (i.e. the client does not have access to the list of rooms it has joined).


Joining and leaving

You can call join to subscribe the socket to a given channel

io.on('connection', socket => {
  socket.join('some room');
});


And then simply use to or in (they are the same) when broadcasting or emitting:

io.to('some room').emit('some event');

You can emit to several rooms at the same time,

just chain .to:

io.to(“room1”).to(“room2”).to(“room3”).emit

Every socket that is at least in one of the rooms will get the event once (even if the socket is in two or more rooms).


You can also broadcast to a room from a given socket

io.on('connection', function(socket){
  socket.to('some room').emit('some event');
});

To leave a channel you call leave in the same fashion as join.


Default room

Each Socket in Socket.IO is identified by a random, unguessable, unique identifier Socket#id. For your convenience, each socket automatically joins a room identified by its own id.


Sample use cases

// I don’t quite understand these examples yet

broadcast data to each device/tab of a given user

io.on('connection', async (socket) => {
  const userId = await fetchUserId(socket);
  socket.join(userId);
  // and then later
  io.to(userId).emit('hi');
});

send notifications about a given entity

io.on('connection', async (socket) => {
  const projects = await fetchProjects(socket);
  projects.forEach(project => socket.join('project:' + project.id));
  // and then later
  io.to('project:4321').emit('project updated');
});

Disconnection

Upon disconnection, sockets leave all the channels they were part of automatically, and no special teardown is needed on your part. You can fetch the rooms the Socket was in by listening to the disconnecting event


Emit cheatsheet

This cheatsheet is particularly useful, I recommend checking this out. https://socket.io/docs/v3/emit-cheatsheet/


I also found a blog post about a case in which you don't want to send info back to the sender: Blog post about the difference between socket.broadcast.to().emit() & io.to().emit


What's new in v3



End of documentation


Thoughts

While reading this documentation, I realized I didn't really know the exact difference between socket & io, therefore I did a quick google search:


socket vs io

  • io is a Socket.IO server instance attached to an instance of http.Server listening for incoming events.

  • The socket argument of the connection event listener callback function is an object that represents an incoming socket connection from a client.

  • Both can have .on

The io variable represents the group of sockets. The code you have starts on line one with providing a function in the second parameter that gives you a socket variable every time a new connection is made. The socket variable is only for communicating with each individual connection. You may not see it in the code but there will be one socket variable for each connection established.


What I think about socket vs io:

If you look at the emit cheatsheet, you will probably notice that io sends to everyone and socket is for a more targeted emit, and usually dàes not send to the sender.

  • On your server: you use io.on("connection", socket =>{}) as a wrapper for your socket.on listeners. io.on(event) doesn't work inside that, use socket.on(event) inside instead.

  • On the client side: if you use io.emit instead of socket.emit you will get an error. It seems that io.whateverFunction is not supported on the client and you should never use it there.

In short, I think that:

  • you should not use io.on inside a io.on("connection", ...) on the server.

  • You should use io.emit on your server in certain cases which are documented in the emit cheatsheet.

  • you should never use io.emit or io.on in your client, use socket instead.


Having now read the documentation, I feel it sometimes lacks real implementation examples. I definitely would've liked to see more practical info or examples of implementation about rooms.

Comments


  • iconmonstr-github-1-240

©2020

bottom of page