ws vs faye-websocket vs socket.io-client vs engine.io-client vs sockjs-client vs socketcluster-client vs primus
WebSocket and Real-time Communication Libraries Comparison
1 Year
wsfaye-websocketsocket.io-clientengine.io-clientsockjs-clientsocketcluster-clientprimusSimilar Packages:
What's WebSocket and Real-time Communication Libraries?

These libraries facilitate real-time communication in web applications, enabling bidirectional data transfer between clients and servers. They are essential for applications requiring instant updates, such as chat applications, online gaming, and collaborative tools. Each library offers unique features and protocols, catering to different use cases and developer preferences.

Package Weekly Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
ws94,912,23522,128147 kB52 months agoMIT
faye-websocket17,573,509611-54 years agoApache-2.0
socket.io-client5,185,57861,8821.41 MB1865 months agoMIT
engine.io-client5,171,49061,882863 kB1863 months agoMIT
sockjs-client2,995,6458,476700 kB30-MIT
socketcluster-client57,446296216 kB265 months agoMIT
primus8,1654,473508 kB50a year agoMIT
Feature Comparison: ws vs faye-websocket vs socket.io-client vs engine.io-client vs sockjs-client vs socketcluster-client vs primus

Transport Protocols

  • ws:

    Ws is focused solely on the WebSocket protocol, providing a simple and efficient implementation for Node.js applications.

  • faye-websocket:

    Faye-websocket strictly implements the WebSocket protocol, ensuring compliance and reliability for real-time messaging without additional transport options.

  • socket.io-client:

    Socket.io-client uses WebSocket as its primary transport but can fall back to other methods like polling if WebSocket is unavailable, ensuring robust connectivity.

  • engine.io-client:

    Engine.io-client supports multiple transport protocols, including WebSocket, polling, and others, allowing it to adapt to various environments and browser capabilities.

  • sockjs-client:

    Sockjs-client provides a consistent API across multiple transport protocols, including WebSocket, XHR, and others, ensuring compatibility with various browsers and environments.

  • socketcluster-client:

    Socketcluster-client primarily utilizes WebSocket for real-time communication but can also handle fallback transports, making it versatile for different environments.

  • primus:

    Primus offers a variety of transport protocols, including WebSocket, SockJS, and others, providing flexibility to choose the best transport method for your application needs.

Ease of Use

  • ws:

    Ws is very easy to use, especially for Node.js developers, as it provides a simple interface for creating WebSocket clients.

  • faye-websocket:

    Faye-websocket is designed for simplicity, making it easy to integrate into existing applications without a steep learning curve.

  • socket.io-client:

    Socket.io-client is known for its ease of use, with extensive documentation and a large community that provides support and examples for common use cases.

  • engine.io-client:

    Engine.io-client is relatively easy to use, with a straightforward API that allows developers to quickly implement real-time features in their applications.

  • sockjs-client:

    Sockjs-client is straightforward to implement, especially for developers familiar with WebSocket, as it provides a similar API with added fallback options.

  • socketcluster-client:

    Socketcluster-client has a moderate learning curve, but its comprehensive features and scalability options make it worthwhile for complex applications.

  • primus:

    Primus offers a user-friendly API that abstracts the complexities of different transport methods, making it accessible for developers of all skill levels.

Scalability

  • ws:

    Ws is lightweight and efficient, making it suitable for scalable applications, particularly in Node.js environments.

  • faye-websocket:

    Faye-websocket is not specifically designed for high scalability, making it better suited for smaller applications or those with moderate traffic.

  • socket.io-client:

    Socket.io-client is designed for scalability, with features like namespaces and rooms that help manage large applications with many concurrent users.

  • engine.io-client:

    Engine.io-client is suitable for small to medium-sized applications but may require additional configurations for large-scale deployments.

  • sockjs-client:

    Sockjs-client can scale well with the right backend implementation, but its performance may vary depending on the chosen transport method.

  • socketcluster-client:

    Socketcluster-client is built for high scalability, capable of handling thousands of concurrent connections and designed for real-time applications that require performance.

  • primus:

    Primus excels in scalability, allowing developers to easily switch between transport methods and handle a large number of connections efficiently.

Community and Support

  • ws:

    Ws has a strong community, especially among Node.js developers, with good documentation and support available.

  • faye-websocket:

    Faye-websocket has a smaller community, which may limit the availability of third-party resources and support.

  • socket.io-client:

    Socket.io-client boasts a large community and extensive documentation, providing ample resources for troubleshooting and learning.

  • engine.io-client:

    Engine.io-client has a moderate community, with sufficient resources and documentation available for developers.

  • sockjs-client:

    Sockjs-client has a decent community and documentation, but it may not be as comprehensive as more popular libraries.

  • socketcluster-client:

    Socketcluster-client has a growing community, but it may not be as extensive as others, which can affect the availability of support.

  • primus:

    Primus has an active community and good documentation, making it easier for developers to find help and examples.

Performance

  • ws:

    Ws is known for its high performance and low overhead, making it an excellent choice for applications that require fast real-time communication.

  • faye-websocket:

    Faye-websocket performs well for small applications but may struggle under heavy load due to its simplicity and lack of advanced features.

  • socket.io-client:

    Socket.io-client provides good performance with features like automatic reconnection and multiplexing, but it may introduce some overhead compared to raw WebSocket implementations.

  • engine.io-client:

    Engine.io-client is optimized for performance, with efficient handling of connections and fallbacks, but may not be as fast as WebSocket-only implementations.

  • sockjs-client:

    Sockjs-client's performance can vary depending on the transport method used, but it generally provides a good balance between compatibility and speed.

  • socketcluster-client:

    Socketcluster-client is highly optimized for performance, capable of handling many connections efficiently, making it ideal for real-time applications.

  • primus:

    Primus is designed for performance, with optimizations for various transport methods, making it suitable for high-load applications.

How to Choose: ws vs faye-websocket vs socket.io-client vs engine.io-client vs sockjs-client vs socketcluster-client vs primus
  • ws:

    Use ws for a simple, fast, and efficient WebSocket client that is easy to integrate into Node.js applications. It is ideal for developers who need a minimalistic approach and high performance without additional features.

  • faye-websocket:

    Select faye-websocket for a simple and robust implementation of the WebSocket protocol that is easy to integrate into existing applications. It is suitable for projects that require a straightforward pub/sub messaging model without complex features.

  • socket.io-client:

    Use socket.io-client if you need a comprehensive solution that includes automatic reconnection, multiplexing, and support for fallback transports. It is ideal for applications that require reliable real-time communication with a rich feature set and a large community support.

  • engine.io-client:

    Choose engine.io-client if you need a lightweight, low-level library that provides a transport-agnostic way to handle real-time communication. It is ideal for applications that require fallback options for older browsers or environments with limited WebSocket support.

  • sockjs-client:

    Select sockjs-client when you need a library that provides a consistent API across various transport methods, including WebSocket and XHR. It is useful for applications that require compatibility with older browsers or environments where WebSocket is not available.

  • socketcluster-client:

    Choose socketcluster-client for applications that demand scalability and high performance. It is designed for real-time applications that need to handle a large number of concurrent connections efficiently, making it suitable for large-scale projects.

  • primus:

    Opt for primus if you want a flexible and extensible framework that supports multiple real-time protocols. It is perfect for developers looking for a unified API to switch between different transport methods seamlessly, making it adaptable for various environments.

README for ws

ws: a Node.js WebSocket library

Version npm CI Coverage Status

ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and server implementation.

Passes the quite extensive Autobahn test suite: server, client.

Note: This module does not work in the browser. The client in the docs is a reference to a backend with the role of a client in the WebSocket communication. Browser clients must use the native WebSocket object. To make the same code work seamlessly on Node.js and the browser, you can use one of the many wrappers available on npm, like isomorphic-ws.

Table of Contents

Protocol support

  • HyBi drafts 07-12 (Use the option protocolVersion: 8)
  • HyBi drafts 13-17 (Current default, alternatively option protocolVersion: 13)

Installing

npm install ws

Opt-in for performance

bufferutil is an optional module that can be installed alongside the ws module:

npm install --save-optional bufferutil

This is a binary addon that improves the performance of certain operations such as masking and unmasking the data payload of the WebSocket frames. Prebuilt binaries are available for the most popular platforms, so you don't necessarily need to have a C++ compiler installed on your machine.

To force ws to not use bufferutil, use the WS_NO_BUFFER_UTIL environment variable. This can be useful to enhance security in systems where a user can put a package in the package search path of an application of another user, due to how the Node.js resolver algorithm works.

Legacy opt-in for performance

If you are running on an old version of Node.js (prior to v18.14.0), ws also supports the utf-8-validate module:

npm install --save-optional utf-8-validate

This contains a binary polyfill for buffer.isUtf8().

To force ws not to use utf-8-validate, use the WS_NO_UTF_8_VALIDATE environment variable.

API docs

See /doc/ws.md for Node.js-like documentation of ws classes and utility functions.

WebSocket compression

ws supports the permessage-deflate extension which enables the client and server to negotiate a compression algorithm and its parameters, and then selectively apply it to the data payloads of each WebSocket message.

The extension is disabled by default on the server and enabled by default on the client. It adds a significant overhead in terms of performance and memory consumption so we suggest to enable it only if it is really needed.

Note that Node.js has a variety of issues with high-performance compression, where increased concurrency, especially on Linux, can lead to catastrophic memory fragmentation and slow performance. If you intend to use permessage-deflate in production, it is worthwhile to set up a test representative of your workload and ensure Node.js/zlib will handle it with acceptable performance and memory usage.

Tuning of permessage-deflate can be done via the options defined below. You can also use zlibDeflateOptions and zlibInflateOptions, which is passed directly into the creation of raw deflate/inflate streams.

See the docs for more options.

import WebSocket, { WebSocketServer } from 'ws';

const wss = new WebSocketServer({
  port: 8080,
  perMessageDeflate: {
    zlibDeflateOptions: {
      // See zlib defaults.
      chunkSize: 1024,
      memLevel: 7,
      level: 3
    },
    zlibInflateOptions: {
      chunkSize: 10 * 1024
    },
    // Other options settable:
    clientNoContextTakeover: true, // Defaults to negotiated value.
    serverNoContextTakeover: true, // Defaults to negotiated value.
    serverMaxWindowBits: 10, // Defaults to negotiated value.
    // Below options specified as default values.
    concurrencyLimit: 10, // Limits zlib concurrency for perf.
    threshold: 1024 // Size (in bytes) below which messages
    // should not be compressed if context takeover is disabled.
  }
});

The client will only use the extension if it is supported and enabled on the server. To always disable the extension on the client, set the perMessageDeflate option to false.

import WebSocket from 'ws';

const ws = new WebSocket('ws://www.host.com/path', {
  perMessageDeflate: false
});

Usage examples

Sending and receiving text data

import WebSocket from 'ws';

const ws = new WebSocket('ws://www.host.com/path');

ws.on('error', console.error);

ws.on('open', function open() {
  ws.send('something');
});

ws.on('message', function message(data) {
  console.log('received: %s', data);
});

Sending binary data

import WebSocket from 'ws';

const ws = new WebSocket('ws://www.host.com/path');

ws.on('error', console.error);

ws.on('open', function open() {
  const array = new Float32Array(5);

  for (var i = 0; i < array.length; ++i) {
    array[i] = i / 2;
  }

  ws.send(array);
});

Simple server

import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('error', console.error);

  ws.on('message', function message(data) {
    console.log('received: %s', data);
  });

  ws.send('something');
});

External HTTP/S server

import { createServer } from 'https';
import { readFileSync } from 'fs';
import { WebSocketServer } from 'ws';

const server = createServer({
  cert: readFileSync('/path/to/cert.pem'),
  key: readFileSync('/path/to/key.pem')
});
const wss = new WebSocketServer({ server });

wss.on('connection', function connection(ws) {
  ws.on('error', console.error);

  ws.on('message', function message(data) {
    console.log('received: %s', data);
  });

  ws.send('something');
});

server.listen(8080);

Multiple servers sharing a single HTTP/S server

import { createServer } from 'http';
import { WebSocketServer } from 'ws';

const server = createServer();
const wss1 = new WebSocketServer({ noServer: true });
const wss2 = new WebSocketServer({ noServer: true });

wss1.on('connection', function connection(ws) {
  ws.on('error', console.error);

  // ...
});

wss2.on('connection', function connection(ws) {
  ws.on('error', console.error);

  // ...
});

server.on('upgrade', function upgrade(request, socket, head) {
  const { pathname } = new URL(request.url, 'wss://base.url');

  if (pathname === '/foo') {
    wss1.handleUpgrade(request, socket, head, function done(ws) {
      wss1.emit('connection', ws, request);
    });
  } else if (pathname === '/bar') {
    wss2.handleUpgrade(request, socket, head, function done(ws) {
      wss2.emit('connection', ws, request);
    });
  } else {
    socket.destroy();
  }
});

server.listen(8080);

Client authentication

import { createServer } from 'http';
import { WebSocketServer } from 'ws';

function onSocketError(err) {
  console.error(err);
}

const server = createServer();
const wss = new WebSocketServer({ noServer: true });

wss.on('connection', function connection(ws, request, client) {
  ws.on('error', console.error);

  ws.on('message', function message(data) {
    console.log(`Received message ${data} from user ${client}`);
  });
});

server.on('upgrade', function upgrade(request, socket, head) {
  socket.on('error', onSocketError);

  // This function is not defined on purpose. Implement it with your own logic.
  authenticate(request, function next(err, client) {
    if (err || !client) {
      socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
      socket.destroy();
      return;
    }

    socket.removeListener('error', onSocketError);

    wss.handleUpgrade(request, socket, head, function done(ws) {
      wss.emit('connection', ws, request, client);
    });
  });
});

server.listen(8080);

Also see the provided example using express-session.

Server broadcast

A client WebSocket broadcasting to all connected WebSocket clients, including itself.

import WebSocket, { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('error', console.error);

  ws.on('message', function message(data, isBinary) {
    wss.clients.forEach(function each(client) {
      if (client.readyState === WebSocket.OPEN) {
        client.send(data, { binary: isBinary });
      }
    });
  });
});

A client WebSocket broadcasting to every other connected WebSocket clients, excluding itself.

import WebSocket, { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('error', console.error);

  ws.on('message', function message(data, isBinary) {
    wss.clients.forEach(function each(client) {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(data, { binary: isBinary });
      }
    });
  });
});

Round-trip time

import WebSocket from 'ws';

const ws = new WebSocket('wss://websocket-echo.com/');

ws.on('error', console.error);

ws.on('open', function open() {
  console.log('connected');
  ws.send(Date.now());
});

ws.on('close', function close() {
  console.log('disconnected');
});

ws.on('message', function message(data) {
  console.log(`Round-trip time: ${Date.now() - data} ms`);

  setTimeout(function timeout() {
    ws.send(Date.now());
  }, 500);
});

Use the Node.js streams API

import WebSocket, { createWebSocketStream } from 'ws';

const ws = new WebSocket('wss://websocket-echo.com/');

const duplex = createWebSocketStream(ws, { encoding: 'utf8' });

duplex.on('error', console.error);

duplex.pipe(process.stdout);
process.stdin.pipe(duplex);

Other examples

For a full example with a browser client communicating with a ws server, see the examples folder.

Otherwise, see the test cases.

FAQ

How to get the IP address of the client?

The remote IP address can be obtained from the raw socket.

import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws, req) {
  const ip = req.socket.remoteAddress;

  ws.on('error', console.error);
});

When the server runs behind a proxy like NGINX, the de-facto standard is to use the X-Forwarded-For header.

wss.on('connection', function connection(ws, req) {
  const ip = req.headers['x-forwarded-for'].split(',')[0].trim();

  ws.on('error', console.error);
});

How to detect and close broken connections?

Sometimes, the link between the server and the client can be interrupted in a way that keeps both the server and the client unaware of the broken state of the connection (e.g. when pulling the cord).

In these cases, ping messages can be used as a means to verify that the remote endpoint is still responsive.

import { WebSocketServer } from 'ws';

function heartbeat() {
  this.isAlive = true;
}

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.isAlive = true;
  ws.on('error', console.error);
  ws.on('pong', heartbeat);
});

const interval = setInterval(function ping() {
  wss.clients.forEach(function each(ws) {
    if (ws.isAlive === false) return ws.terminate();

    ws.isAlive = false;
    ws.ping();
  });
}, 30000);

wss.on('close', function close() {
  clearInterval(interval);
});

Pong messages are automatically sent in response to ping messages as required by the spec.

Just like the server example above, your clients might as well lose connection without knowing it. You might want to add a ping listener on your clients to prevent that. A simple implementation would be:

import WebSocket from 'ws';

function heartbeat() {
  clearTimeout(this.pingTimeout);

  // Use `WebSocket#terminate()`, which immediately destroys the connection,
  // instead of `WebSocket#close()`, which waits for the close timer.
  // Delay should be equal to the interval at which your server
  // sends out pings plus a conservative assumption of the latency.
  this.pingTimeout = setTimeout(() => {
    this.terminate();
  }, 30000 + 1000);
}

const client = new WebSocket('wss://websocket-echo.com/');

client.on('error', console.error);
client.on('open', heartbeat);
client.on('ping', heartbeat);
client.on('close', function clear() {
  clearTimeout(this.pingTimeout);
});

How to connect via a proxy?

Use a custom http.Agent implementation like https-proxy-agent or socks-proxy-agent.

Changelog

We're using the GitHub releases for changelog entries.

License

MIT