ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and server implementation.ws是一个简单易用、快速且经过全面测试的WebSocket客户端和服务器实现。
Passes the quite extensive Autobahn test suite: server, client.通过了相当广泛的高速公路测试套件: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. 文档中的客户端是对后端的引用,该后端在WebSocket通信中扮演客户端的角色。Browser clients must use the native WebSocket object. 浏览器客户端必须使用本机WebSocket对象。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.为了使相同的代码在Node.js和浏览器上无缝工作,您可以使用npm上可用的许多包装器中的一个,比如isomorphic-ws。
protocolVersion: 8
)protocolVersion: 13
)npm install ws
There are 2 optional modules that can be installed along side with the ws module. 有2个可选模块可以与ws模块一起安装。These modules are binary addons that improve the performance of 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.预构建的二进制文件可用于最流行的平台,因此您不一定需要在机器上安装C++编译器。
npm install --save-optional bufferutil
npm install --save-optional utf-8-validate
To not even try to require and use these modules, use the WS_NO_BUFFER_UTIL and WS_NO_UTF_8_VALIDATE environment variables. 为了甚至不尝试要求和使用这些模块,请使用WS_NO_BUFFER_UTIL和WS_NO_UTF_8_VALIDATE环境变量。These might 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.由于Node.js解析器算法的工作原理,在用户可以将包放入另一个用户的应用程序的包搜索路径的系统中,这些可能有助于增强安全性。
The 如果buffer.isUtf8()可用,则无论utf-8-validate
module is not needed and is not required, even if it is already installed, regardless of the value of the WS_NO_UTF_8_VALIDATE
environment variable, if buffer.isUtf8() is available.WS_NO_UTF_8_VALIDATE
环境变量的值如何,utf-8-validate
模块都不需要也不需要,即使它已经安装。
See /doc/ws.md for Node.js-like documentation of ws classes and utility functions.有关类似Node.js的ws类和实用程序函数的文档,请参阅/doc/ws.md。
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.ws支持permessage-delate扩展,该扩展使客户端和服务器能够协商压缩算法及其参数,然后选择性地将其应用于每个WebSocket消息的数据有效负载。
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. 请注意,Node.js在高性能压缩方面存在各种问题,其中并发性的增加,尤其是在Linux上,可能会导致灾难性的内存碎片和性能下降。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.如果您打算在生产中使用permesage-deflate,那么值得为您的工作负载设置一个测试代表,并确保Node.js/zlib能够以可接受的性能和内存使用率来处理它。
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.zlibDeflateOptions
和zlibInflateOptions
,它们直接传递到原始放气/充气流的创建中。
See the docs for more options.有关更多选项,请参阅文档。
import WebSocket, { WebSocketServer } from 'ws';
const wss = new WebSocketServer({
port: 8080,
perMessageDeflate: {
zlibDeflateOptions: {
// See zlib defaults.请参阅zlib默认值。
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.限制性能的zlib并发性。
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
.perMessageDeflate
选项设置为false
。
import WebSocket from 'ws';
const ws = new WebSocket('ws://www.host.com/path', {
perMessageDeflate: false
});
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);
});
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);
});
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');
});
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);
import { createServer } from 'http';
import { parse } from 'url';
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 } = parse(request.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);
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
.express-session
的示例。
A client WebSocket broadcasting to all connected WebSocket clients, including itself.客户端WebSocket广播到所有连接的WebSocket客户端,包括其自身。
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.客户端WebSocket广播到除自身之外的所有其他连接的WebSocket客户端。
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 });
}
});
});
});
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);
});
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);
For a full example with a browser client communicating with a ws server, see the examples folder.有关浏览器客户端与ws服务器通信的完整示例,请参阅examples文件夹。
Otherwise, see the test cases.否则,请参阅测试用例。
The remote IP address can be obtained from the raw socket.远程IP地址可以从原始套接字中获取。
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 当服务器运行在像NGINX这样的代理之后时,事实上的标准是使用X-Forwarded-For
header.X-Forwarded-For
标头。
wss.on('connection', function connection(ws, req) {
const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
ws.on('error', console.error);
});
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.在这些情况下,可以使用ping消息来验证远程端点是否仍有响应。
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.Pong消息是根据规范的要求自动发送的,以响应ping消息。
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. 您可能需要在客户端上添加一个ping侦听器来防止这种情况发生。A simple implementation would be:一个简单的实现方式是:
import WebSocket from 'ws';
function heartbeat() {
clearTimeout(this.pingTimeout);
// Use `WebSocket#terminate()`, which immediately destroys the connection,使用`WebSocket#terminate()`,它会立即破坏连接,
// instead of `WebSocket#close()`, which waits for the close timer.而不是等待关闭计时器的`WebSocket#close()`。
// Delay should be equal to the interval at which your server sends out pings plus a conservative assumption of the latency.延迟应该等于服务器发送ping的间隔加上保守的延迟假设。
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);
});
Use a custom 使用自定义http.Agent
implementation like https-proxy-agent or socks-proxy-agent.http.Agent
实现,如https-proxy-agent或socks-proxy-agent。
We're using the GitHub releases for changelog entries.我们正在使用GitHub版本的变更日志条目。