ws

易于使用,为Node.js提供快速且经过全面测试的WebSocket客户端和服务器。(Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js.)

Github stars Tracking Chart

ws:一个Node.js WebSocket库

ws是一个简单易用、快速且经过彻底测试的WebSocket客户端和服务器实现。

通过相当广泛的Autobahn测试套件:服务器客户

注意:此模块在浏览器中不起作用。文档中的客户端是引用WebSocket中客户端角色的后端通讯。浏览器客户端必须使用本机WebSocket 对象。

协议支持

  • HyBi草稿07-12 (使用 protocolVersion:8 选项)
  • HyBi草稿13-17 (当前默认,或者 protocolVersion:13 )

安装

npm install --save ws

选择性能和规格符合性

有两个可选模块可以与ws一起安装模块。这些模块是改进某些操作的二进制插件。预编译的二进制文件可用于最流行的平台,所以你不需要必须在你的机器上安装一个C++编译器。

  • npm install --save-optional bufferutil :允许有效地执行诸如屏蔽和取消屏蔽WebSocket帧的数据有效负载之类的操作。
  • npm install --save-optional utf-8-validate :允许有效地检查消息是否包含规范要求的有效UTF-8。

API文档

有关ws类的类Node.js文档,请参见/doc/ws.md

WebSocket压缩

ws支持 permessage-deflate 扩展,它使客户端和服务器能够协商压缩算法及其参数,然后有选择地将其应用于每个WebSocket消息的数据有效负载。

默认情况下,在服务器上禁用该扩展,并在客户端上默认启用该扩展。它在性能和内存消耗方面增加了很大的开销,所以我们建议只在真正需要时启用它。

请注意,Node.js存在高性能压缩的各种问题,其中增加的并发性(尤其是在Linux上)可能导致灾难性的内存碎片和性能降低。如果您打算在生产中使用permessage-deflate,则值得设置一个代表您的工作负载的测试,并确保Node.js/zlib将以可接受的性能和内存使用情况处理它。

可以通过下面定义的选项来完成渗透 - 收缩的调整。您还可以使用zlibDeflateOptions和zlibInflateOptions,它们直接传递给raw deflate / inflate流的创建。

有关更多选项,请参阅文档

const WebSocket = require('ws');

const wss = new WebSocket.Server({
  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.
  }
});

如果在服务器上支持并启用了扩展,则客户端将仅使用该扩展。 要始终禁用客户端上的扩展,请将perMessageDeflate选项设置为false。

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

用法示例

发送和接收文本数据

const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
  ws.send('something');
});
ws.on('message', function incoming(data) {
  console.log(data);
});

发送二进制数据

const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
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);
});
});

服务器示例

 const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });
  ws.send('something');
});

广播示例

const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
const server = new https.createServer({
  cert: fs.readFileSync('/path/to/cert.pem'),
  key: fs.readFileSync('/path/to/key.pem')
});
const wss = new WebSocket.Server({ server });
wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });
  ws.send('something');
});
server.listen(8080);

多个服务器共享一个HTTP/S服务器

const http = require('http');
const WebSocket = require('ws');
const server = http.createServer();
const wss1 = new WebSocket.Server({ noServer: true });
const wss2 = new WebSocket.Server({ noServer: true });
wss1.on('connection', function connection(ws) {
  // ...
});
wss2.on('connection', function connection(ws) {
  // ...
});
server.on('upgrade', function upgrade(request, socket, head) {
  const pathname = url.parse(request.url).pathname;
  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);

echo.websocket.org演示

const WebSocket = require('ws');
const ws = new WebSocket('wss://echo.websocket.org/', {
  origin: 'https://websocket.org'
});
ws.on('open', function open() {
  console.log('connected');
  ws.send(Date.now());
});
ws.on('close', function close() {
  console.log('disconnected');
});
ws.on('message', function incoming(data) {
  console.log(`Roundtrip time: ${Date.now() - data} ms`);
  setTimeout(function timeout() {
    ws.send(Date.now());
  }, 500);
});

其他例子

有关浏览器客户端与ws服务器通信的完整示例,请参阅 示例文件夹。

否则,请参阅测试用例。

错误处理最佳实践

// If the WebSocket is closed before the following send is attempted
ws.send('something');
// Errors (both immediate and async write errors) can be detected in an optional
// callback. The callback is also the only way of being notified that data has
// actually been sent.
ws.send('something', function ack(error) {
  // If error is not defined, the send has been completed, otherwise the error
  // object will indicate what failed.
});
// Immediate errors can also be handled with `try...catch`, but **note** that
// since sends are inherently asynchronous, socket write failures will *not* be
// captured when this technique is used.
try {
  ws.send('something');
} catch (e) {
  /* handle error */
}

常见问题

如何获取客户端的IP地址?

远程IP地址可以从原始套接字获得。

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws, req) {
  const ip = req.connection.remoteAddress;
});

当服务器运行在像NGINX这样的代理服务器后面时,事实上的标准就是使用 X-Forwarded-For 标题。

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

如何检测并关闭断开的连接?

有时服务器和客户端之间的链接可能会被中断 保持服务器和客户端都不知道服务器断开状态的方式 连接(例如,拉动电线时)。

在这些情况下,ping消息可用作验证远程设备的手段 端点仍然有响应。

const WebSocket = require('ws');
function noop() {}
function heartbeat() {
  this.isAlive = true;
}
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
  ws.isAlive = true;
  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(noop);
  });
}, 30000);
Pong消息根据需要自动发送以响应ping消息 由规格。

如何通过代理连接

使用自定义 http.Agent 实现,如 https-proxy-agent socks-proxy-agent

更新日志

我们正在使用GitHub 发布更新日志条目。

许可证

MIT

Overview

Name With Ownerwebsockets/ws
Primary LanguageJavaScript
Program languageJavaScript (Language Count: 1)
PlatformLinux, Mac, Windows
License:MIT License
Release Count177
Last Release Name8.17.0 (Posted on )
First Release Namev0.0.1 (Posted on )
Created At2011-11-09 22:32:45
Pushed At2024-04-28 05:48:46
Last Commit At2024-04-28 07:41:02
Stargazers Count21.1k
Watchers Count377
Fork Count2.3k
Commits Count1.9k
Has Issues Enabled
Issues Count1494
Issue Open Count7
Pull Requests Count493
Pull Requests Open Count0
Pull Requests Close Count216
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private

ws: a Node.js WebSocket library

Version npm
Linux Build
Windows Build
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 back end 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 and spec compliance

There are 2 optional modules that can be installed along side with the ws
module. These modules are binary addons which improve certain operations.
Prebuilt binaries are available for the most popular platforms so you don't
necessarily need to have a C++ compiler installed on your machine.

  • npm install --save-optional bufferutil: Allows to efficiently perform
    operations such as masking and unmasking the data payload of the WebSocket
    frames.
  • npm install --save-optional utf-8-validate: Allows to efficiently check if a
    message contains valid UTF-8 as required by the spec.

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.

const WebSocket = require('ws');

const wss = new WebSocket.Server({
  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.
  }
});

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.

const WebSocket = require('ws');

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

Usage examples

Sending and receiving text data

const WebSocket = require('ws');

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

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

ws.on('message', function incoming(data) {
  console.log(data);
});

Sending binary data

const WebSocket = require('ws');

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

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

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

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

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

External HTTP/S server

const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');

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

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

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

server.listen(8080);

Multiple servers sharing a single HTTP/S server

const http = require('http');
const WebSocket = require('ws');
const url = require('url');

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

wss1.on('connection', function connection(ws) {
  // ...
});

wss2.on('connection', function connection(ws) {
  // ...
});

server.on('upgrade', function upgrade(request, socket, head) {
  const pathname = url.parse(request.url).pathname;

  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

const http = require('http');
const WebSocket = require('ws');

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

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

server.on('upgrade', function upgrade(request, socket, head) {
  authenticate(request, (err, client) => {
    if (err, !client) {
      socket.destroy();
      return;
    }

    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.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

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

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

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

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

echo.websocket.org demo

const WebSocket = require('ws');

const ws = new WebSocket('wss://echo.websocket.org/', {
  origin: 'https://websocket.org'
});

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

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

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

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

Use the Node.js streams API

const WebSocket = require('ws');

const ws = new WebSocket('wss://echo.websocket.org/', {
  origin: 'https://websocket.org'
});

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

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.

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

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

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(/\s*,\s*/)[0];
});

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.

const WebSocket = require('ws');

function noop() {}

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

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.isAlive = true;
  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(noop);
  });
}, 30000);

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:

const WebSocket = require('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://echo.websocket.org/');

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

To the top