Escucha TCP / HTTP en puertos: ¿cómo pueden muchos usuarios compartir el mismo puerto?
Entonces, ¿qué sucede cuando un servidor escucha las conexiones entrantes en un puerto TCP? Por ejemplo, supongamos que tiene un servidor web en el puerto 80. Supongamos que su computadora tiene la dirección IP pública 24.14.181.229 y la persona que intenta conectarse con usted tiene la dirección IP 10.1.2.3. Esta persona puede conectarse con usted abriendo un socket TCP al 24.14.181.229:80. Suficientemente simple.
De manera intuitiva (y errónea), la mayoría de la gente asume que se parece a esto:
Local Computer | Remote Computer
--------------------------------
<local_ip>:80 | <foreign_ip>:80
^^ not actually what happens, but this is the conceptual model a lot of people have in mind.
Esto es intuitivo, porque desde el punto de vista del cliente, tiene una dirección IP y se conecta a un servidor en IP: PORT. Dado que el cliente se conecta al puerto 80, ¿su puerto también debe ser 80? Esto es algo sensato de pensar, pero en realidad no es lo que sucede. Si eso fuera correcto, solo podríamos atender a un usuario por dirección IP extranjera. Una vez que una computadora remota se conecta, entonces él toma la conexión del puerto 80 al puerto 80, y nadie más podría conectarse.
Hay que entender tres cosas:
1.) En un servidor, un proceso está escuchando en un puerto. Una vez que obtiene una conexión, la pasa a otro hilo. La comunicación nunca acapara el puerto de escucha.
2.) Las conexiones son identificadas de forma única por el sistema operativo mediante las siguientes 5 tuplas: (IP local, puerto local, IP remota, puerto remoto, protocolo). Si algún elemento de la tupla es diferente, entonces esta es una conexión completamente independiente.
3.) Cuando un cliente se conecta a un servidor, elige un puerto de origen de orden superior aleatorio y no utilizado . De esta manera, un solo cliente puede tener hasta ~ 64k conexiones al servidor para el mismo puerto de destino.
Entonces, esto es realmente lo que se crea cuando un cliente se conecta a un servidor:
Local Computer | Remote Computer | Role
-----------------------------------------------------------
0.0.0.0:80 | <none> | LISTENING
127.0.0.1:80 | 10.1.2.3:<random_port> | ESTABLISHED
Observando lo que realmente sucede
Primero, usemos netstat para ver qué está sucediendo en esta computadora. Usaremos el puerto 500 en lugar del 80 (porque un montón de cosas están sucediendo en el puerto 80 ya que es un puerto común, pero funcionalmente no hace ninguna diferencia).
netstat -atnp | grep -i ":500 "
Como se esperaba, la salida está en blanco. Ahora comencemos un servidor web:
sudo python3 -m http.server 500
Ahora, aquí está el resultado de ejecutar netstat nuevamente:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
Así que ahora hay un proceso que está escuchando activamente (Estado: LISTEN) en el puerto 500. La dirección local es 0.0.0.0, que es un código para "escuchar todas las direcciones IP". Un error fácil de cometer es escuchar solo en el puerto 127.0.0.1, que solo aceptará conexiones desde la computadora actual. Entonces, esto no es una conexión, esto solo significa que un proceso solicitó bind () al puerto IP, y ese proceso es responsable de manejar todas las conexiones a ese puerto. Esto sugiere la limitación de que solo puede haber un proceso por computadora escuchando en un puerto (hay formas de evitar eso usando multiplexación, pero este es un tema mucho más complicado). Si un servidor web está escuchando en el puerto 80, no puede compartir ese puerto con otros servidores web.
Así que ahora, conectemos a un usuario a nuestra máquina:
quicknet -m tcp -t localhost:500 -p Test payload.
Este es un script simple ( https://github.com/grokit/quickweb ) que abre un socket TCP, envía el payload ("Test payload." En este caso), espera unos segundos y se desconecta. Si vuelve a hacer netstat mientras esto sucede, se muestra lo siguiente:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:54240 ESTABLISHED -
Si se conecta con otro cliente y vuelve a hacer netstat, verá lo siguiente:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:26813 ESTABLISHED -
... es decir, el cliente usó otro puerto aleatorio para la conexión. Así que nunca hay confusión entre las direcciones IP.