Biblioteca ws
Construa servidores WebSocket de produção em Node.js com a biblioteca ws, incluindo heartbeat, autenticação e desligamento gracioso.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import { WebSocketServer, WebSocket } from "ws";
import { createServer } from "node:http";
const server = createServer();
const wss = new WebSocketServer({ noServer: true });
server.on("upgrade", (req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => wss.emit("connection", ws, req));
});
wss.on("connection", (ws) => {
ws.on("message", (data) => ws.send(data));
ws.on("close", () => console.log("Cliente desconectado"));
});
server.listen(3000);Quando usar isso: Você precisa de controle bruto de WebSocket sem a sobrecarga do Socket.IO, ou do gateway WebSocket do NestJS/Fastify.
Exemplo de Trabalho
import { WebSocketServer, WebSocket } from "ws";
import { createServer } from "node:http";
const server = createServer();
const wss = new WebSocketServer({ noServer: true });
function heartbeat() {
// @ts-expect-error propriedade customizada
this.isAlive = true;
}
server.on("upgrade", (req, socket, head) => {
const url = new URL(req.url ?? "/", "http://localhost");
const token = url.searchParams.get("token");
if (token !== process.env.WS_TOKEN) {
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
socket.destroy();
return;
}
wss.handleUpgrade(req, socket, head, (ws) => wss.emit("connection", ws, req));
});
wss.on("connection", (ws: WebSocket) => {
ws.isAlive = true;
ws.on("pong", heartbeat);
ws.on("message", (raw) => {
const msg = JSON.parse(String(raw));
if (msg.type === "ping") {
ws.send(JSON.stringify({ type: "pong" }));
return;
}
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ type: "broadcast", data: msg }));
}
});
});
ws.on("close", () => console.log("Cliente saiu"));
});
// Heartbeat a cada 30s
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) { ws.terminate(); return; }
ws.isAlive = false;
ws.ping();
});
}, 30_000);
wss.on("close", () => clearInterval(interval));
server.listen(3000);O que isso demonstra:
- Autenticação de token durante o upgrade HTTP
- Heartbeat ping/pong para detectar conexões mortas
- Protocolo de mensagem JSON com campo
type - Broadcast para todos os clientes conectados
terminate()para clientes sem resposta
Mergulho Profundo
Como Funciona
wsimplementa o protocolo WebSocket (RFC 6455)noServer: truepermite que você lide com o upgrade manualmente (para autenticação)ping()/pong()são frames de nível de protocolo (não mensagens de aplicação)wss.clientsé um Set de todas as instâncias WebSocket conectadas
Conexão do Cliente (Navegador)
const ws = new WebSocket("ws://localhost:3000?token=secret");
ws.onopen = () => ws.send(JSON.stringify({ type: "hello" }));
ws.onmessage = (e) => console.log(JSON.parse(e.data));
ws.onclose = () => console.log("Desconectado");Integração com NestJS
@WebSocketGateway({ path: "/ws" })
export class EventsGateway {
@SubscribeMessage("message")
handleMessage(@MessageBody() data: string) {
return { event: "response", data };
}
}Armadilhas
- Sem autenticação no upgrade - qualquer um pode conectar e enviar mensagens. Correção: valide o token antes de
handleUpgrade. - Sem heartbeat - conexões mortas permanecem em
wss.clientspara sempre. Correção: intervalo ping/pong comterminate(). - Broadcast para todos os clientes - picos de CPU em escala. Correção: fanout baseado em salas ou pub/sub Redis.
- Não tratar
closeno desligamento do servidor - clientes recebem RST. Correção: notifique os clientes, depois chamewss.close(). - Analisar JSON não validado - falha em mensagens malformadas. Correção: try/catch com resposta de erro.
- Vazamento de memória de listeners de eventos - listeners se acumulam na reconexão. Correção: remova listeners ao desconectar (
close).
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Socket.IO | Precisa de salas, fallback, adaptador Redis | Quer sobrecarga mínima de protocolo |
| SSE | Apenas do servidor para o cliente | Mensagens bidirecionais |
| uWebSockets.js | Máxima taxa de transferência WebSocket | API ws mais simples preferida |
| NestJS Gateway | App NestJS com decoradores | Projeto não-NestJS |
FAQs
ws vs Socket.IO?
ws é WebSocket bruto. Socket.IO adiciona salas, namespaces, fallback e adaptador Redis. Use ws quando quiser controle; Socket.IO quando quiser recursos.
Como limito conexões por IP?
Rastreie conexões em um Map com chave por IP durante o upgrade. Rejeite quando o limite for excedido.
ws funciona com Fastify?
Sim, via plugin @fastify/websocket, que envolve ws internamente.
Como envio dados binários?
ws.send(buffer) com Buffer ou ArrayBuffer. Útil para chunks de arquivos ou protobuf.
E sobre WSS (WebSocket seguro)?
Termine o TLS no proxy (igual ao HTTPS). O cliente conecta em wss:// que faz proxy para ws:// no Node.
Quantos clientes ws pode lidar?
10k-50k ociosos por processo. Depende da taxa de mensagens e do padrão de fanout. Perfure sua carga de trabalho.
Como testo servidores WebSocket?
Use o cliente WebSocket ws em testes, ou o pacote websocket do npm. Conecte à porta server.address().
Devo usar protocolo JSON ou binário?
JSON para simplicidade. Binário (protobuf, msgpack) quando o volume ou tamanho da mensagem exigir.
Relacionado
- Noções Básicas de Tempo Real - seleção de protocolo
- Socket.IO - alternativa de nível superior
- Escalando Tempo Real - multi-instância
- Noções Básicas de HTTP no Node - upgrade HTTP
- Melhores Práticas de Tempo Real - checklist da seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (LTS Ativo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.