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

TCP com Net no Node.js: Guia Prático

Atualizado em: 10 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

HTTP, bancos de dados, filas e muitos protocolos de aplicação funcionam sobre TCP. O módulo nativo Net no Node.js permite criar servidores e clientes TCP diretamente, trabalhar com sockets como streams e implementar protocolos personalizados sem dependências externas.

TCP entrega um fluxo confiável e ordenado de bytes, mas não preserva limites de mensagens. Uma chamada de write() no cliente pode chegar dividida em vários chunks, ou várias mensagens podem chegar juntas. Por isso, um protocolo precisa definir framing, limites, timeout e tratamento de conexões lentas.

Neste guia, você aprenderá a criar servidor e cliente TCP, processar dados, implementar mensagens delimitadas, controlar backpressure, aplicar timeouts, encerrar conexões e usar TLS quando houver dados sensíveis.

Importando o módulo Net

const net = require('node:net');

Em ES Modules:

import net from 'node:net';

A documentação oficial do módulo Net detalha Server, Socket e opções de conexão. Para entender streams e backpressure, veja Streams no Node.js. A especificação do TCP no RFC 9293 apresenta o protocolo.

Criando um servidor TCP

const server = net.createServer(socket => {
  console.log('Cliente conectado', {
    address: socket.remoteAddress,
    port: socket.remotePort
  });

  socket.write('Bem-vindo\n');

  socket.on('data', chunk => {
    socket.write(chunk);
  });
});

server.listen(4000, '127.0.0.1', () => {
  console.log('Servidor TCP na porta 4000');
});

Esse exemplo implementa um echo server. Ele devolve os bytes recebidos. Em produção, trate erros, limites, timeout e encerramento.

Escutando em todas as interfaces

server.listen(4000, '0.0.0.0');

Isso expõe o serviço em interfaces IPv4 disponíveis. Use firewall, regras de rede e autenticação. Para acesso apenas local, prefira 127.0.0.1 ou socket Unix.

Criando um cliente TCP

const client = net.createConnection({
  host: '127.0.0.1',
  port: 4000
});

client.on('connect', () => {
  client.write('Olá servidor\n');
});

client.on('data', chunk => {
  console.log(chunk.toString('utf8'));
});

O evento connect indica que o handshake TCP terminou. Isso não significa que o protocolo da aplicação autenticou o cliente.

Socket é uma Duplex Stream

Um net.Socket permite leitura e escrita:

socket.on('data', processChunk);
socket.write(responseBuffer);

As regras de backpressure das streams se aplicam. Se write() retornar false, aguarde drain.

Respeitando backpressure

async function writeSafely(socket, data) {
  if (socket.write(data)) return;

  await new Promise((resolve, reject) => {
    socket.once('drain', resolve);
    socket.once('error', reject);
  });
}

Um cliente lento pode fazer o buffer de saída crescer. Defina limites de fila e encerre conexões que não acompanham o fluxo.

TCP não preserva mensagens

Este código é incorreto para JSON sem framing:

socket.on('data', chunk => {
  const message = JSON.parse(chunk.toString());
});

O JSON pode chegar incompleto ou conter duas mensagens. O protocolo precisa definir como separar.

Framing por quebra de linha

const { StringDecoder } = require('node:string_decoder');

function attachLineProtocol(socket) {
  const decoder = new StringDecoder('utf8');
  let pending = '';

  socket.on('data', chunk => {
    pending += decoder.write(chunk);

    let index;
    while ((index = pending.indexOf('\n')) !== -1) {
      const line = pending.slice(0, index);
      pending = pending.slice(index + 1);
      handleLine(socket, line);
    }
  });

  socket.on('end', () => {
    pending += decoder.end();
    if (pending) handleLine(socket, pending);
  });
}

Imponha limite ao conteúdo pendente para impedir uma conexão que nunca envia delimitador.

Limite por mensagem

const MAX_MESSAGE_BYTES = 64 * 1024;

if (Buffer.byteLength(pending, 'utf8') > MAX_MESSAGE_BYTES) {
  socket.destroy(new Error('Mensagem grande demais'));
}

Controle bytes antes que a string cresça muito. Para protocolo binário, mantenha Buffer e leia cabeçalhos.

Framing por tamanho

Um protocolo pode enviar quatro bytes com o tamanho seguidos pelo payload:

function encodeMessage(payload) {
  const body = Buffer.from(JSON.stringify(payload));
  const header = Buffer.alloc(4);
  header.writeUInt32BE(body.length);
  return Buffer.concat([header, body]);
}

O parser acumula bytes até possuir cabeçalho e corpo completos. Valide o tamanho antes de alocar.

Timeout de inatividade

socket.setTimeout(30000);

socket.on('timeout', () => {
  socket.end('Timeout\n');
  setTimeout(() => socket.destroy(), 1000).unref();
});

setTimeout() no socket emite evento; ele não encerra automaticamente. Implemente política explícita.

Keepalive

socket.setKeepAlive(true, 10000);

TCP keepalive ajuda a detectar peers mortos, mas intervalos e comportamento dependem do sistema operacional. Não substitui heartbeat do protocolo quando você precisa de detecção rápida.

Nagle e setNoDelay()

socket.setNoDelay(true);

Desativar Nagle pode reduzir latência de mensagens pequenas e aumentar quantidade de pacotes. Meça de acordo com o protocolo.

Eventos importantes do Socket

  • connect: conexão estabelecida;
  • data: bytes recebidos;
  • end: peer encerrou escrita;
  • finish: escrita local terminou;
  • close: socket fechado;
  • error: falha;
  • timeout: período configurado sem atividade.

Registre error para evitar exceções não tratadas.

end() e destroy()

socket.end('Até logo\n');

end() finaliza a escrita depois dos dados pendentes. destroy() fecha imediatamente e pode descartar buffers.

socket.destroy(error);

Use destroy para protocolo inválido, limite excedido ou falha irrecuperável.

Half-open connections

TCP permite encerrar uma direção e manter a outra. A opção allowHalfOpen controla como o Node.js reage:

const server = net.createServer(
  { allowHalfOpen: false },
  socket => {}
);

Para a maioria dos protocolos simples, o padrão evita conexões parcialmente abertas esquecidas.

Limite de conexões

server.maxConnections = 1000;

Esse limite ajuda, mas também controle conexões no balanceador e sistema operacional. Defina backlog e limites de arquivos abertos conforme a carga.

Contando conexões

server.getConnections((error, count) => {
  if (!error) metrics.set('tcp_connections', count);
});

Em arquiteturas com vários processos, o valor representa apenas a instância correspondente.

Sockets Unix

server.listen('/var/run/app.sock');

Sockets Unix evitam porta TCP e podem usar permissões do arquivo. Remova o socket antigo com cuidado na inicialização e proteja o diretório.

Named pipes no Windows

No Windows, pipes nomeados utilizam sintaxe específica:

server.listen('\\\\.\\pipe\\my-app');

Encapsule diferenças de plataforma e teste em todos os sistemas suportados.

DNS e conexão

Ao usar hostname, resolução DNS acontece antes da conexão. Configure timeout global que considere ambas as etapas e use AbortSignal quando a API suportar.

Veja DNS no Node.js e AbortController no Node.js.

Reconexão do cliente

Não reconecte imediatamente em loop. Use backoff e jitter:

const delay = Math.min(
  baseDelay * 2 ** attempt,
  maxDelay
) * (0.5 + Math.random());

Resete a tentativa depois de uma conexão estável. Limite fila enquanto estiver desconectado.

Autenticação

TCP puro não fornece identidade de aplicação. Implemente autenticação no protocolo ou use TLS mútuo. Nunca envie senha ou token em texto por rede não confiável.

TLS

Para criptografia, use node:tls ou um proxy seguro. Veja HTTPS e TLS no Node.js.

Slowloris em protocolos TCP

Um cliente pode enviar bytes lentamente e ocupar conexão. Defina timeout de leitura, tamanho máximo de cabeçalho/mensagem e limite de conexões por origem quando possível.

Validação de protocolo

Valide versão, tipo de mensagem, comprimento, campos obrigatórios e sequência de estados. Um parser deve rejeitar dados desconhecidos e nunca confiar no comprimento declarado sem limite.

Observabilidade

Monitore:

  • conexões ativas e novas por segundo;
  • bytes recebidos e enviados;
  • erros por código;
  • timeouts;
  • mensagens inválidas;
  • fila de escrita;
  • duração das conexões.

Não use endereço IP como label de métrica de alta cardinalidade.

Graceful shutdown

server.close(() => {
  console.log('Servidor não aceita novas conexões');
});

server.close() espera conexões existentes. Mantenha um Set de sockets para solicitar encerramento e aplicar prazo final.

const sockets = new Set();

server.on('connection', socket => {
  sockets.add(socket);
  socket.on('close', () => sockets.delete(socket));
});

Testando servidor TCP

Inicie em porta aleatória:

server.listen(0, '127.0.0.1');

Depois obtenha server.address().port. Teste mensagens divididas, várias mensagens no mesmo chunk, timeout, payload grande e encerramento abrupto.

Erros comuns

  • Tratar chunk como mensagem: parsing falha de forma intermitente.
  • Ignorar backpressure: memória cresce com clientes lentos.
  • Não definir timeout: conexões abandonadas permanecem.
  • Enviar segredo sem TLS: dados podem ser interceptados.
  • Confiar no tamanho declarado: alocações enormes são solicitadas.
  • Reconectar sem backoff: o servidor recebe tempestade de conexões.
  • Usar destroy em todo encerramento: dados pendentes são perdidos.

Boas práticas

  • Defina framing explícito.
  • Limite tamanho de mensagens e buffers.
  • Respeite backpressure.
  • Configure timeout e keepalive.
  • Trate todos os eventos de erro.
  • Use TLS para rede não confiável.
  • Aplique backoff em reconexões.
  • Valide estados e campos do protocolo.
  • Monitore conexões e filas.
  • Implemente graceful shutdown.

Conclusão

O módulo Net no Node.js oferece acesso direto a TCP através de servidores e sockets integrados às streams. Ele é a base para protocolos personalizados e clientes de serviços.

Confiabilidade depende da camada de aplicação. TCP ordena bytes, mas não define mensagens, autenticação ou limites. Com framing, backpressure, timeouts, TLS e validação, um serviço TCP pode operar de forma previsível mesmo sob conexões lentas e dados inválidos.

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