ws:一个Node.js WebSocket库
ws是一个简单易用、快速且经过彻底测试的WebSocket客户端和服务器实现。
注意:此模块在浏览器中不起作用。文档中的客户端是引用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 发布更新日志条目。