Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

UDP com Dgram no Node.js: Guia Prático

Atualizado em: 11 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

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.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita