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 发布更新日志条目。