Nem toda comunicação precisa de conexão, confirmação e entrega ordenada. Telemetria, descoberta de serviços, jogos, DNS e transmissão em tempo real frequentemente utilizam UDP para enviar datagramas com baixa sobrecarga. O módulo nativo Dgram no Node.js permite criar sockets UDP, enviar mensagens, receber pacotes e trabalhar com multicast.
UDP não garante entrega, ordem ou ausência de duplicação. Cada datagrama preserva seu limite, mas pode ser perdido, chegar repetido ou seguir rota diferente. A aplicação precisa decidir se aceita essas características ou implementa confirmação, sequência e retry.
Neste guia, você aprenderá a criar servidor e cliente UDP, usar IPv4 e IPv6, limitar tamanho, tratar erros, configurar broadcast e multicast e aplicar práticas contra amplificação e abuso.
Importando o módulo Dgram
const dgram = require('node:dgram');Em ES Modules:
import dgram from 'node:dgram';A documentação oficial do módulo Dgram detalha sockets e opções. Para comunicação orientada a conexão, veja TCP com Net no Node.js. A especificação do UDP no RFC 768 apresenta o protocolo.
Criando um socket UDP
const socket = dgram.createSocket('udp4');Para IPv6:
const socket6 = dgram.createSocket('udp6');O tipo precisa corresponder aos endereços e à configuração de rede.
Servidor UDP básico
const server = dgram.createSocket('udp4');
server.on('message', (message, remote) => {
console.log({
text: message.toString('utf8'),
address: remote.address,
port: remote.port,
size: remote.size
});
});
server.on('error', error => {
console.error(error);
server.close();
});
server.bind(5000, '127.0.0.1');O evento message representa um datagrama completo. Diferentemente de TCP, os limites de cada envio são preservados.
Enviando um datagrama
const client = dgram.createSocket('udp4');
const message = Buffer.from('Olá UDP');
client.send(
message,
5000,
'127.0.0.1',
error => {
if (error) console.error(error);
client.close();
}
);O callback confirma que os dados foram entregues ao sistema operacional para envio, não que o servidor os recebeu.
UDP não possui conexão real
Algumas APIs permitem conectar um socket UDP a um destino padrão:
client.connect(5000, '127.0.0.1', () => {
client.send('mensagem');
});Isso simplifica chamadas e filtra peers, mas não cria handshake ou garantia como TCP.
Tamanho do datagrama
O campo UDP permite payload limitado. Na prática, datagramas grandes podem ser fragmentados pela rede e têm maior chance de perda. Prefira mensagens pequenas, geralmente abaixo do MTU do caminho.
const MAX_PAYLOAD = 1200;
if (message.length > MAX_PAYLOAD) {
throw new Error('Datagrama grande demais');
}O limite ideal depende do protocolo e da rede.
Fragmentação IP
Se um datagrama ultrapassa o MTU, o IP pode fragmentá-lo. A perda de um fragmento impede remontar o pacote inteiro. Algumas redes bloqueiam fragmentos.
Para dados grandes, use TCP, QUIC ou divida em mensagens com identificador, sequência, limite e timeout.
Formato da mensagem
Defina versão e tipo:
const payload = Buffer.from(JSON.stringify({
version: 1,
type: 'heartbeat',
instanceId: 'api-1',
timestamp: Date.now()
}));Valide tamanho antes de fazer JSON.parse. Um datagrama pode ser malformado ou enviado por qualquer origem alcançável.
Parser seguro
function parseMessage(buffer) {
if (buffer.length > 4096) {
throw new Error('Payload excedeu o limite');
}
const value = JSON.parse(buffer.toString('utf8'));
if (value.version !== 1) {
throw new Error('Versão não suportada');
}
return value;
}Valide esquema, tipos e faixas após o parse.
Perda de pacotes
Se cada mensagem é crítica, UDP puro pode não ser apropriado. Para tolerar perda, envie estados periódicos em vez de eventos únicos. O próximo pacote corrige o estado anterior.
Sequência e duplicatas
{
"streamId": "sensor-1",
"sequence": 153,
"value": 42
}O receptor pode ignorar sequências antigas e detectar lacunas. Contadores precisam lidar com reinício e overflow.
Confirmação na aplicação
Uma aplicação pode responder ACK:
server.send(
Buffer.from(JSON.stringify({ ack: message.id })),
remote.port,
remote.address
);Ao adicionar ACK, retry, janela e congestionamento, você começa a reconstruir funcionalidades de protocolos confiáveis. Avalie se TCP ou QUIC seria mais adequado.
Retry com backoff
Não envie retry em frequência fixa e alta. Use limite, timeout e jitter. Veja Retry com Backoff no Node.js.
Bind em todas as interfaces
server.bind(5000, '0.0.0.0');Isso expõe a porta em interfaces IPv4. Proteja com firewall e autenticação no protocolo.
Porta aleatória
server.bind(0, '127.0.0.1', () => {
console.log(server.address());
});Portas aleatórias são úteis em testes.
Reutilização de endereço
const socket = dgram.createSocket({
type: 'udp4',
reuseAddr: true
});O comportamento varia por sistema. Reutilização pode permitir vários sockets na mesma porta, especialmente em multicast. Teste na plataforma de produção.
Broadcast
socket.bind(() => {
socket.setBroadcast(true);
socket.send(
'discover',
5000,
'255.255.255.255'
);
});Broadcast normalmente fica limitado à rede local e pode ser filtrado. Evite tempestades de descoberta; inclua intervalos e jitter.
Multicast
const group = '239.255.0.1';
server.bind(5000, () => {
server.addMembership(group);
});Vários receptores podem participar do grupo. Configure interface, TTL e loopback conforme necessário.
TTL do multicast
socket.setMulticastTTL(1);TTL baixo limita propagação. Não confie apenas nele como controle de segurança.
Interface de multicast
Máquinas com várias interfaces podem escolher uma rota inesperada. Use setMulticastInterface() quando a topologia exigir.
Amplificação UDP
UDP permite falsificação do endereço de origem em várias redes. Um servidor que responde com pacote muito maior pode ser usado em ataque de amplificação.
- não responda grandes volumes antes de validar o cliente;
- mantenha resposta menor ou próxima da requisição;
- aplique rate limiting;
- restrinja rede quando o serviço for interno;
- evite endpoints públicos de reflexão.
Autenticação de mensagens
Para telemetria em rede não confiável, assine a mensagem:
const hmac = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');Inclua timestamp, nonce ou sequência para reduzir replay. Rotacione chaves e compare assinatura em tempo constante.
Criptografia
HMAC garante integridade e autenticidade, não confidencialidade. Para dados secretos, use protocolo seguro como DTLS, QUIC ou uma camada de criptografia corretamente projetada. Não invente um esquema próprio.
Timeout no receptor
UDP não mantém conexão. A aplicação pode expirar o estado de um peer:
peers.set(id, {
lastSeenAt: Date.now(),
address: remote.address
});Uma tarefa periódica remove peers inativos. Use timer com limite e evite mapa sem expiração.
Buffers de socket
APIs como getRecvBufferSize(), setRecvBufferSize() e equivalentes de envio permitem consultar ou ajustar buffers, conforme sistema e permissões.
Aumentar buffers reduz perda em picos, mas consome memória e não corrige consumidor lento.
Observando perda
Inclua sequência e monitore lacunas. Métricas do sistema operacional também podem mostrar drops. Não existe confirmação automática no módulo.
Tratamento de erros
socket.on('error', error => {
logger.error('Falha UDP', {
code: error.code,
message: error.message
});
});Erros podem ocorrer no bind, DNS, envio ou configuração de opções.
Encerramento
socket.close(() => {
console.log('Socket UDP fechado');
});Depois de close, novos envios falham. Pare timers e aguarde tarefas que dependem do socket.
AbortSignal
Versões modernas podem aceitar signal em opções de criação ou bind. Verifique a versão do Node.js e teste cancelamento.
Testando UDP
Use porta aleatória e endereço local. Teste:
- mensagem válida;
- JSON inválido;
- payload acima do limite;
- pacotes fora de ordem;
- duplicatas;
- timeout de peer;
- perda simulada;
- shutdown.
Erros comuns
- Esperar entrega garantida: UDP pode perder pacotes.
- Enviar datagrama enorme: fragmentação aumenta perdas.
- Confiar no endereço de origem: spoofing pode ocorrer.
- Responder muito mais dados: serviço vira amplificador.
- Não limitar JSON: payload malicioso consome recursos.
- Retry sem jitter: clientes criam tempestade sincronizada.
- Usar HMAC como criptografia: conteúdo continua visível.
Boas práticas
- Mantenha datagramas pequenos.
- Defina versão, tipo e tamanho.
- Valide todos os campos.
- Use sequência quando ordem importa.
- Projete para perda e duplicação.
- Aplique rate limiting e autenticação.
- Evite amplificação.
- Monitore drops e lacunas.
- Restrinja broadcast e multicast.
- Use protocolo seguro para confidencialidade.
Conclusão
O módulo Dgram no Node.js oferece acesso direto a UDP para telemetria, descoberta, multicast e aplicações sensíveis a latência. Cada envio preserva o datagrama e evita o custo de uma conexão.
A simplicidade transfere responsabilidade para o protocolo. Perda, duplicação, spoofing e amplificação precisam ser considerados. Com mensagens pequenas, validação, sequência, autenticação e limites, UDP pode ser uma escolha eficiente sem comprometer a estabilidade do serviço.




