Introduction to Socket.IO

Socket.IO enables real-time bidirectional event-based communication.
It works on every platform, browser or device, focusing equally on reliability and speed.

namspace, room, and socket

There are three basic concepts in socket.io:
namespace, room, and socket.

A client connects to a server to establish a bidirectional connection. The connection is represented as a socket, which is organized by namespaces.

A namespace is a URL path. For example, you can have two namespaces ("/foo" and "/bar/foo") with following two URLs:

http://localhost/foo
http://localhost/bar/foo

By default, the namespace is "/". You can use io.of("your namespace") to get a new namespace.

socket.io

Each namespace could have multiple rooms. When a socket is created, it creates and joins a "special" room, named with its socket id (socket.id).

The socket can join other rooms by calling socket.join('room name').

Basic operations

Instances used in example:

socket - represents a socket instance (client or server side)

io - represents the default name space ("/"), you can use a different name space with io.of("namespace")

To send a message: (same on client and server side)

socket.emit("message name", msg)

To send message to all the clients in a room (server side):

io.in("the room").emit("message name", msg)

To send message to all the clients in a namesapce (server side):

io.of("the namespace").emit("message name", msg)

Another way to send a message to a single client is to send the message to the "socket id" room, as each socket has a special room, whose name is the socket id.

io.in("the socket id").emit("message name", msg)

Create a socket.io server with express.js

const path = require("path");
const express = require('express');
const app = express();

const server = app.listen(3000, function () {
  console.log('web server listening on port 3000!');
});

// setup socket.io server with express server
const io = require('socket.io')(server);

// when new client is connected to "/demo" namespace
io.of("/demo").on('connection', (socket) => {
  // socket represents the connection between the server and the client
  console.log('a user connected');
  
  // when the client is disconnected
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

Create a client

var socket = io('http://localhost:3000/demo');
socket.on('connect', ()=> {
  console.log("connected to server");
});

On the client side, we create a socket by specifying the server url and namespace (in this example, http://localhost:3000/demo).

When the connection is established, server emits a 'connect' event, the client prints a 'connected to server' message on the console.

Authentication

In the basic server implementation, any one can connect to the server. In a real application, you probably want to only allow authorized client to connect.

In order to authenticate the client, the client need to provide its credential after the connection is established. This could be done by sending an 'authenticate' message when connected. The server verifies the credential, and disconnects the client if not valid.

A read-to-use library socketio-auth module can be found on npm. See following code:

const path = require("path");
const express = require('express');
const app = express();

const server = app.listen(3000, function () {
  console.log('web server listening on port 3000!');
});

const io = require('socket.io')(server);

const socketioAuth = require('socketio-auth');

socketioAuth(io, {
  authenticate : function(socket, msg, callback) {
    var authenticated = true;
    
    var credential = msg.credential;
    
    // verify the credentials

    callback(null, authenticated);
  },

  postAuthenticate: function(socket, msg) {
    // client is authenticated
  },

  timeout : 1000, // disconnect the socket if 'authenticate' message is not received in 1 second after connected
});

You need to implement the 'authenticate' callback to verify the credential. Once the credential is verified, 'postAuthenticate' callback will be called.

On the client side, send the 'authentication' message with your credential once the connection is established:

var socket = io();
socket.on('connect', ()=> {
  // you can use any credential here, ex. token, password ...
  socket.emit("authentication", { clientId : clientId, password : password });
});

socket.on('unauthorized', function(err) {
  console.error(err.message);
})

When the credential verification failed on server side, an 'unauthorized' event will be fired.