Introduction

Websocket client for NodeJS platform. The client works best with the Regoch Websocket Server. Small, optimised and very powerful library made according to RFC6455 Standard for websocket version 13.

Features

  • RFC6455, websocket v.13
  • supported subprotocols: jsonRWS, raw
  • small, medium and large messages (up to 9,007,199,254 gigabytes or ~9 quadrillion bytes)
  • ping & pong
  • questions - send request to server and receive response (simmilar to HTTP request but on the websocket TCP level)
  • rooms - send group message to a subscribed clients
  • small file size, minified (*~7.5kB only*)
  • powerful API which saves your development time
  • easy integration with RxJS

Installation

Install the websocket client in your project via npm:

$ npm install --save regoch-websocket


How to use

A short example how to use NodeJS Websocket Client after installation:

const { RWClientNodejs, lib } = require('regoch-websocket');
const helper = lib.helper;

class TestClient extends RWClientNodejs {
  constructor(wcOpts) {
    super(wcOpts);
  }
}

const main = async () => {
  // connect to websocket server
  const wcOpts = {
    wsURL: 'ws://localhost:3211?authkey=TRTmrt',
    questionTimeout: 3*1000, // wait 3 seconds for answer
    reconnectAttempts: 5, // try to reconnect 5 times
    reconnectDelay: 3000, // delay between reconnections is 3 seconds
    subprotocols: ['jsonRWS', 'raw'], // ask server if it supports "jsonRWS" or "raw" subprotocol
    debug: false // print debug messages (useful for development)
  };
  const testClient = new TestClient(wcOpts);
  await testClient.connect();

  await helper.sleep(1300);

  console.log('message sent');
  await testClient.sendOne(210726155519554340, 'some message');
};


main().catch(err => console.log(err));
  

Exports in the npm package


module.exports = {
  RWServer,
  RWHttpServer,
  RWClientNodejs,
  RWClientBrowser,
  lib
};
  

TCPDUMP

Use tcpdump command to debug the messages sent from the server to the client.
For example sudo tcpdump -i any port 57190 -X -s0 where 57190 is the client port i.e. socket.localPort

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.

class RWClientNodejs

The websocket client properties:
- websocket version 13
- subprotocol: jsonRWS (Regoch Router can be used on the client side)
- current file: Client13jsonRWS.js

Subprotocol jsonRWS the message fomat {id :number, from :number, to :number, cmd :string, payload :any}

Properties

RWClientNodejs

PropertyDescriptionTypeDefault
wcOptswebsocket client options (see the table below)object
socketTCP Socket object https://nodejs.org/api/net.html#net_class_net_socketobject
socketIDsocket id (18 digits number)number
attemptreconnect attempt counternumber1
resHeadersonUpgrade response headersobject


wcOpts

PropertyDescriptionTypeDefault
wsURLwebsocket URL: ws://localhost:3211/something?authkey=TRTmrtstring
questionTimeoutTimeout in ms when question to the server is sent and answer is not receivednumber
reconnectAttemptsthe number of recconnection attemptsnumber
reconnectDelaydelay in ms between two recconnection attemptsnumber
subprotocolswebsocket subprotocols: ['jsonRWS', 'raw']string[]
debugdebug incoming and outgoing messagesbooleanfalse

Methods

Use this methods to connect the NodeJS websocket client to the websocket server and to send the messages from the client to server and the other clients.

  • constructor (wcOpts :object) :void

    Create the Client13jsonRWS class instance.

    
    const { RWClientNodejs, lib } = require('regoch-websocket');
    const wcOpts = {
      wsURL: 'ws://localhost:3211?authkey=TRTmrt',
      questionTimeout: 3*1000, // wait 3secs for answer
      reconnectAttempts: 5, // try to reconnect 5 times
      reconnectDelay: 3000, // delay between reconnections is 3 seconds
      subprotocols: ['jsonRWS'],
      debug: false
    };
    const client = new RWClientNodejs(wcOpts);
    const wsocket = await client.connect();
          



  • Connectors

    Connect/disconnect/reconnect the websocket client (application) to websocket server.
  • connect () :Promise<Socket>

    Connect to the websocket server via wsURL. Returned value is the promise with the resolved TCP Socket object.
    Example: 001connect.js

  • disconnect () :void

    Disconnect from the server by sending the "close" websocket frame which contains opcode 0x8.
    Example: 002disconnect.js

  • async reconnect () :Promise<void>

    Try to reconnect the client when the socket is closed i.e. when websocket server is restarted and opcode 0x8 "closed-by-server event" is received. This method is fired on every 'close' socket's event automatically and no need to use it in the application code.
    Example: 003reconnect.js

  • blockReconnect () :void

    Block reconnect usually after disconnect() method is used. This method is used internally and no need to use it in your application.



  • Receivers

    Recive the messages sent from server to client.
  • onMessage () :void

    Receive the message as buffer and convert it in the appropriate subprotocol format. Dispatch it as 'route', 'question', 'message', 'message-error' or 'error' event to the eventEmitter.
    This method is used internally and it's not recommended to use in your app. Use on() listeners instead.



  • Senders

    Send a message in one direction, from client to server.
  • async carryOut (to :number, cmd :string, payload :any) :Promise<void>

    Send message to the websocket server.
    This method is used internally and it's not recommended to use in your app.
    to the receiver socket id
    cmd the command in the message object {id, from, to, cmd, payload}
    payload the payload in the message object {id, from, to, cmd, payload}

  • async sendOne (to :number, msg :any) :Promise<void>

    Send message (payload) to one client.
    to the receiver socket id: 210205081923171300
    msg the payload in the message object {id, from, to, cmd, payload}
    Example A: 021sendOne.js
    Example B: 021sendOne_consecutive.js

  • async send (to :number[], msg :any) :Promise<void>

    Send message (payload) to one or more clients.
    to array of the receiver socket ids: [210205081923171300, 210205082042463230]
    msg the payload in the message object {id, from, to, cmd, payload}
    Example: 022send.js

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

    Send message (payload) to all clients except the sender.
    msg the payload in the message object {id, from, to, cmd, payload}
    Example: 023broadcast.js

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

    Send message (payload) to all clients and the sender.
    msg the payload in the message object {id, from, to, cmd, payload}
    Example: 024sendAll.js

  • async ping (ms :number, n: number) :Promise<void>

    Send PING to server n times, every ms miliseconds.
    ms sending interval
    n how many times to send ping (if 0 or undefined send infinitely)
    Example: 005send-ping.js

  • async pong () :Promise<void>

    When PING is received from the server send PONG back.
    The PONG is automatically sent as response every time the server sends PING. So there's no need to use this method in the application code.



  • Questions

    Question is when client sends a request to server and wait for the answer. So the data is going in two directions client → server and then server → client. It's simmilar to HTTP request but on the websocket (TCP) level. The returned value is the info message like socket ID, list of socket IDs, room list ...etc.
  • question (cmd :string) :Promise<object>

    Send question and expect the answer. Returned value is the promise with the answered object.
    This method is used internally and it's not recommended to use in your app.
    cmd the command in the message object {id, from, to, cmd, payload}

  • async questionSocketId () :Promise<number>

    Send a question about the client's socket ID (cmd: 'question/socket/id').
    Example: 011questionSocketId.js

  • async questionSocketList () :Promise<number[]>

    Send a question about all socket IDs connected to the server (cmd: 'question/socket/list').
    Example: 012questionSocketList.js

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

    Send a question about all rooms in the server (cmd: 'question/room/list').
    The returned value is object {name, socketIds} where name is the room name and socketIds is the list of sockets joined the room.
    Example: 013questionRoomList.js

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

    Send question about all rooms where the client was entered (cmd: 'question/room/listmy').
    Example: 014questionRoomListmy.js



  • Rooms

    Clients (sockets) can be grouped in the groups called rooms. That way the message can be sent to the group of clients. Receive the room message by the on('message', callback) method.
  • async roomEnter (roomName :string) :Promise<void>

    Subscribe in the room cmd: 'room/enter'.
    roomName the room name: 'tech-chat'
    Example: 031roomEnter.js

  • async roomExit (roomName :string) :Promise<void>

    Unsubscribe in the room cmd: 'room/exit'.
    roomName the room name: 'tech-chat'
    Example: 032roomExit.js

  • async roomExitAll () :Promise<void>

    Unsubscribe from the all rooms cmd: 'room/exitall'.
    Example: 033roomExitAll.js

  • async roomSend (roomName :string, msg :any) :Promise<void>

    Send a message to the room cmd: 'room/send'. The sender doesn't need to be subscribed in the room.
    roomName the room name: 'tech-chat'
    msg the payload in the message object {id, from, to, cmd, payload}
    Example: 034roomSend.js



  • Misc

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

    Set a nick name on the server side socket.extension.nickname cmd: 'socket/nick'.
    nickname nick name
    Example: 041setNick.js

  • async route (uri :string, body :any) :Promise<void>

    Send route command to the server's router cmd: 'route'. Integrate regoch-router on the server side and create complex, real-time Websocket API.
    uri route URI, for example /shop/product/55?x=3 (the payload part in the message object {id, from, to, cmd, payload})
    body body data (the payload part in the message object {id, from, to, cmd, payload})
    Example (client): 042route.js
    Example (server): 000dev.js



  • Listeners

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

    Listen eventName and execute listener on every event.
    eventName event name: 'connected', 'disconnected', 'closed-by-server', 'ping', 'pong', 'message', 'message-error', 'question', 'route', 'error'
    listener the callback function

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

    Listen eventName and execute the listener only once.
    eventName event name: 'connected', 'disconnected', 'closed-by-server', 'ping', 'pong', 'message', 'message-error', 'question', 'route', 'error'
    listener the callback function

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

    Remove the event listener.
    Notice: The listener must be the same function as used in the on().
    eventName event name: 'connected', 'message', 'route', 'error'
    listener the callback function

Events

There are several events used in the client:
  1. connected - emitted by the connect() and if the client is succesfuly connected to the server
  2. disconnected - emitted when the socket is closed
  3. closed-by-server - emitted when server sent OPCODE 0x8 CLOSE (instruction that client should close websocket connection)
  4. ping - emitted when server sent OPCODE 0x9 PING (Client is sending PONG as the answer to server.)
  5. pong - emitted when client received OPCODE 0xA PONG from the server. (Client previously sent PING to server.)
  6. message - emitted when client received the valid message (valid means validated by the subprotocol)
  7. message-error - emitted when client received the invalid message (invalid means if the message doesn't conform the subprotocol rules)
  8. route - emitted when client received 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>
  9. question - emitted when client received the message with cmd: 'question/...' from the server. This message is the response to the sent question.
  10. server-error - emitted when client received the message with cmd: 'error' from the server. This is the case when some error haeppend on the server side and server wants to announce the client. (see Datatransfer::sendError())

How to use listeners

  • on ('connected', (socket: Socket) => { ... }) :void

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

  • on ('closed-by-server', (msgSTR: string) => { ... }) :void

    When client is disconnected by the websocket server.
    msgSTR - the string with value "OPCODE 0x8 CLOSE"

  • on ('ping', (msgSTR: string) => { ... }) :void

    When client received PING request from the server.
    msgSTR - the string with value "OPCODE 0x9 PING"

  • on ('pong', (msgSTR: string) => { ... }) :void

    When client received PONG answer from the server. Previously client sent PING request to the server.
    msgSTR - the string with value "OPCODE 0xA PONG"

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

    When client receive the message from the websocket server which is valid. It includes the all messages except route and questions.
    msg - the message after subprotocol is applied (jsonRWS)
    msgSTR - the message converted from buffer to string
    msgBUF - the message in buffer format (raw bytes)

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

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

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

    When client received message from the server with the route command. For example: {"id":210408084514971400,"from":0,"to":210408084507407140,"cmd":"route","payload":{"uri":"/returned/back/21","body":{"x":"something","y":28}}}
    Useful to separate the messages which can be used in the regoch-router from ordinary messages.
    msg - the message after subprotocol is applied (jsonRWS)
    msgSTR - the message converted from buffer to string
    msgBUF - the message in buffer format (only in regoch-websocket-nodejs)

  • once ('question', (msg :any, msgSTR :string) => { ... }) :void

    Used internally to get the answer. Not recommended to use in the app.

  • on ('server-error', (msg :any, msgSTR :string, msgBUF :Buffer) => { ... }) :void

    When error haeppend on the server-side and it's sent to the client.
    msg - the message after subprotocol is applied (jsonRWS)
    msgSTR - the message converted from buffer to string
    msgBUF - the message in buffer format (raw bytes)