¿Cómo descubre el servidor a qué puerto de cliente enviar?

26

Según tengo entendido, esto es lo que sucede cuando un cliente realiza una solicitud de conexión:

  1. El servidor estará vinculado a un número de puerto en particular. El número de puerto siempre está vinculado a un proceso de escucha. Como solo el servidor está escuchando las conexiones entrantes, no necesitamos vincularnos del lado del cliente
  2. El servidor seguirá escuchando ese número de puerto.
  3. El cliente enviará una connect()solicitud.
  4. El servidor aceptará la solicitud usando accept(). Tan pronto como el servidor acepta la solicitud del cliente, el kernel asigna un número de puerto aleatorio para el servidor send()y receive(), dado que el mismo número de puerto en el servidor no se puede usar para enviar y escuchar, y el puerto anterior todavía está escuchando nuevas conexiones

Dado todo eso, ¿cómo descubre el servidor en qué puerto está recibiendo el cliente? Sé que el cliente enviará segmentos TCP con un puerto de origen y un puerto de destino, por lo que el servidor utilizará el puerto de origen de ese segmento como su puerto de destino, pero ¿qué función llama el servidor para averiguar sobre ese puerto? Es accept()?

Subi Suresh
fuente
Relacionado: stackoverflow.com/questions/14388706/…
Pacerier el

Respuestas:

33

Es parte del encabezado TCP (o UDP, etc.) en el paquete. Entonces el servidor se entera porque el cliente se lo dice. Esto es similar a cómo descubre la dirección IP del cliente (que es parte del encabezado IP).

Por ejemplo, cada paquete TCP incluye un encabezado IP (con IP de origen, IP de destino y protocolo [TCP], como mínimo). Luego hay un encabezado TCP (con puerto de origen y destino, y más).

Cuando el núcleo recibe un paquete SYN (el inicio de una conexión TCP) con una IP remota de 10.11.12.13 (en el encabezado de IP) y un puerto remoto de 12345 (en el encabezado de TCP), entonces conoce la IP y el puerto remotos . Devuelve un SYN | ACK. Si recupera un ACK, la listenllamada devuelve un nuevo socket, configurado para esa conexión.

Un socket TCP se identifica de forma exclusiva por los cuatro valores (IP remota, IP local, puerto remoto, puerto local). Puede tener múltiples conexiones / sockets, siempre que al menos uno de ellos sea diferente.

Normalmente, el puerto local y la IP local serán los mismos para todas las conexiones a un proceso de servidor (por ejemplo, todas las conexiones a sshd estarán en local-ip: 22). Si una máquina remota realiza múltiples conexiones, cada una utilizará un puerto remoto diferente. Por lo tanto, todo menos el puerto remoto será el mismo, pero está bien, solo uno de los cuatro tiene que diferir.

Puede usar, por ejemplo, wirehsark para ver el paquete, y etiquetará todos los datos por usted. Aquí está el puerto de origen resaltado (observe que está resaltado en el paquete decodificado, así como el volcado hexadecimal en la parte inferior):

Wireshark mostrando un paquete TCP SYN

derobert
fuente
> Gracias por la explicación. Entonces quiso decir que el nuevo descriptor de socket del servidor (es decir, la tupla) obtenido después de accept () tendrá el puerto del cliente y los detalles de la dirección del cliente y usará ese nuevo servidor de descriptor de socket para enviar y recibir los datos a y desde el cliente. El nuevo descriptor del archivo de socket tendrá un nuevo número de puerto del servidor asignado por el núcleo, la IP del servidor, la IP del cliente y el puerto del cliente. ¿Estoy en lo cierto?
Subi Suresh
@SubiSuresh sí, la tupla se almacena dentro del núcleo, asociada con ese descriptor de archivo.
derobert
> Gracias derobert. Así que estoy concluyendo que el nuevo descriptor de socket del servidor tendrá el puerto del cliente y la dirección del cliente, que el servidor obtiene de accept (). ¿Entiendo que está bien?
Subi Suresh
@SubiSuresh Sí, eso es correcto. Desde el punto de vista de la aplicación, generalmente no le importa (excepto el registro). El núcleo se asegura de que los datos que usted write(etc.) vaya al lugar correcto.
derobert
> gracias por su ayuda y creo que entendí el punto. ;-)
Subi Suresh
2

La "solicitud de conexión (la connect()llamada al sistema del programa del cliente , por lo general) provoca un protocolo de enlace de tres vías . El primer paquete del protocolo de enlace de tres vías (del cliente al servidor) tiene el indicador SYN establecido e incluye el número de puerto TCP del programa del cliente kernel le asigna.

Puede ver esto en un artículo sobre los paquetes Nmap vs Natural SYN . La decodificación del paquete Nmap SYN tiene la frase "source.60058> dest.22". La decodificación del "paquete SYN legítimo" tiene la frase "source.35970> dest.80". Los dos paquetes SYN le dicen al núcleo remoto que los paquetes provienen del puerto TCP 60058 y del puerto 35970, respectivamente.

Bruce Ediger
fuente
> Pero Bruce eso está sucediendo en el back-end. Pero cómo mi servidor realmente está obteniendo detalles como el número de puerto debido normalmente a los programas de servidor del cliente, nunca he visto ninguna función para recuperar el puerto y la dirección del cliente
Subi Suresh
La llamada al sistema getpeername()debería permitirle hacerlo en cualquier socket abierto. La accept()llamada al sistema que debe usar el código del servidor para obtener un descriptor de archivo de socket para comunicarse con el cliente tiene un parámetro ("sockaddr" en mis páginas de manual) que contiene la dirección IP y el número de puerto TCP del cliente potencial.
Bruce Ediger
> No se preocupe si estobobo. De todas las entradas que obtuve, entendí que accept () tiene la estructura sockaddr_in llena con los detalles del cliente y el nuevo descriptor de socket del servidor devuelto después de accept () tendrá automáticamente el puerto y la dirección del cliente. Es por eso que podemos enviar usando send (nuevo descriptor de socket de servidor). Espero, ¿voy al grano? Esto es solo para asegurarme de que lo que he entendido es correcto.
Subi Suresh
@SubiSuresh - Creo que escribiste la verdad.
Bruce Ediger
1

El socket TCP es un socket orientado a la transmisión. Los dos descriptores de socket (propiedad de usted y su par) están conectados de manera confiable. Para que no tenga que preocuparse por el puerto del cliente, ¡simplemente escriba su descriptor de socket!

Además, siéntase libre de getsockname (2) si realmente quiere saber eso (para el registro tal vez)

b166er
fuente
0

La conexión está definida por una tupla (IP de origen, puerto de origen, IP de destino, puerto de destino). Las respuestas van al revés.

vonbrand
fuente
@vondrand Ese punto entendí von. Pero, ¿desde qué función llega a saber el servidor el número de puerto del cliente? Sin saber el número de puerto del cliente, ¿cómo se enviará? Puerto ?
Subi Suresh