Guía
¿Qué es un puerto, en concreto? Una explicación práctica para desarrolladores
14 de septiembre de 20264 min de lectura
Has escrito localhost:3000 mil veces. Has visto “port already in use” más seguido de lo que quisieras. Pero si alguien te preguntara qué es un puerto exactamente, quizás dudarías un segundo.
Acá va la versión corta, y después las partes que importan cuando algo se rompe.
Un puerto es un número, nada más
Tu Mac tiene una dirección IP en una red dada. Decenas de programas quieren usar esa conexión al mismo tiempo: un navegador, un servidor de desarrollo, Docker, Spotify, una VPN. La dirección IP lleva los datos hasta tu máquina, pero no dice nada sobre qué programa debería recibirlos.
El puerto es esa segunda mitad de la dirección. La IP lleva el paquete hasta el computador, el número de puerto lo lleva hasta el programa correcto.
Es un número de 16 bits, por eso hay exactamente 65.536, del 0 al 65535. No hay hardware involucrado, no hay un conector que puedas apuntar. Es una etiqueta que el sistema operativo usa para repartir los datos que llegan.
La regla que causa la mayoría de tus problemas
En una dirección dada, solo un proceso puede escuchar en un puerto a la vez.
Esa es toda la razón detrás de “address already in use”. No es un error, es el sistema operativo aplicando la regla que hace que los puertos funcionen. Si dos programas pudieran escuchar en el 3000, el kernel no tendría forma de decidir cuál recibe una conexión entrante.
Así que cuando tu servidor de desarrollo se niega a partir, no hay nada roto. Algo llegó primero, casi siempre una corrida anterior del mismo servidor que no cerró bien.
Escuchar no es lo mismo que conectarse
Esto confunde a mucha gente, así que vale la pena separarlo.
Un servidor escucha en un puerto. Reclama el 3000 y se queda esperando. Ese es el exclusivo, y es el que te muestra cualquier herramienta de puertos.
Un cliente se conecta desde un puerto. Cuando tu navegador abre localhost:3000, también usa un puerto de tu lado, elegido automáticamente. Se llaman puertos efímeros, y en macOS puedes ver el rango del que el sistema los saca:
sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
# net.inet.ip.portrange.first: 49152
# net.inet.ip.portrange.last: 65535
Nunca piensas en esos porque nunca los eliges. Cuando alguien habla de “qué está usando el puerto 3000”, casi siempre se refiere al lado que escucha.
Los puertos bajo 1024 necesitan root
Intenta ocupar el puerto 80 como usuario normal y macOS te detiene:
PermissionError: [Errno 13] Permission denied
Los puertos 0 al 1023 son privilegiados. Históricamente fue una medida de seguridad, con la idea de que solo un proceso confiable pudiera responder en el puerto donde todo el mundo espera encontrar HTTP.
Por eso los servidores de desarrollo usan 3000, 5173, 8000 u 8080 en vez del 80. No es convención porque sí, es la opción de menor fricción que no te obliga a usar sudo.
El TCP 3000 y el UDP 3000 son puertos distintos
TCP y UDP tienen cada uno su propio conjunto completo de 65.536 puertos. No chocan entre sí. Se puede comprobar: con el TCP 9955 ocupado y escuchando, ocupar el UDP 9955 al mismo tiempo funciona sin problema.
En la práctica casi todo lo que manejas localmente es TCP, así que rara vez aparece. Importa cuando estás leyendo la salida de una herramienta y te preguntas por qué un puerto se ve libre y ocupado a la vez. Fíjate en qué protocolo estás mirando.
Qué significa todo esto cuando un puerto queda pegado
Entender el modelo hace que el arreglo sea obvio. Algo está escuchando, quieres saber qué, y después quieres que se vaya.
lsof -i :3000
lsof parte del puerto y te entrega el proceso, que es la dirección que necesitas. Una vez que sabes el PID:
lsof -ti :3000 | xargs kill
-t imprime solo los IDs de proceso, xargs se los pasa todos a un solo kill. Esa última parte importa con servidores que corren varios workers, porque cada worker ocupa el mismo puerto y quieres que se vayan todos.
Para seguir
Si quieres la capa siguiente, escribimos sobre qué es realmente localhost y cómo están divididos los rangos de puertos.
Y si prefieres ver tus puertos en vez de escribir un comando cada vez, Bosun mantiene la lista en tu barra de menú, con el proceso, el contenedor y cualquier túnel abierto nombrados como corresponde. Gratis por 14 días, macOS 14 o posterior.
Ve esto en vez de escribirlo
Bosun vive en tu barra de menú y muestra cada puerto abierto en tu Mac, en vivo, mapeado al proceso detrás. Un clic para matarlo, SIGTERM primero. Útil la primera vez que pasa esto. Realmente útil la quinta vez que pasa en una misma tarde.