snowluma-ws 是一个面向 Node.js 的 ESM-only WebSocket 客户端 / 服务端库,底层使用 cmake-js + node-addon-api 构建原生 addon,遵循 RFC 6455。
它支持:
- 独立端口运行 WebSocket 服务端
- 独立 TLS 端口运行
wss://服务端 - 复用已有的 Node.js
http.Server - 复用已有的 Node.js
https.Server noServer + handleUpgrade手动接管 HTTP upgrade- WebSocket 客户端连接
ws:///wss:// - 与
ws包互通 - 与 Node.js 内置
globalThis.WebSocket客户端互通 - text / binary 消息
- fragmented message
permessage-deflate- WebSocket extensions 协商框架
- subprotocol 自动协商响应
- ping / pong
- close code / reason
- payload 边界与协议错误校验
- Node.js
>= 18 - npm
- CMake
- C++17 编译器
- Windows 下建议安装 Visual Studio 2022 C++ 构建工具
npm install安装时会执行:
cmake-js compile也可以手动构建:
npm run build构建产物默认位于:
build/Release/snowluma_ws.node
npm test当前测试覆盖:
- 原生握手和 frame build / parse
- 与
ws包双向互通 - 与 Node.js 内置
globalThis.WebSocket客户端互通 ws:///wss://双向连接测试- 独立端口服务端
- 复用已有
http.Server - 复用已有
https.Server noServer + handleUpgradepermessage-deflate与ws双向互通- subprotocol 自动协商
- payload 长度边界:
0, 1, 125, 126, 127, 255, 65535, 65536, 100000 - 大消息
- fragmentation
- ping / pong
- close code / reason
- unmasked client frame
- invalid UTF-8
- unknown opcode
- RSV bits
- oversized control frame
- non-minimal payload length
- 并发连接
- TCP 任意切包输入
import { WebSocket } from 'snowluma-ws';
const socket = new WebSocket('ws://127.0.0.1:8080');
socket.on('open', () => {
socket.send('hello');
});
socket.on('message', (data, isBinary) => {
console.log(isBinary ? data : data.toString('utf8'));
});
socket.on('close', (code, reason) => {
console.log('closed', code, reason);
});import { WebSocketServer } from 'snowluma-ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('listening', () => {
console.log('WebSocket server listening on', wss.address());
});
wss.on('connection', (socket, request) => {
socket.on('message', (data, isBinary) => {
socket.send(data, { binary: isBinary });
});
});import fs from 'node:fs';
import { WebSocketServer } from 'snowluma-ws';
const wss = new WebSocketServer({
port: 8443,
tls: {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
},
});
wss.on('connection', (socket) => {
socket.send('connected over wss://');
});import http from 'node:http';
import { WebSocketServer } from 'snowluma-ws';
const server = http.createServer((req, res) => {
res.end('normal http response');
});
const wss = new WebSocketServer({ server });
wss.on('connection', (socket, request) => {
socket.send('connected through shared http server');
});
server.listen(8080);import fs from 'node:fs';
import https from 'node:https';
import { WebSocketServer } from 'snowluma-ws';
const server = https.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
}, (req, res) => {
res.end('normal https response');
});
const wss = new WebSocketServer({ server });
wss.on('connection', (socket) => {
socket.send('connected through shared https server');
});
server.listen(8443);默认不启用压缩。客户端和服务端都需要显式传入 perMessageDeflate。
import { WebSocket, WebSocketServer } from 'snowluma-ws';
const wss = new WebSocketServer({
port: 8080,
perMessageDeflate: true,
});
wss.on('connection', (socket) => {
console.log(socket.extensions);
socket.on('message', (data) => {
socket.send(data, { compress: true });
});
});
const client = new WebSocket('ws://127.0.0.1:8080', {
perMessageDeflate: true,
});
client.on('open', () => {
console.log(client.extensions);
client.send('hello'.repeat(1000), { compress: true });
});服务端传入 protocols 后,会自动从客户端请求的协议列表中选择第一个服务端支持的协议,并返回 Sec-WebSocket-Protocol。
import { WebSocket, WebSocketServer } from 'snowluma-ws';
const wss = new WebSocketServer({
port: 8080,
protocols: ['chat.v2', 'chat.v1'],
});
wss.on('connection', (socket) => {
console.log(socket.protocol);
});
const client = new WebSocket('ws://127.0.0.1:8080', ['chat.v3', 'chat.v2']);
client.on('open', () => {
console.log(client.protocol);
});适合在同一个 HTTP 端口上按 path 或业务逻辑手动路由 WebSocket。
import http from 'node:http';
import { WebSocketServer } from 'snowluma-ws';
const server = http.createServer((req, res) => {
res.end('plain http');
});
const wss = new WebSocketServer({ noServer: true });
server.on('upgrade', (req, socket, head) => {
if (req.url === '/ws') {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit('connection', ws, req);
});
return;
}
socket.destroy();
});
wss.on('connection', (socket) => {
socket.on('message', (data) => {
socket.send('echo:' + data.toString('utf8'));
});
});
server.listen(8080);创建客户端连接。
支持参数:
url:ws://或wss://URLprotocols:可选协议数组options.maxPayload:最大 payload 字节数options.perMessageDeflate:是否请求permessage-deflateoptions.headers:额外握手请求头options.socketOptions:透传给net.connect或tls.connect
连接自签名证书的测试服务时,可以通过 socketOptions 传递 TLS 参数:
const socket = new WebSocket('wss://127.0.0.1:8443', {
socketOptions: {
rejectUnauthorized: false,
},
});常用事件:
openmessage(data, isBinary)ping(data)pong(data)close(code, reason)error(error)
常用方法:
send(data[, options][, callback])ping([data][, callback])pong([data][, callback])close([code][, reason])terminate()
send() 的 options 支持:
binary:强制按 binary 或 text 发送compress:协商permessage-deflate后是否压缩当前消息
创建服务端。
支持模式:
{ port }:创建独立 HTTP server 并监听 WebSocket upgrade{ port, tls }:创建独立 HTTPS server 并监听wss://upgrade{ server }:复用已有http.Server或https.Server{ noServer: true }:不监听端口,手动调用handleUpgrade
常用 options:
port:监听端口host:监听地址server:已有http.ServernoServer:手动 upgrade 模式path:只接受指定 pathmaxPayload:最大 payload 字节数perMessageDeflate:是否接受permessage-deflateprotocols:服务端支持的 subprotocol 列表,也可以是选择函数verifyClient(info)或verifyClient(info, callback):握手校验tls:独立端口模式下传给https.createServer()的 TLS 配置key/cert/pfx等 TLS 字段:也可直接放在 options 顶层
常用事件:
listeningconnection(socket, request)error(error)
常用方法:
address()handleUpgrade(request, socket, head, callback)close([callback])
本库导出类似 ws 的表面 API:
import { WebSocket, WebSocketServer, Server } from 'snowluma-ws';其中:
WebSocket是客户端构造器WebSocketServer是服务端构造器Server是WebSocketServer的别名
测试中已经覆盖:
snowluma-wsserver +wsclientwsserver +snowluma-wsclientsnowluma-wsserver + Node.js 内置globalThis.WebSocketclientsnowluma-wsstandalone TLS server +snowluma-wswss://clientwssecure server +snowluma-wswss://clientpermessage-deflate双向压缩互通- subprotocol 自动协商
C++ addon 负责:
- SHA1
- Base64
Sec-WebSocket-Accept计算- WebSocket frame 构建
- WebSocket frame 流式解析
- mask / unmask
- payload length 校验
- RSV bit 暴露与协商后校验
JavaScript 层负责:
- TCP / HTTP I/O
- client handshake
- server upgrade handshake
- message fragmentation 组装
- WebSocket extensions 协商
permessage-deflate压缩 / 解压- subprotocol 自动协商与校验
- UTF-8 校验
- ping / pong 自动响应
- close handshake
- 用户 API 与事件派发
当前实现覆盖核心 RFC 6455 功能。
已实现:
- text frame
- binary frame
- continuation frame
- close frame
- ping / pong frame
- client-to-server masking 校验
- server-to-client unmasked 校验
- control frame 限制
- close code 校验
- UTF-8 文本校验
permessage-deflate- WebSocket extensions 协商
- subprotocol 自动协商响应
暂未实现:
- 浏览器环境打包
npm run build
npm run rebuild
npm run clean
npm testsnowluma-ws/
├─ CMakeLists.txt
├─ package.json
├─ src/
│ ├─ base64.cc
│ ├─ base64.h
│ ├─ bindings.cc
│ ├─ frame_builder.cc
│ ├─ frame_builder.h
│ ├─ frame_parser.cc
│ ├─ frame_parser.h
│ ├─ handshake.cc
│ ├─ handshake.h
│ ├─ sha1.cc
│ └─ sha1.h
├─ lib/
│ ├─ client.js
│ ├─ extensions.js
│ ├─ index.js
│ ├─ native.js
│ ├─ server.js
│ └─ websocket.js
└─ test/
├─ boundary.test.js
├─ interop.test.js
└─ smoke.test.js