¿Qué es TCP/IP?
TCP/IP es el conjunto de protocolos que sostiene internet. IP se encarga del direccionamiento y el enrutamiento de paquetes entre hosts; TCP se sitúa por encima y convierte esos paquetes no confiables en un flujo de bytes ordenado y fiable. Juntos forman la capa de transporte sobre la que funcionan HTTP, las bases de datos, el correo electrónico, WebSocket y casi todo lo demás que hace tu servidor.
Rara vez escribirás código TCP directamente, pero entenderlo explica muchas cosas: por qué las conexiones son costosas, por qué el keep-alive es importante, por qué un pool de base de datos tiene un límite de tamaño y por qué cierto tráfico utiliza UDP en su lugar.
El modelo de capas
Las redes suelen describirse en capas, donde cada una se encarga de una tarea específica:
| Capa | Tarea | Ejemplos |
|---|---|---|
| Aplicación | Significado | HTTP, WebSocket, DNS |
| Transporte | Fiabilidad y puertos | TCP, UDP |
| Internet | Direccionamiento y enrutamiento | IP, ICMP |
| Enlace | Entrega local | Ethernet, Wi-Fi |
Cada capa utiliza la que tiene debajo. HTTP no sabe si viaja a través de Ethernet o Wi-Fi; confía en TCP para entregar los bytes, y este a su vez confía en IP para mover los paquetes. Esta separación es la razón por la cual internet puede transportar tantos tipos de tráfico.
Direcciones IP y puertos
Una dirección IP identifica a un host, y un puerto identifica un servicio en ese host. Juntos forman una dirección de socket, como 203.0.113.10:443.
- Las direcciones IPv4 son de 32 bits (
203.0.113.10); las direcciones IPv6 son de 128 bits y se escriben en hexadecimal. - Los puertos inferiores a 1024 están reservados para servicios conocidos.
- Los clientes reciben un puerto efímero durante la vida útil de una conexión.
- Una conexión se identifica de forma única mediante la tupla completa: dirección de origen, puerto de origen, dirección de destino y puerto de destino.
Esa tupla es la razón por la cual un servidor puede aceptar miles de conexiones en el puerto 443 simultáneamente: el puerto de origen de cada cliente es diferente.
El saludo de tres vías (three-way handshake)
Antes de que fluya cualquier dato, TCP establece una conexión en tres pasos:
- El cliente envía un SYN con su número de secuencia inicial.
- El servidor responde con un SYN-ACK, confirmando la recepción del cliente y enviando su propio número de secuencia.
- El cliente envía un ACK, y la conexión queda establecida.
Esto implica un viaje de ida y vuelta (round trip) antes del primer byte de datos de la aplicación, lo que representa un coste real de latencia. TLS añade otro viaje de ida y vuelta (o dos), y DNS puede añadir otro antes de eso. La reutilización de conexiones existe específicamente para evitar pagar estos costes repetidamente.
Fiabilidad y control de flujo
TCP proporciona fiabilidad a través de varios mecanismos:
- Números de secuencia que ordenan los bytes, permitiendo que el receptor los reensamble correctamente.
- Acuses de recibo (Acknowledgements) que confirman la recepción; los datos no confirmados se retransmiten.
- Sumas de comprobación (Checksums) para detectar la corrupción de datos.
- Control de flujo que utiliza una ventana de recepción para evitar que un emisor rápido sature a un receptor lento.
- Control de congestión que adapta la tasa de envío a la red para evitar el colapso.
El resultado es un flujo de bytes que llega completo y en orden, a costa de cierta latencia y sobrecarga (overhead).
UDP: la alternativa rápida
UDP no requiere conexión. Envía datagramas sin handshake, sin orden garantizado y sin retransmisión. Esto podría parecer peor, pero para ciertas cargas de trabajo es preferible:
- La latencia importa más que la perfección: el video en vivo, la voz y los juegos prefieren perder un frame que recibirlo con retraso.
- La aplicación gestiona la fiabilidad: QUIC, que impulsa HTTP/3, construye su propia capa de fiabilidad sobre UDP.
- Consultas pequeñas y sin estado: DNS utiliza tradicionalmente UDP porque una única solicitud y respuesta caben en un solo datagrama.
Elige TCP cuando la exactitud y el orden sean fundamentales, y UDP cuando necesites velocidad y puedas tolerar la pérdida de datos.
Sockets en Node.js
Node expone TCP a través de node:net y UDP a través de node:dgram. Un servidor TCP te entrega un socket por cada conexión.
// echo.js
import { createServer } from "node:net";
const server = createServer((socket) => {
socket.setKeepAlive(true, 10_000);
socket.setTimeout(30_000);
socket.on("timeout", () => socket.destroy());
socket.on("data", (data) => socket.write(data));
socket.on("error", (err) => console.error(err));
});
server.listen(9000);
Los sockets son streams: lees eventos data y escribes con socket.write(). Debido a que TCP es un flujo de bytes sin límites de mensaje, los protocolos de aplicación deben definir su propio framing; es por esto que HTTP tiene encabezados con longitudes de contenido y WebSockets tiene frames.
Ciclo de vida y optimización de conexiones
- Keep-alive reutiliza las conexiones, evitando el handshake en cada solicitud.
- Pools limitan el número de conexiones concurrentes a una base de datos o servicio.
- Timeouts cierran las conexiones inactivas o bloqueadas para liberar recursos.
- TIME_WAIT es un estado normal tras el cierre, pero demasiadas conexiones de corta duración pueden agotar los puertos.
- Backpressure:
socket.write()devuelve false cuando el buffer está lleno, y debes esperar adrain.
La mayor parte de la optimización en producción consiste en reutilizar conexiones y limitar la concurrencia, no en cambiar los parámetros del kernel.
Mejores prácticas
- Reutiliza las conexiones mediante keep-alive o un pool.
- Configura timeouts y keep-alive en las conexiones de larga duración.
- Limita la concurrencia para no agotar los sockets o los descriptores de archivos.
- Gestiona los errores y la backpressure en cada socket.
- Utiliza TCP a menos que tengas un motivo específico para usar UDP.
- Ten en cuenta que TCP es un flujo de bytes (byte stream) y define tu propio framing.
- Monitoriza el estado TIME_WAIT y el número de conexiones como indicadores de la rotación de conexiones (connection churn).
Errores comunes
- Abrir una nueva conexión para cada solicitud.
- Asumir que TCP preserva los límites de los mensajes.
- Ignorar la contrapresión (backpressure) de
socket.write()y almacenar datos sin límite en el búfer. - Mantener conexiones de larga duración sin keep-alive o timeouts.
- Configurar tamaños de pool mucho más grandes de lo que la base de datos puede soportar.
- Optar por UDP cuando la fiabilidad es realmente importante.
Próximos pasos
TCP/IP es la capa sobre la cual se apoya todo lo demás. Sigue profundizando con la guía de HTTP, comprende los conceptos básicos de red en Cómo funciona Internet y pasa a las conexiones persistentes con WebSockets. Después, inspecciona las conexiones reales en tu máquina con ss o netstat mientras tu servidor gestiona el tráfico.