ws | Yarn - JavaScript 软件包管理器 | Yarn中文文档 - Yarn中文网
websockets/ws: Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js
Buffer、ArrayBuffer及DataView之间的转换 - 慕尘 - 博客园
ws 提速c++模块
bufferutil | Yarn - JavaScript 软件包管理器 | Yarn中文文档 - Yarn中文网
同构模块:
isomorphic-ws | Yarn - JavaScript 软件包管理器 | Yarn中文文档 - Yarn中文网
heineiuo/isomorphic-ws:WebSocket的同构实现(https://www.npmjs.com/package/ws) --- heineiuo/isomorphic-ws: Isomorphic implementation of WebSocket (https://www.npmjs.com/package/ws)
浏览器的 WebSocket 对象
WebSocket—Web APIs网站|MDN --- WebSocket - Web APIs | MDN
The WebSocket API (WebSockets) - Web APIs | MDN
WebSockets标准 --- WebSockets Standard
socket.io
socketio/engine.io-protocol: Engine.IO protocol
CloseEvent:代码属性 - Web API |MDN的 --- CloseEvent: code property - Web APIs | MDN
chrome://net-internals/#sockets
websocket 状态码
Status code 状态码 | Meaning 意义 | Description 描述 |
---|---|---|
0 –999 |
Not used. 未使用。 | |
1000 |
Normal Closure 正常闭合 | The connection successfully completed the purpose for which it was created. 连接成功完成了创建它的目的。 |
1001 |
Going Away 离开 | The endpoint is going away, either because of a server failure or because the browser is navigating away from the page that opened the connection. 终结点即将消失,可能是因为服务器故障,要么是因为浏览器正在远离打开连接的页面。 |
1002 |
Protocol error 协议错误 | The endpoint is terminating the connection due to a protocol error. 由于协议错误,终结点正在终止连接。 |
1003 |
Unsupported Data 不支持的数据 | The connection is being terminated because the endpoint received data of a type it cannot accept. (For example, a text-only endpoint received binary data.) 连接正在终止,因为终结点接收到它无法接受的类型的数据。(例如,纯文本终结点接收二进制数据。 |
1004 |
Reserved 保留 | Reserved. A meaning might be defined in the future. 保留。将来可能会定义一个含义。 |
1005 |
No Status Rcvd 无状态 Rcvd | Reserved. Indicates that no status code was provided even though one was expected. 保留。指示未提供状态代码,即使预期有状态代码。 |
1006 |
Abnormal Closure 异常闭合 | Reserved. Indicates that a connection was closed abnormally (that is, with no close frame being sent) when a status code is expected. 保留。指示连接在需要状态代码时异常关闭(即,没有发送关闭帧)。 |
1007 |
Invalid frame payload data 无效的帧有效负载数据 | The endpoint is terminating the connection because a message was received that contained inconsistent data (e.g., non-UTF-8 data within a text message). 终结点正在终止连接,因为收到的消息包含不一致的数据(例如,文本消息中的非 UTF-8 数据)。 |
1008 |
Policy Violation 违反政策 | The endpoint is terminating the connection because it received a message that violates its policy. This is a generic status code, used when codes 1003 and 1009 are not suitable. 终结点正在终止连接,因为它收到了违反其策略的消息。这是通用状态代码,在代码 1003 和 1009 不合适时使用。 |
1009 |
Message Too Big 消息太大 | The endpoint is terminating the connection because a data frame was received that is too large. 终结点正在终止连接,因为接收到的数据帧太大。 |
1010 |
Mandatory Ext. 强制性分机 | The client is terminating the connection because it expected the server to negotiate one or more extension, but the server didn't. 客户端正在终止连接,因为它希望服务器协商一个或多个扩展,但服务器没有。 |
1011 |
Internal Error 内部错误 | The server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request. 服务器正在终止连接,因为它遇到了意外情况,导致它无法满足请求。 |
1012 |
Service Restart 服务重启 | The server is terminating the connection because it is restarting. 服务器正在终止连接,因为它正在重新启动。 |
1013 |
Try Again Later 请稍后再试 | The server is terminating the connection due to a temporary condition, e.g. it is overloaded and is casting off some of its clients. 服务器由于临时情况而终止连接,例如它过载并正在抛弃其某些客户端。 |
1014 |
Bad Gateway 网关错误 | The server was acting as a gateway or proxy and received an invalid response from the upstream server. This is similar to 502 HTTP Status Code. 服务器充当网关或代理,并收到来自上游服务器的无效响应。这类似于 502 HTTP 状态代码。 |
1015 |
TLS handshake TLS 握手 | Reserved. Indicates that the connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified). 保留。指示由于无法执行 TLS 握手(例如,无法验证服务器证书)而关闭连接。 |
1016 –2999 |
For definition by future revisions of the WebSocket Protocol specification, and for definition by extension specifications. 用于 WebSocket 协议规范的未来修订版的定义,以及扩展规范的定义。 | |
3000 –3999 |
For use by libraries, frameworks, and applications. These status codes are registered directly with IANA. The interpretation of these codes is undefined by the WebSocket protocol. 供库、框架和应用程序使用。这些状态代码直接在 IANA 注册。这些代码的解释不受 WebSocket 协议的定义。 | |
4000 –4999 |
For private use, and thus can't be registered. Such codes can be used by prior agreements between WebSocket applications. The interpretation of these codes is undefined by the WebSocket protocol. 供私人使用,因此无法注册。这些代码可以由WebSocket应用程序之间的先前协议使用。这些代码的解释不受 WebSocket 协议的定义。 |
WS 文档
来自 GitHub
ws: a Node.js WebSocket library
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][node-zlib-deflaterawdocs].
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
[node-zlib-deflaterawdocs]:
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
发表评论