ws vs socket.io vs websocket vs uws
WebSocket Libraries for Real-Time Communication Comparison
1 Year
wssocket.iowebsocketuws
What's WebSocket Libraries for Real-Time Communication?

WebSocket libraries facilitate real-time communication between clients and servers, enabling bidirectional data exchange over a single, long-lived connection. These libraries are essential for applications that require instant data updates, such as chat applications, live notifications, and collaborative tools. They provide abstractions over the WebSocket protocol, making it easier for developers to implement real-time features without dealing with the complexities of the underlying protocol directly.

Package Weekly Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
ws92,584,08622,034147 kB611 days agoMIT
socket.io6,870,81661,7101.41 MB1854 months agoMIT
websocket1,069,6353,770154 kB7010 months agoApache-2.0
uws66,8878,325-354 years ago-
Feature Comparison: ws vs socket.io vs websocket vs uws

Performance

  • ws:

    The ws library is lightweight and fast, providing a good balance between performance and ease of use. It is designed to be efficient and can handle a large number of connections with minimal resource usage.

  • socket.io:

    Socket.IO is built on top of the WebSocket protocol and includes additional features like fallbacks for older browsers. While it is slightly less performant than raw WebSocket implementations due to these additional features, it still provides good performance for most applications.

  • websocket:

    The WebSocket API provides the best performance since it is a native implementation. It allows for low-latency communication and is suitable for applications where performance is critical.

  • uws:

    uWS is optimized for performance, allowing for handling thousands of concurrent connections with minimal overhead. It is designed for speed and efficiency, making it one of the fastest WebSocket libraries available.

Ease of Use

  • ws:

    The ws library is simple to use and integrates easily with existing Node.js applications. It provides a clean API that is easy to understand, making it a good choice for developers looking for a straightforward WebSocket solution.

  • socket.io:

    Socket.IO abstracts many complexities of WebSocket communication, providing a user-friendly API that simplifies the implementation of real-time features. Its built-in support for events and automatic reconnection makes it easy to use for developers of all skill levels.

  • websocket:

    The WebSocket API is straightforward for those familiar with the protocol. However, it requires more boilerplate code for handling events and reconnections compared to libraries like Socket.IO.

  • uws:

    uWS has a steeper learning curve due to its focus on performance and lower-level API. While it is powerful, developers may need to invest more time to understand its usage compared to higher-level libraries.

Features

  • ws:

    The ws library provides a minimal set of features, focusing on the core WebSocket functionality. It does not include advanced features like automatic reconnection or event handling, which must be implemented by the developer.

  • socket.io:

    Socket.IO includes features like automatic reconnection, event-based communication, and fallback options for older browsers. It also supports rooms and namespaces, allowing for organized communication between different groups of clients.

  • websocket:

    The WebSocket API is a low-level protocol that does not include any built-in features beyond the basic connection and message handling. Developers have full control but must implement additional features manually.

  • uws:

    uWS focuses on performance and does not include additional features like automatic reconnection or event handling. It is a barebones library that requires developers to implement these features themselves if needed.

Community and Support

  • ws:

    The ws library has a growing community and good documentation, making it relatively easy to find help. Its simplicity and integration with Node.js contribute to its popularity among developers.

  • socket.io:

    Socket.IO has a large community and extensive documentation, making it easy to find resources and support. Its popularity means that many developers are familiar with it, which can be beneficial for collaboration and troubleshooting.

  • websocket:

    The WebSocket API is part of the web standards and is supported by all modern browsers. However, since it is a low-level API, community support may vary based on specific implementations.

  • uws:

    uWS has a smaller community compared to Socket.IO, which may result in fewer resources available for support. However, its performance-focused nature attracts a niche audience that values speed and efficiency.

Compatibility

  • ws:

    The ws library is designed for Node.js and works well with modern browsers. However, like the WebSocket API, it may face compatibility issues with older browsers.

  • socket.io:

    Socket.IO is compatible with a wide range of browsers, including older versions, thanks to its fallback mechanisms. This makes it a good choice for applications that need to support a diverse user base.

  • websocket:

    The WebSocket API is supported by all modern browsers, but older browsers may not support it. Developers need to consider compatibility when using this API in production applications.

  • uws:

    uWS is designed for high performance and is compatible with modern browsers. However, it may not support older browsers as effectively as Socket.IO due to its focus on performance rather than broad compatibility.

How to Choose: ws vs socket.io vs websocket vs uws
  • ws:

    Choose ws if you are looking for a simple, lightweight, and fast WebSocket implementation for Node.js. It is easy to use and integrates well with existing Node.js applications, making it a good choice for developers who want a straightforward solution without extra features.

  • socket.io:

    Choose Socket.IO if you need a comprehensive solution that includes automatic reconnection, multiplexing, and fallback options for older browsers. It is ideal for applications that require real-time communication with a focus on reliability and ease of use.

  • websocket:

    Choose the WebSocket API if you want a native, low-level implementation without any additional abstractions. This is suitable for developers who prefer working directly with the WebSocket protocol and need maximum control over the connection.

  • uws:

    Choose uWS (µWebSockets) if performance is your top priority. It is designed for high throughput and low latency, making it suitable for applications that require handling a large number of concurrent connections efficiently.

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