Skip to content
This repository has been archived by the owner on Nov 2, 2020. It is now read-only.

Latest commit

 

History

History
325 lines (253 loc) · 9.98 KB

README.md

File metadata and controls

325 lines (253 loc) · 9.98 KB

(Deprecated) ClusterWS/cWS

Fast WebSocket implementation for Node.js

npm version travis build

Important Notes

  • This project has been deprecated consider using latest version of uWebSockets

  • This repository is a fork of uWebSockets v0.14 therefore has two licence MIT and ZLIB

  • Consider using latest uWebSockets version instead

  • You can support me through Patreon and PayPal

Supported Node Versions (SSL)

This table is true if you run ssl directly with cws (Node.js). In case if you use proxy for example nginx, cws can be run on bigger coverage.

CWS Version Node 10 Node 11 Node 12 Node 13 Node 14
3.0.0 >=10.0 X >=12.16 >=13.9 >=14.5
2.0.0 >=10.0 X >=12.16 >=13.9 X
1.6.0 >=10.0 >=11.0 >=12.0 & <12.16 >=13.9 X

Documentation

Useful links

Table of Contents

Installation

npm i @clusterws/cws

Websocket Client

Typings: dist/client.d.ts

Import cws WebSocket:

const { WebSocket } = require('@clusterws/cws');

Connect to WebSocket server:

const socket = new WebSocket(/* ws server endpoint **/);

Event on open is triggered when server accepts connection and this connection is ready to perform other actions:

socket.on('open', () => { });
// or like browser interface
socket.onopen = () => { };

Event on error is triggered if there is an error with the connection:

socket.on('error', (err) => { });
// or like browser interface
socket.onerror = (err) => { };

Event on close is triggered when connection has been closed:

// code: number and reason: string are optional
socket.on('close', (code, reason) => { });
// or like browser interface
socket.onclose = (code, reason) => { };

Event on message is triggered when client receives message(s):

// receives string or binary

socket.on('message', (message) => { });
// or like browser interface
socket.onmessage = (message) => { };

Event ping is triggered when other side sends ping to check if connection still active:

socket.on('ping', () => { });

Event pong is triggered after ping has been sent and it received back conformation:

socket.on('pong', () => { })

To send message use send function:

// send accepts string or binary
// will automatically identify type
socket.send(msg);

// will overwrite/specify message type to string (does not transform data to specified type)
socket.send(msg, { binary: false });

// will overwrite/specify message type to binary (does not transform data to specified type)
socket.send(msg, { binary: true });

// will call callback after message sent or errored
socket.send(msg, null, (err) => { });

To close connection you can use close or terminate methods:

// clean close code and reason are optional
socket.close(code, reason);
// destroy socket 
socket.terminate()

Use ping function to manually send ping:

socket.ping();
// now just wait for `pong` event

To get current socket ready state can use readyState getter:

socket.readyState; // -> OPEN (1) or CLOSED (3)

// check if socket open can be done by
if(socket.readyState === socket.OPEN) {}

// check if socket closed can be done by
if(socket.readyState === socket.CLOSED) {}

To get addresses use _socket getter:

socket._socket;

// Returns some thing like (all fields could be undefined):
// {
//  remotePort,
//  remoteAddress,
//  remoteFamily
// }

For more information check typings (*.d.ts) files in dist folder

Websocket Server

Typings: dist/server.d.ts

Import cws WebSocket:

const { WebSocket } = require('@clusterws/cws');

Create WebSocket server:

const wsServer = new WebSocket.Server({ 
  /**
   * port?: number (creates server and listens on provided port)
   * host?: string (provide host if necessary with port)
   * path?: string (url at which accept ws connections)
   * server?: server (provide already existing server)
   * noDelay?: boolean (set socket no delay)
   * noServer?: boolean (use this when upgrade done outside of cws)
   * maxPayload?: number 
   * perMessageDeflate?: boolean | { serverNoContextTakeover: boolean;}
   * verifyClient?: (info: ConnectionInfo, next: VerifyClientNext) => void (use to allow or decline connections)
   **/ 
 }, () => {
  // callback called when server is ready
  // is not called when `noServer: true` or `server` is provided
  // from outside
});

Event on connection is triggered when new client is connected to the server:

// `ws` is websocket client all available options 
// can be found in `Websocket Client` section above
// `req` is http upgrade request
wsServer.on('connection', (ws, req) => {})

Event on error is triggered when server has some issues and noServer is false:

// on error event will NOT include httpServer errors IF
// server was passed under server parameter { server: httpServer },
// you can register on 'error' listener directly on passed server
wsServer.on('error', (err) => { })

Event on close is triggered after you call wsServer.close() function, if cb is provided both cb and on close listener will be triggered:

wsServer.on('close', () => { })

To get all connected clients use clients getter:

wsServer.clients;

// loop thought all clients:
wsServer.clients.forEach((ws) => { });

// get number of clients:
wsServer.clients.length;

To send message to all connected clients use broadcast method:

// broadcast string
wsServer.broadcast(message);

// broadcast binary
wsServer.broadcast(message, { binary: true });

cWS supports auto ping with startAutoPing function:

wsServer.startAutoPing(interval, appLevel);

// send ping to each client every 10s and destroy client if no pong received 
wsServer.startAutoPing(10000);

// pass true as second parameter to run app level ping
// this is mainly used for browser to track pings at client level
// as they do not expose on ping listener 
// look for browser client side implementation at the bottom
// `Handle App Level Ping In Browser (example)`
wsServer.startAutoPing(10000, true);

To stop server use close function:

wsServer.close(() => {
  // triggered after server has been stopped
})

handleUpgrade is function which is commonly used together with noServer (same as ws module)

const wss = new WebSocket.Server({ noServer: true });
const server = http.createServer();

wss.on('connection', (ws, req) => { })

server.on('upgrade', (request, socket, head) => {
  wss.handleUpgrade(request, socket, head, (ws) => {
      wss.emit('connection', ws, request);
  });
});

For more information check typings (*.d.ts) files in dist folder

Secure WebSocket

You can use wss:// with cws by providing https server to cws and setting secureProtocol on https options:

const { readFileSync } = require('fs');
const { createServer }  = require('https');
const { WebSocket, secureProtocol  } = require('@clusterws/cws');

const options = {
  key: readFileSync(/** path to key */),
  cert: readFileSync(/** path to certificate */),
  secureProtocol
  // ...other Node HTTPS options
};

const server = createServer(options);
const wsServer = new WebSocket.Server({ server });
// your secure ws is ready (do your usual things)

server.listen(port, () => {
  console.log('Server is running');
})

For more detail example check examples folder

Handle App Level Ping In Browser (example)

Handling custom App level ping, pong from the client side which does not have onping and onpong listeners available such as browsers.

Note if all your clients have onping and onpong listeners do not send appLevelPing ping from the server. If you enable appLevelPing you will need to implement similar handler for every client library which connects to the server.

const PING = 57;
const PONG = new Uint8Array(['A'.charCodeAt()]);

socket.binaryType = 'arraybuffer';

socket.onmessage = function (message) {
    // note actually sent message in
    // browser default WebSocket is under `message.data`

    // check if message is not string 
    if (typeof message.data !== 'string') {
        // transform it to Uint8Array
        let buffer = new Uint8Array(message.data);

        // Check if it is actually ping from the server
        if (buffer.length === 1 && buffer[0] === PING) {
            // this is definitely `ping` event you can call custom on ping handler 
            // also must send back immediately pong to the server 
            // otherwise server will disconnect this client
            return socket.send(PONG);
        }
    }

    // process with your logic
}