ws vs socket.io-client vs engine.io-client vs faye-websocket vs primus vs socketcluster-client vs sockjs-client
WebSocket and Real-time Communication Libraries
wssocket.io-clientengine.io-clientfaye-websocketprimussocketcluster-clientsockjs-clientSimilar Packages:

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.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
ws149,689,26822,704148 kB62 months agoMIT
socket.io-client10,807,83962,9671.42 MB2033 months agoMIT
engine.io-client062,967868 kB2033 months agoMIT
faye-websocket0614-55 years agoApache-2.0
primus04,474508 kB502 years agoMIT
socketcluster-client0301217 kB244 months agoMIT
sockjs-client08,521700 kB30-MIT

Feature Comparison: ws vs socket.io-client vs engine.io-client vs faye-websocket vs primus vs socketcluster-client vs sockjs-client

Transport Protocols

  • ws:

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

  • 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.

  • faye-websocket:

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

  • 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.

  • socketcluster-client:

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

  • sockjs-client:

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

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.

  • 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.

  • faye-websocket:

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

  • primus:

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

  • socketcluster-client:

    Socketcluster-client has a moderate learning curve, but its comprehensive features and scalability options make it worthwhile for complex 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.

Scalability

  • ws:

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

  • 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.

  • faye-websocket:

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

  • primus:

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

  • 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.

  • sockjs-client:

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

Community and Support

  • ws:

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

  • 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.

  • faye-websocket:

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

  • primus:

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

  • socketcluster-client:

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

  • sockjs-client:

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

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.

  • 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.

  • faye-websocket:

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

  • primus:

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

  • socketcluster-client:

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

  • 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.

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

  • 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.

  • 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.

  • 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.

  • 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.

  • 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.

  • 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.

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