Introduction

Ultra fast Websocket Server with rich API for creating real-time, complex applications.
The library is made for NodeJS and according to RFC6455 Standard for websocket version 13.
Very clean code with straightforward logic and no npm dependencies.

Features

  • RFC6455, websocket v.13
  • NodeJS v10+
  • no npm dependencies
  • small, medium and large messages (up to 9,007,199,254 gigabytes or ~9 quadrillion bytes)
  • internal HTTP server
  • socket (client) authentication
  • limit total number of the connected clients
  • limit the number of connected clients per IP
  • rooms (grouped websocket clients)
  • ping & pong supported
  • Regoch-Router integration (build complex, real-time API)
  • easy RxJS integration

Installation

Install the server in your project via npm:


  $ npm install --save regoch-websocket
  


Development

When developing the Regoch WS Server start the server with the command:


  ## start the test server
  $ nodemon examples/server/000dev.js
  

Exports

When npm is used there are several exported classes and libraries:


  const { RWServer, RWHttpServer, lib } = require('regoch-websocket');

  /*
    lib
    -------
    {
      subprotocol,
      raw,
      jsonRWS,
      websocket13,
      helper,
      getMessageSize,
      getMessageSizeFromBlob,
      StringExt
    }

   */
  

Quick Start

A short example which shows the power of the Regoch Websocket Server.

/**
 * An example with the built-in HTTP server.
 */
const { RWServer, RWHttpServer, lib } = require('regoch-websocket');
const helper = lib.helper;

const Router = require('regoch-router');
const router = new Router({debug: false});


// start internal HTTP server
const httpOpts = {
  port: 3211,
  timeout: 0 // if 0 the socket connection will never timeout
};
const rwHttpServer = new RWHttpServer(httpOpts);
const httpServer = rwHttpServer.start(); // nodeJS HTTP server instance
setTimeout(() => {
  // rwsHttpServer.restart();
  // rwsHttpServer.stop();
}, 3400);



// websocket ultra
const wsOpts = {
  timeout: 5*60*1000,
  maxConns: 5,
  maxIPConns: 3,
  storage: 'memory',
  subprotocol: 'jsonRWS',
  tightening: 100,
  version: 13,
  debug: false
};
const rws = new RWServer(wsOpts);
rws.socketStorage.init(null);
rws.bootup(httpServer);



/*** socket stream ***/
rws.on('connection', async socket => {
  /* send message back to the client */
  const msgWelcome = 'New connection from socketID ' + socket.extension.id;
  // socket.extension.sendSelf({id: helper.generateID(), from: 0, cmd: 'info', payload: msgWelcome});

  // rws.dataTransfer.send(msgWelcome, socket);

  /* authenticate the socket */
  const authkey = 'TRTmrt'; // can be fetched from the database, usually 'users' table
  socket.extension.authenticate(authkey); // authenticate the socket: compare authkey with the sent authkey in the client request URL ws://localhost:3211/something?authkey=TRTmrt

  helper.sleep(1300);

  /* socketStorage test */
  // await new Promise(resolve => setTimeout(resolve, 5500));
  // const socketFound = rws.socketStorage.findOne({ip: '::1'});
  // if (!!socketFound && socketFound.extension) console.log('found socket.extension::', socketFound.extension);

});




/*** message stream ***/
rws.on('message', (msg, msgSTR, msgBUF, socket) => {
  // console.log('\nreceived message SUBPROTOCOL::', msg); // after subprotocol
  console.log('\nreceived message STRING::', msgSTR); // after DataParser
  // console.log('\nreceived message BUFFER::', msgBUF); // incoming buffer
  // console.log('\nsocketID', socket.extension.id);
  // rws.dataTransfer.sendOne(msg, socket); // return message back to the sender
});



/*** route stream ***/
rws.on('route', (msgObj, socket, dataTransfer, socketStorage, eventEmitter) => { // msgObj:: {id, from, to, cmd, payload: {uri, body}}
  console.log('routeStream::', msgObj);
  const payload = msgObj.payload;

  // router transitional variable
  router.trx = {
    uri: payload.uri,
    body: payload.body,
    msgObj,
    socket,
    dataTransfer: rws.dataTransfer
  };


  // route definitions
  router.def('/shop/login', (trx) => { console.log('trx::', trx.uri); });
  router.def('/shop/product/:id', (trx) => { console.log('trx.uri::', trx.uri, '\ntrx.query::', trx.query, '\ntrx.params::', trx.params); });
  router.def('/send/me/back', (trx) => {
    const id = trx.msgObj.id;
    const from = 0;
    const to = trx.msgObj.from;
    const cmd = 'route';
    const payload = {uri: '/returned/back/21', body: {x: 'something', y: 28}};
    const msg = {id, from, to, cmd, payload};
    rws.dataTransfer.sendOne(msg, trx.socket);
  }); // send new route back to the client
  router.notfound((trx) => { console.log(`The URI not found: ${trx.uri}`); });

  // execute the router
  router.exe().catch(err => {
    console.log(err);
    rws.dataTransfer.sendOne({cmd: 'error', payload: err.stack}, socket);
  });

});
    

License

The software is published under MIT License.
The MIT License

Copyright (C) 2021  Saša Mikodanić http://www.regoch.org

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to
whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

RFC6455

The standard is described at the offical RFC6455 Standard page. Here we bring schemas which will help to understand the section 5.2. Data Framing what is essential in the Regoch Websocket Server

Basic postulates


1) The mask is defined in the first bit of the second byte. When messages are sent from the client mask = 1 and masking bytes exist. Masking bytes always have 4 bytes length. If the message is sent from the server to client there's no masking bytes and the mask = 0.
2) The payload length in the second byte is the most important for defining payload data length.
- if the payload length defined in the second byte is <126 then there's no extension to define payload length
- if the payload length defined in the second byte is =126   =0b1111110   =0x7E then there are 2 bytes (16 bits) extension to define payload length
- if the payload length defined in the second byte is =127   =0b1111111   =0x7F then there are 8 bytes (64 bits) extension to define payload length

· Sending messages from client to server

Small Messages (< 126 B), Medium Messages (= 126 B), Large Messages (= 127 B)

RFC6455 diagram 1

· Sending messages from server to client

Small Messages (< 126 B), Medium Messages (= 126 B), Large Messages (= 127 B)

RFC6455 diagram 2

class RWServer

The most important class which defines methods to start the websocket server and listen the events.

Properties

RWS

PropertyDescriptionTypeDefault
wsOptswebsocket server options (see the table below)object
serverHTTP server instanceobject
dataTransferdata transfer instanceobject
socketStoragesocket storage instanceobject


wsOpts

PropertyDescriptionTypeDefault
timeoutClose socket after ms inactivity. If 0 never close. Default: 5 minnumber5*60*1000
allowHalfOpenIf false close socket if it's readyState is writeOnly or readOnly,
for example when NginX timeout socket on the client side [Client -X- NginX --- WSServer(NodeJS)]
booleanfalse
maxConnsLimit connections to the servernumber10000
maxIPConnsLimit connections from the same IP address. If 0 then infinite.number1
storageSocket storage type: memory, redis, mongodb, ...stringmemory
subprotocolProtocol used internally in the server and client: jsonRWS, rawstringjsonRWS
tighteningDelays in the server process execution (miliseconds)number400
versionWebsocket versionnumber13
debugDebug incoming and outgoing messagesbooleanfalse

Methods

Use this methods to start the websocket server and listen its events.

  • constructor (wsOpts :object) :void

    Create the RWS class instance.

    
    const { RWServer } = require('regoch-websocket');
    const wsOpts = {
      timeout: 5*60*1000, // close the socket if the client didn't send any message in this period of miliseconds
      maxConns: 5,
      maxIPConns: 3,
      storage: 'memory',
      subprotocol: 'jsonRWS',
      tightening: 100,
      version: 13,
      debug: false
    };
    const rws = new RWServer(wsOpts);
          

  • bootup (httpServer :HTTPServer) :void

    Start the websocket server on the top of the HTTP server.
    httpServer - instance of the NodeJS HTTP Server

  • on (eventName :string, listener :Function) :void

    Listen the websocket events: 'connection', 'message', 'route'
    eventName - event name: 'connection', 'message', 'route'
    listener - callback function

Events

There are several events used in the client:
  1. connection - emitted when the client is succesfuly connected to the server
  2. message - emitted when client sent the valid message (valid means validated by the subprotocol)
  3. message-error - emitted when client sent invalid message (invalid means if the message doesn't conform the subprotocol rules)
  4. route - emitted when client sent the message with cmd: 'route' from the server. This is used to separate the route messages (messages useful for regoch-router) from other ordinary messages./li>
  • on ('connection', (socket :Socket) => { ... }) :void

    When client is connected to the websocket server.
    socket - net socket https://nodejs.org/api/net.html#net_class_net_socket

  • on ('message', (msg :any, msgSTR :string, msgBUF :Buffer, socket :Socket) => { ... }) :void

    When client sent the message to the websocket server.
    msg - the message after subprotocol is applied
    msgSTR - the message converted from buffer to string
    msgBUF - the message in buffer format (originally sent from the client, unmodified, with no parsing)
    socket - connected client, net socket https://nodejs.org/api/net.html#net_class_net_socket

  • on ('message-error', (err :Error) => { ... }) :void

    When server receive the message from the websocket client which is invalid (usually not valid in the subprotocol). Useful to catch the invalid messages sent from the client.
    err - error which caused invalid message

  • on ('route', (msg, socket, dataTransfer, socketStorage, eventEmitter) => { ... }) :void

    When client sent message with the route command. For example: {id: 210129163129492000, from: 210129163129492111, to: 0, cmd: 'route', payload: {uri: 'shop/login', body: {username:'mark', password:'thG5$#w'}}}
    msg - the message after subprotocol is applied
    socket - connected client, net socket https://nodejs.org/api/net.html#net_class_net_socket
    dataTransfer - DataTransfer instance
    socketStorage - SocketStorage instance
    eventEmitter - NodeJS EventEmitter instance

class RWHttpServer

The internal HTTP server. Simple creates a HTTP server instance on which websocket server will be attached.

Properties

httpOpts

PropertyDescriptionTypeDefault
portHTTP server port numbernumber3000
timeoutms of inactivity after ws will be closed. If 0 then the ws will never close. Default is 5 minutes.number5*60*1000

Methods

Use this methods to start, stop or restart the HTTP server.

  • constructor (httpOpts :object) :void

    Create the RWSHttpServer class instance.

    
    const { RWHttpServer } = require('regoch-websocket');
    const httpOpts = {
      port: 3211,
      timeout: 0 // if 0 the socket connection will never timeout
    };
    const rwHttpServer = new RWHttpServer(httpOpts);
    const httpServer = rwHttpServer.start(); // nodeJS HTTP server instance
    setTimeout(() => {
      rwHttpServer.restart();
      // rwHttpServer.stop();
    }, 3400);
          

  • start () :httpServer

    Start the HTTP server. Creates and return NodeJS HTTP Server instance.

  • async stop () :Promise<void>

    Stop the HTTP server.

  • async restart () :Promise<void>

    Restart the HTTP server.

helper

instance of the class Helper

The auxilary helper methods.

Properties

No class properties.

Methods

Use this methods as additional helpers.

  • generateID () :number

    Create unique id. It's combination of timestamp and random number 'r'in format:
    YYMMDDHHmmssSSSrrr → YY year, MM month, DD day, HH hour, mm min, ss sec, SSS ms, rrr 3 random digits
    18 digits in total, for example: 210129163129492100

  • nowTime () :string

    Gives now time in nice format -> Friday, 1/29/2021, 16:31:29.801.

  • async sleep (ms :number) :Promise<void>

    Pause the code execution in miliseconds.

  • printBuffer (buff :Buffer) :void

    Print all buffer values as string. For example: 81 7e 00 8b 7b 22 69 64 22 3a 32 31 30 32 31 34 31 30. Useful to debug the buffer messages.

  • tableOfBytes (buff :Buffer, perRow :number) :string

    Print buffer in nice table of bytes. The method returns string with bytes.
    buff - buffer (hex numbers, bytes)
    perRow - how many bytes printed per row (per line)

StringExt

class StringExt

The String extension. Extended string methods.

Properties

No class properties.

Methods

Use this methods to extends original String class.

  • constructor () :void

    Create the StringExt class instance. No need to instantiate because it's already instantiated in the RWServer class.

    
    const { lib } = require('regoch-websocket');
    new lib.StringExt();
    
    console.log('some tekst'.cliBoja('red', 'italic')); // prints red, italic text
    });
          

  • trimall () :void

    Trim empty spaces from left and right and remove tab spaces inside text.

  • clean () :void

    Remove all whitespaces.

  • beautify () :void

    Remove unwanted chars.

  • cliBoja (optFG :string, optSPEC :string) :void

    Print colored text in the terminal.
    optFG - foreground color: black, red, green, yellow, blue, magenta, cyan, white, crimson
    optSPEC - special: bright, bold, italic, underline, revers, hidden, overwritten

subprotocol

The subprotocol is the "languge" which client and server must speak to understand each other. It defines the format of the sent message when it's sent. Currently the Regoch Websocket Server supports:
jsonRWS subprotocol → {id:number, from:number, to:number, cmd:string, payload:any}
raw subprotocol → msg:string

When using 'Regoch-Router' the "jsonRWS" subprotocol have to be implemented.

Handshaking

According to the RFC6455, if the client does not send "Sec-Websocket-Protocol" header then server shouldn't send it in the response. If the client sends "Sec-Websocket-Protocol" the same value must be returned by the server in the "Sec-Websocket-Protocol" HTTP header.

Methods

When creating a new subprotocol all methods must be defined and included in the class. This is like an interface.
Examples: jsonRWS.js , raw.js

  • incoming (msgSTR :string) :any

    Execute the subprotocol for incoming messages. Filter and map incoming messages.
    msgSTR - the message string converted from the buffer format

  • outgoing (msg :any) :string

    Execute the subprotocol for outgoing messages. Filter and map outgoing messages.
    msg - the message which is used in the regoch websocket server

  • process () :void

    Process client messages internally, for example execute the route commands.

jsonRWS Subprotocol Rules

Subprotocol name: jsonRWS
* HTTP header: "Sec-WebSocket-Protocol": "jsonRWS"
*
* Subprotocol description:
* This subprotocol is created for communication between websocket server and client.
*
* Subprotocol definitons:
* a) Client have to send message in valid JSON format. Allowed fields: id, from, to, cmd, payload.
* b) Server have to send message in valid JSON format. Allowed fields: id, from, to, cmd, payload.
* c) The message is converted from string to object.

Example


// ---------- SERVER SIDE -----------
const wsOpts = {
  timeout: 5*60*1000,
  maxConns: 5,
  maxIPConns: 3,
  storage: 'memory',
  subprotocol: 'jsonRWS', // subprotocol supported by the server
  tightening: 100,
  version: 13,
  debug: false
};
const rws = new RWServer(wsOpts);
rws.socketStorage.init(null);
rws.bootup(httpServer);


// ---------- CLIENT SIDE ------------
class TestClient extends window.regoch.Client13jsonRWS {
  constructor(wcOpts) {
    super(wcOpts);
  }

  async connectMe() {
    const wsocket = await this.connect();
    console.log('+++Connected', wsocket);
    this.messageReceiver();
  }
}

const wcOpts = {
  wsURL: 'ws://localhost:3211?authkey=TRTmrt',
  timeout: 3*1000, // wait 3secs for answer
  reconnectAttempts: 5, // try to reconnect 5 times
  reconnectDelay: 3000, // delay between reconnections is 3 seconds
  subprotocols: ['jsonRWS'], // tells server which subprotocols can the client handle
  debug: true
};

const testCB = new TestClient(wcOpts);
  


1) Both client and server must support same subprotocol, otherwise the error on the server side will be thrown:
Error: None of the requested subprotocols "XYZ" is supported by the server.
2) If raw subprotocol is used then Regoch Websocket Server can not use the Regoch Router.

DataTransfer

class DataTransfer

Data transfer according to RFC6455.

Properties

DataTransfer

PropertyDescriptionTypeDefault
wsOptswebsocket server options (injected from the RWS class)object
socketStoragesocket storage libraryobject
subprotocolLibsubprotocol libraryobject
eventEmitterNodeJS EventEmitter instanceobject

Methods

Use this methods to send messages from the websocket server to one or more clients or to a room.

  • async sendOne (msg :any, socket :Socket) :Promise<void>

    Send message to one client (socket).
    msg - message which will be sent to the client
    socket - the client receiver

  • async send (msg :any, sockets :Socket[]) :Promise<void>

    Send message to one or more clients (sockets).
    msg - message which will be sent to the client(s)
    sockets - array of the clients receivers

  • async broadcast (msg :any, socketSender :Socket) :Promise<void>

    Send message to all clients excluding the client who sent the message.
    msg - message which will be sent to the clients
    socketSender - the client sender (prevent sending message back to the sender)

  • async sendAll (msg :any) :Promise<void>

    Send message to all clients including the client who sent the message.
    msg - message which will be sent to the clients

  • async sendRoom (msg :any, socketSender :Socket, roomName :string) :Promise<void>

    Send message to all clients in the specific room excluding the client who sent the message.
    msg - message which will be sent to the clients
    socketSender - client which is sending message
    roomname - the room name

  • async sendError (err :Error, socket :Socket) :Promise<void>

    Send error message to one client (socket) in format {id, from, to, cmd, payload}, where payload is the error.message and cmd:'error'.
    err - the error object
    socket - client receiver

  • async sendID (socket :Socket) :Promise<void>

    Send socket ID back to the client.
    socket - client receiver

jsonRWS Subprotocol Specifics

If jsonRWS subprotocol is used for the communication between client and server the msg in above methods must have:
- allowed fields: 'id', 'from', 'to', 'cmd', 'payload'
- required fields: 'id', 'from', 'to', 'cmd'
for example:


const msg1 = {id:210407145416438530, from:210407145413970240, to:210407145413970240, cmd:'info/socket/id', payload:210407145413970240};
await rws.dataTransfer.sendOne(msg1, socket);
// or just use await rws.dataTransfer.sendID(socket);

const msg2 = {id:2104071455295653, from:210407145413970240, to:0, cmd:'room/enter', payload: 'Entered in the room "sasa"'};
await rws.dataTransfer.sendOne(msg2, socket);

const msg3 = {id:2104071455295653, from:210407145413970240, to:0, cmd:'route', payload: {uri: '/confirmed-email'}};
await rws.dataTransfer.sendOne(msg3, socket);
  



A list of predefined commands (cmd)

No need to use these commands directly, use above methods instead.
socket → 'socket/sendone', 'socket/send', 'socket/broadcast', 'socket/sendall', 'socket/nick'
room → 'room/enter', 'room/exit', 'room/exitall', 'room/send'
info → 'info/socket/id', 'info/socket/list', 'info/room/list', 'info/room/listmy'
route → 'route'

Example

This class is instantiated in the RWServer class so no need to use new DataTransfer().


const { RWServer } = require('regoch-websocket');

const rws = new RWServer(wsOpts);
rws.socketStorage.init(null);
rws.bootup(httpServer);

rws.on('message', (msg, msgSTR, msgBUF, socket)) => {
  rws.dataTransfer.sendOne(msg, socket); // return message back to sender
});
  

SocketExtension

class SocketExtension

This class has several roles:
- adds extension property to the existing Net Socket properties
- listen for the socket events: 'storage-add', 'timeout', 'error', 'close'
- Change the socket timeout. Timeout is the time of inactivity after which the socket will be closed.
- Authenticate a websocket. Do not allow a client (socket) connection if the authentication failed.

Extended Socket Properties & Methods

socket.extension properties

PropertyDescriptionTypeDefault
id18 digits client socket ID (if id=0 it's a server)number
nickclient nick namestring
ipclient IP addressstring
portclient portnumber
timeconnection time: 2020-11-15T14:41:48.479Z (GMT - Greenwich Meridian Time)string
wsOptswebsocket server optionsobject
userAgentclient user agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36string
authenticatedis the web socket authenticated with authenticate(authKey) methodstring
urlrequested URL: /something?authkey=TRTmrtstring
originorigin header valuestring
urlQueryquery string converted to object: /something?authkey=TRTmrt → {authkey: 'TRTmrt'}string


socket.extension methods

  • changeTimeout () :void

    Change the socket timeout to timeout defined by the wsOpts.timeout.
    Timeout is the time of inactivity after which the socket will be closed.

  • authenticate (authkey :string) :void

    Authenticate a websocket. Do not allow a client (socket) connection if the authentication failed. Compares authkey from the URL query with the authkey in the function argument and set "socket.extension.authenticated" to true or false.

  • addSocket () :void

    Add the socket to the socket storage.

  • removeSocket () :void

    Remove the socket from the socket storage.

  • exitAllRooms () :void

    Exit from the all rooms.

  • sendSelf (msg :any) :void

    Selfsending, send the message to the socket.

Methods

the class SocketExtension methods.

  • changeTimeout () :void

    Change the socket timeout to timeout defined by the wsOpts.timeout.
    Timeout is the time of inactivity after which the socket will be closed.

  • authenticate (authkey) :void

    Authenticate a websocket. Do not allow a client (socket) connection if the authentication failed. Compares authkey from the URL query with the authkey in the function argument and set "socket.extension.authenticated" to true or false.

Example

This class is instantiated in the RWS class so no need to use new DataTransfer().


const { RWServer } = require('regoch-websocket');

const rws = new RWServer(wsOpts);
rws.socketStorage.init(null);
rws.bootup(httpServer);

rws.on('socket', (socket)) => {
  console.log(socket.extension.id, socket.extension.nick, socket.extension.ip, socket.extension.port);
  socket.extension.authenticate(authkey);
});
  

storage

instance if the class Storage

The class defines methods to save, remove, list, find, count, ... sockets in different storage environment:
- 'memory' (RAM) → socketStorageMemory
- 'redis' (Redis DB) → socketStorageRedis
- 'mongo' (Mongo DB) → socketStorageMongo
- 'mysql' (Mysql DB) → socketStorageMysql

Properties

PropertyDescriptionTypeDefault
storageOptsstorage options specific for the certain storage, for example database credentialsobject

Methods

When creating a new storage class all methods must be defined. This is like an interface.

    SOCKET METHODS

  • async count () :Promise<number>

    Get total number of saved sockets.

  • async getAll () :Promise<Socket[]>

    Get all sockets.

  • async add (socket :Socket) :Promise<void>

    Add socket to sockets array which is used when we want to send message (broadcast) to all TCP sockets (TCP clients).

  • async remove (socket :Socket) :Promise<void>

    1. Kill the web socket. Ensures that no more I/O activity happens on this socket
    2. Remove socket from sockets array.
    3. Remove socket from the rooms.

  • async removeByQuery (query :object) :Promise<void>

    First find all sockets by the query, then remove them.

  • async listIDs (sort :'asc'|'desc') :Promise<number[]>

    Get array of all socket IDs. Because socket object is very big and hard to debug.
    sort - sort IDs ascending or descending

  • async find (query :object) :Promise<Socket[]>

    Find an array of sockets by querying the socket.extension object (see SocketExtension.js).
    query - search query object {id, ip, port, time, ... }. The fields are the socket.extension properties.
    Query examples:
    {id: 201117092132387170}
    {id: {$in: [201117092132387170, 201117092132387171, 201117092132387172]}}
    {ip: {$ne: '::1'}}
    {userAgent: {$regex: /moz/i}}

  • async findOne (query :object) :Promise<Socket>

    Find a socket by querying the socket.extension object.

  • async exists (socket :Socket) :Promise<boolean>

    Find out if the socket exists i.e. if the socket is saved in the storage.

  • async setNick (socket :Socket, nickname: string) :Promise<void>

    Set an unique nick name to the socket (client). If the nickname already exists the error will be thrown.

  • async purge (sec :number) :Promise<void>

    Purge disconnected sockets periodically every sec seconds. If sec=0 then purge once.
    Check the readyState for every socket periodically. If the socket is readOnly or writeOnly remove it.
    readyState: opening, open, readOnly, writeOnly -- https://nodejs.org/api/net.html#net_socket_readystate



  • ROOM METHODS

  • roomEnter (socket :Socket, roomName: string) :void

    Add socket in the existing room. If the room doesn't exist create a new room with the socket.

  • roomExit (socket :Socket, roomName: string) :void

    Remove socket from the room. If the room doesn't have any sockets remove the room.

  • roomExitAll (socket :Socket) :void

    Remove socket from all rooms. For example in case that socket is closed.

  • async roomList () :Promise<[{name:string, socketIds:number[]}]>

    List all the rooms. The returned value is array of objects {name:string, socketIds:number[]} where name is the room name and socketIds are socket ids in the room.

  • async roomListOf (socket :Socket) :Promise<[{name:string, socketIds:number[]}]>

    List all the rooms where is the specific client.

  • async roomFindOne (roomName :string) :Promise<{name:string, socketIds:number[]}>

    Find a room by room name.

Example

This class is instantiated in the RWS class so no need to use new DataTransfer().


const { RWServer } = require('regoch-websocket');

const rws = new RWServer(wsOpts);
rws.socketStorage.init(null);
rws.bootup(httpServer);

rws.on('socket', async socket => {
  const tot = await rws.socketStorage.count();
  console.log('The total number of connected clients:', tot);

  rws.socketStorage.roomEnter(socket, 'tech-chat');
});
  

Regoch Router

The "regoch-router" can be applied to separate code nicely and to build powerful, real-time API. It supports URL parameters and URL query string. The requirement is that server sent valid "jsonRWS" message with the cmd: 'route'.
Example: 000dev.js