Preguntas ilustradas de la entrevista de alta frecuencia con protocolo de enlace de tres vías y onda de cuatro vías de TCP (última versión de 2023)

Hola a todos, recientemente revisé una versión de las preguntas de la entrevista de protocolo de enlace de tres vías y onda de cuatro vías de TCP (última versión de 2023).

-----

No importa cuántas veces TCP me tortura, todavía lo trato como a mi primer amor.

Enorme, enorme, enorme, largo esquema, ¡comencemos! ¡Empecemos!

imagen

imagen


Comprensión básica de TCP

¿Cuáles son los formatos de encabezado TCP?

Primero echemos un vistazo al formato del encabezado TCP. Los colores marcados indican los campos que son más relevantes para este artículo. Los demás campos no se detallarán.

imagen

formato de encabezado TCP

Número de serie : un número aleatorio generado por la computadora al establecer una conexión como su valor inicial, que se transmite al host receptor a través del paquete SYN. ​​Cada vez que se envían datos, se "acumula el tamaño del "número de bytes de datos". ". Se utiliza para resolver el problema del desorden de paquetes de red.

Número de respuesta de confirmación : se refiere al número de secuencia de los siguientes datos recibidos "esperados". Una vez que el remitente recibe esta respuesta de confirmación, puede considerar que los datos anteriores a este número de secuencia se han recibido normalmente. Se utiliza para resolver el problema de la pérdida de paquetes.

Bit de control:

  • ACK1 Cuando  , el campo de "respuesta de reconocimiento" se vuelve válido. TCP estipula SYN que este bit debe establecerse excepto para el paquete  cuando se establece inicialmente la conexión 1 .

  • RST1 cuando , significa que se produce una excepción en la conexión TCP y la conexión debe desconectarse a la fuerza.

  • SYN1 Cuando , indica que desea establecer una conexión y establecer el valor inicial del número de serie en su campo "número de serie".

  • FIN1 Cuando , significa que no se enviarán más datos en el futuro y se espera que la conexión se desconecte. Cuando finaliza la comunicación y desea desconectarse, los hosts en ambos lados de la comunicación pueden intercambiar  FIN segmentos TCP con el bit 1 entre sí.

#¿Por qué necesitamos el protocolo TCP? ¿En qué capa funciona TCP?

IP La capa es "poco confiable" y no garantiza la entrega de paquetes de red, la entrega en orden de paquetes de red o la integridad de los datos en los paquetes de red.

imagen

La relación entre el modelo de referencia OSI y TCP/IP

Si es necesario garantizar la confiabilidad de los paquetes de datos de la red, entonces el  TCP protocolo de la capa superior (capa de transporte) debe ser responsable.

Debido a que TCP es un servicio de transmisión de datos confiable que trabaja en la capa de transporte, puede garantizar que los paquetes de red recibidos por el extremo receptor no estén dañados , no sean intermitentes, no redundantes y sean secuenciales.

#¿Qué es TCP?

TCP es un protocolo de comunicación de capa de transporte basado en flujo de bytes, confiable y orientado a conexión .

imagen

imagen

  • Orientado a la conexión : La conexión debe ser "uno a uno". A diferencia del protocolo UDP, un host puede enviar mensajes a varios hosts al mismo tiempo, es decir, no se puede realizar uno a muchos;

  • Confiable : No importa qué cambios de enlace ocurran en el enlace de red, TCP puede garantizar que un mensaje pueda llegar al extremo receptor;

  • Flujo de bytes : cuando los mensajes del usuario se transmiten a través del protocolo TCP, el sistema operativo puede "agrupar" los mensajes en varios mensajes TCP. Si el programa receptor no conoce los "límites del mensaje", no puede leer un mensaje válido. Usuario mensajes. Y los mensajes TCP están "ordenados". Cuando no se recibe el mensaje TCP "anterior", incluso si recibe primero el mensaje TCP posterior, no se puede enviar a la capa de aplicación para su procesamiento. Al mismo tiempo, "repetido" TCP Los paquetes se descartarán automáticamente.

#¿Qué es una conexión TCP?

Echemos un vistazo a cómo RFC 793 define "conexión":

Conexiones: Los mecanismos de control de flujo y confiabilidad descritos anteriormente requieren que los TCP inicialicen y mantengan cierta información de estado para cada flujo de datos. La combinación de esta información, incluidos sockets, números de secuencia y tamaños de ventana, se denomina conexión.

En pocas palabras, es información de estado que se utiliza para garantizar la confiabilidad y el mantenimiento del control de flujo. La combinación de esta información, incluido el socket, el número de secuencia y el tamaño de la ventana, se denomina conexión.

imagen

imagen

Entonces podemos saber que establecer una conexión TCP requiere que el cliente y el servidor lleguen a un consenso sobre las tres informaciones anteriores.

  • Socket : consta de dirección IP y número de puerto.

  • Número de serie : utilizado para solucionar problemas de avería, etc.

  • Tamaño de ventana : utilizado para control de flujo.

# ¿Cómo determinar de forma única una conexión TCP?

El cuádruple TCP puede determinar de forma única una conexión y el cuádruple incluye lo siguiente:

  • Dirección de la fuente

  • Puerto de origen

  • Dirección de destino

  • Puerto de destino

imagen

TCP cuádruple

Los campos (32 bits) de la dirección de origen y la dirección de destino están en el encabezado IP y la función es enviar el mensaje al otro host a través del protocolo IP.

Los campos puerto de origen y puerto de destino (16 bits) están en el encabezado TCP y su función es indicarle al protocolo TCP a qué proceso se debe enviar el mensaje.

Hay un servidor IP escuchando un puerto, ¿cuál es el número máximo de sus conexiones TCP?

El servidor generalmente escucha en un determinado puerto local, esperando la solicitud de conexión del cliente.

Por lo tanto, la IP y el puerto del cliente son variables y la fórmula de cálculo del valor teórico es la siguiente:

imagen

imagen

Para IPv4, la cantidad de IP de cliente es como máximo la  potencia 2 de  32 , y la cantidad de puertos de cliente es como máximo la potencia de  2 ,  16 es decir, la cantidad máxima de conexiones TCP en un solo servidor es aproximadamente  la potencia 2 de  48 .

Por supuesto, el número máximo de conexiones TCP simultáneas en el servidor está lejos de alcanzar el límite superior teórico y se verá afectado por los siguientes factores:

  • Límite de descriptores de archivos

    , cada conexión TCP es un archivo, si el descriptor del archivo está lleno, se producirán demasiados archivos abiertos. Linux impone tres restricciones sobre la cantidad de descriptores de archivos abiertos:

    • Nivel del sistema : el número máximo que se puede abrir en el sistema actual, mediante  cat /proc/sys/fs/file-max visualización;

    • Nivel de usuario : especifique el número máximo que un usuario puede abrir viendo  cat /etc/security/limits.conf ;

    • Nivel de proceso : El número máximo que un solo proceso puede abrir, mediante  cat /proc/sys/fs/nr_open visualización;

  • Límite de memoria : Cada conexión TCP ocupa una cierta cantidad de memoria. La memoria del sistema operativo es limitada. Si los recursos de memoria están llenos, se producirá OOM.

¿Cuál es la diferencia entre #UDP y TCP? ¿Cuáles son los diferentes escenarios de aplicación?

UDP no proporciona mecanismos de control complejos y utiliza IP para proporcionar servicios de comunicación "sin conexión".

El protocolo UDP es realmente simple, el encabezado solo tiene  8 1 byte (64 bits), el formato del encabezado UDP es el siguiente:

imagen

formato de encabezado UDP

  • Puertos de destino y origen: principalmente le indican al protocolo UDP a qué proceso se debe enviar el mensaje.

  • Longitud del paquete: este campo almacena la suma de la longitud del encabezado UDP y la longitud de los datos.

  • Suma de verificación: la suma de verificación está diseñada para proporcionar encabezados y datos UDP confiables para evitar la recepción de paquetes UDP dañados durante la transmisión de la red.

La diferencia entre TCP y UDP:

1. Conectar

  • TCP es un protocolo de capa de transporte orientado a la conexión y se debe establecer una conexión antes de transmitir datos.

  • UDP no requiere conexión y transmite datos inmediatamente.

2. Objetos de servicio

  • TCP es un servicio de dos puntos uno a uno, es decir, una conexión tiene solo dos puntos finales.

  • UDP admite comunicación interactiva uno a uno, uno a muchos y muchos a muchos.

3. Fiabilidad

  • TCP entrega datos de manera confiable y los datos pueden llegar en orden sin errores, pérdidas o duplicaciones.

  • UDP es una entrega de mejor esfuerzo y no garantiza una entrega confiable de datos. Pero podemos implementar un protocolo de transmisión confiable basado en el protocolo de transmisión UDP, como el protocolo QUIC.

4. Control de congestión, control de flujo.

  • TCP tiene mecanismos de control de congestión y control de flujo para garantizar la seguridad de la transmisión de datos.

  • UDP no, incluso si la red está muy congestionada, no afectará la velocidad de envío de UDP.

5. Gastos generales iniciales

  • La longitud del encabezado TCP es larga y habrá una cierta sobrecarga. Cuando no se usa el campo "opción", el encabezado es de un byte,  20 y será más largo si se usa el campo "opción".

  • El encabezado UDP tiene solo 8 bytes y es fijo, por lo que la sobrecarga es pequeña.

6. Método de transmisión

  • TCP es transmisión, sin límites, pero se garantiza que será secuencial y confiable.

  • UDP se envía paquete a paquete y tiene límites, pero pueden producirse pérdidas y desorden de paquetes.

7. Diferentes fragmentos

  • Si el tamaño de los datos TCP es mayor que el tamaño de MSS, se fragmentarán en la capa de transporte. Después de que el host de destino los reciba, también ensamblará el paquete de datos TCP en la capa de transporte. Si se pierde un fragmento en el medio, sólo es necesario transmitir el fragmento perdido.

  • Si el tamaño de los datos UDP es mayor que el tamaño de la MTU, se fragmentarán en la capa IP. Después de que el host de destino reciba los datos, los ensamblará en la capa IP y luego los pasará a la capa de transporte.

Escenarios de aplicación TCP y UDP:

Dado que TCP está orientado a la conexión y puede garantizar una entrega confiable de datos, a menudo se usa para:

  • FTP transferencia de archivos;

  • HTTP/HTTPS;

Debido a que UDP está orientado sin conexión, puede enviar datos en cualquier momento y el procesamiento de UDP en sí es simple y eficiente, por lo que a menudo se usa para:

  • Comunicación con un tamaño total de paquete pequeño, como  DNS , SNMP etc.;

  • Vídeo, audio y otras comunicaciones multimedia;

  • comunicaciones por radiodifusión;

¿Por qué el encabezado UDP no tiene un campo "Longitud del encabezado", pero el encabezado TCP tiene un campo "Longitud del encabezado"?

La razón es que TCP tiene un campo de "opción" de longitud variable , mientras que la longitud del encabezado UDP no cambia . No es necesario crear un campo adicional para registrar la longitud del encabezado UDP.

¿Por qué el encabezado UDP tiene un campo "Longitud del paquete" pero el encabezado TCP no tiene un campo "Longitud del paquete"?

Primero hablemos de cómo TCP calcula la longitud de los datos de la carga útil:

imagen

imagen

Entre ellos, la longitud total de la IP y la longitud del encabezado IP se conocen en el formato del encabezado IP. La longitud del encabezado TCP se conoce en el formato del encabezado TCP, por lo que se puede encontrar la longitud de los datos TCP.

Todos se sorprendieron en ese momento y preguntaron: "UDP también se basa en la capa IP, por lo que la longitud de los datos de UDP también se puede calcular mediante esta fórmula. ¿Por qué existe una" longitud de paquete "? "

Cuando me preguntan de esta manera, siento que la "longitud del paquete" de UDP es redundante.

Revisé mucha información y creo que hay dos afirmaciones más confiables:

  • La primera afirmación: porque para facilitar el diseño y procesamiento del hardware del dispositivo de red, la longitud del encabezado debe ser un  4 múltiplo entero de bytes. Si se elimina el campo "Longitud del paquete" de UDP, entonces la longitud del encabezado UDP no es un  4 múltiplo entero de bytes, por lo que creo que esto puede complementar el hecho de que la longitud del encabezado UDP es un múltiplo entero de  4 bytes, por lo que " Se agrega el campo "Longitud del paquete".

  • La segunda declaración: el protocolo UDP actual se desarrolla en base al protocolo IP, pero es posible que este no sea el caso en ese entonces. Puede depender de otros protocolos de capa de red que no proporcionan su propia longitud de mensaje o longitud de encabezado, por lo que el encabezado del mensaje UDP necesita tener una longitud de campos para el cálculo.

¿Pueden #TCP y UDP usar el mismo puerto?

Respuesta: .

En la capa de enlace de datos, los hosts de la LAN se encuentran a través de direcciones MAC. En la capa de Internet, las direcciones IP se utilizan para encontrar hosts o enrutadores interconectados en la red. En la capa de transporte, se requiere direccionamiento por puerto para identificar diferentes aplicaciones que se comunican simultáneamente en la misma computadora.

Por tanto, la función del "número de puerto" de la capa de transporte es distinguir los paquetes de datos de diferentes aplicaciones en el mismo host.

La capa de transporte tiene dos protocolos de transporte, TCP y UDP, que son dos módulos de software completamente independientes en el kernel.

Cuando el host recibe el paquete de datos, puede saber que el paquete de datos es TCP/UDP en el campo "número de protocolo" del encabezado del paquete IP, por lo que puede determinar qué módulo (TCP/UDP) enviar al TCP/UDP. módulo basado en esta información. El paquete se envía a la aplicación que se envía para su procesamiento en función del "número de puerto".

imagen

imagen

Por lo tanto, los respectivos números de puerto de TCP/UDP también son independientes entre sí. Por ejemplo, TCP tiene un número de puerto 80 y UDP también puede tener un número de puerto 80. No hay conflicto entre los dos.

Todavía hay muchos puntos de conocimiento que se pueden discutir sobre los puertos, por ejemplo, también pueden involucrar los siguientes problemas:

  • ¿Se pueden vincular varios procesos de servicio TCP al mismo puerto al mismo tiempo?

  • ¿Por qué aparece el mensaje de error "Dirección en uso" al reiniciar el proceso del servicio TCP? ¿Cómo evitarlo?

  • ¿Se puede reutilizar el puerto del cliente?

  • Si la conexión TCP del cliente tiene demasiados estados TIME_WAIT, ¿se agotarán los recursos del puerto y no se podrán establecer nuevas conexiones?

#Conexión TCP establecida

¿Qué es el proceso de protocolo de enlace de tres vías #TCP?

TCP es un protocolo orientado a la conexión, por lo que se debe establecer una conexión antes de usar TCP, y la conexión se establece mediante un protocolo de enlace de tres vías . El proceso de protocolo de enlace de tres vías es el siguiente:

imagen

Protocolo de enlace de tres vías TCP

  • Inicialmente, tanto el cliente como el servidor están en  CLOSE estado. Primero, el servidor escucha activamente un determinado puerto y está en el  LISTEN estado

imagen

El primer mensaje: mensaje SYN

  • El cliente inicializará aleatoriamente el número de secuencia ( client_isn), colocará este número de secuencia en el campo "número de secuencia" del encabezado TCP y establecerá  SYN la posición de la bandera para  1indicar  SYN el mensaje. Luego, el primer mensaje SYN se envía al servidor, lo que indica que se inició una conexión con el servidor. El mensaje no contiene datos de la capa de aplicación y el cliente se encuentra en el estado posterior  SYN-SENT .

imagen

El segundo mensaje: mensaje SYN + ACK

  • Después de que el servidor recibe el  SYN mensaje del cliente, primero inicializa aleatoriamente su propio número de secuencia ( server_isn), completa este número de secuencia en el campo "Número de serie" del encabezado TCP y luego completa el campo "Número de respuesta de confirmación" del encabezado TCP.  client_isn + 1Luego coloque  SYN la  ACK marca y en su posición  1. Finalmente, el mensaje se envía al cliente. El mensaje no contiene datos de la capa de aplicación. Después de eso, el servidor está en  SYN-RCVD estado.

imagen

El tercer mensaje: mensaje ACK

  • ACK Después de recibir el mensaje del servidor, el cliente debe responder al servidor con el último mensaje de respuesta. Primero, la posición del indicador del encabezado TCP del  mensaje de respuesta es, luego  1 se completa el campo "Número de respuesta de confirmación"  server_isn + 1 y finalmente se envía el mensaje. al servicio.final, esta vez el mensaje puede transportar los datos del cliente al servidor, y luego el cliente está en el  ESTABLISHED estado.

  • Después de que el servidor recibe el mensaje de respuesta del cliente, también ingresa  ESTABLISHED al estado.

Del proceso anterior, podemos encontrar que el tercer apretón de manos puede transportar datos, pero los dos primeros no pueden transportar datos.Esta también es una pregunta frecuente en las entrevistas.

Una vez que se completa el protocolo de enlace de tres vías y ambas partes se encuentran en  ESTABLISHED el estado, se establece la conexión y el cliente y el servidor pueden enviarse datos entre sí.

#¿Cómo comprobar el estado de TCP en el sistema Linux?

Verifique el estado de la conexión TCP mediante  netstat -napt comandos en Linux.

imagen

Ver el estado de la conexión TCP

#¿Por qué es un apretón de manos de tres vías? ¿No dos o cuatro veces?

Creo que la respuesta más común de todos es: "Porque el protocolo de enlace de tres vías puede garantizar que ambas partes tengan la capacidad de recibir y enviar".

Esta respuesta es sí, pero es unilateral y no indica la razón principal.

Anteriormente sabíamos qué  es una conexión TCP :

  • Cierta información de estado se utiliza para garantizar la confiabilidad y el mantenimiento del control de flujo. La combinación de esta información, incluido  el socket, el número de secuencia y el tamaño de la ventana, se denomina conexión.

Por lo tanto, lo importante es por qué el protocolo de enlace de tres vías puede inicializar el socket, el número de secuencia y el tamaño de la ventana y establecer una conexión TCP.

A continuación, analice los motivos del apretón de manos de tres vías desde tres aspectos:

  • Solo el protocolo de enlace de tres vías puede evitar la inicialización repetida de conexiones históricas (la razón principal)

  • El protocolo de enlace de tres vías puede sincronizar los números de serie iniciales de ambas partes

  • Un apretón de manos de tres vías puede evitar el desperdicio de recursos

Razón 1: evitar conexiones históricas

Echemos un vistazo a las principales razones para utilizar un protocolo de enlace de tres vías para conexiones TCP según lo establecido en RFC 793 :

La razón principal del protocolo de enlace de tres vías es evitar que los antiguos inicios de conexión duplicados causen confusión.

En pocas palabras, la razón principal para el protocolo de enlace de tres vías es evitar la confusión causada por antiguas inicializaciones de conexiones duplicadas.

Consideremos un escenario en el que el cliente envía primero un mensaje SYN (seq = 90) y luego el cliente falla. Además, el mensaje SYN es bloqueado por la red y el servidor no lo recibe. Luego, después de que el cliente se reinicia, la conexión se restablece en el servidor y se envía un mensaje SYN (seq = 100) (¡ nota! No es una retransmisión de SYN, el número de secuencia del SYN retransmitido es el mismo ).

Vea cómo el protocolo de enlace de tres vías bloquea conexiones históricas:

imagen

Apretón de manos de tres vías para evitar conexiones históricas

El cliente envía múltiples mensajes SYN (todos los mismos cuatro tuplas) para establecer una conexión. En el caso de congestión de la red :

  • Un "mensaje SYN antiguo" llega al servidor antes que el "mensaje SYN más reciente", luego el servidor devolverá un  SYN + ACK mensaje al cliente. El número de confirmación en este mensaje es 91 (90+1).

  • Después de que el cliente lo recibe, descubre que el número de confirmación que espera recibir debe ser 100 + 1 en lugar de 90 + 1, por lo que devolverá un mensaje RST.

  • Después de que el servidor reciba el mensaje RST, liberará la conexión.

  • Una vez que llega el último SYN al servidor, el cliente y el servidor normalmente pueden completar el protocolo de enlace de tres vías.

El "mensaje SYN antiguo" mencionado anteriormente se denomina conexión histórica. La razón principal por la que TCP utiliza un protocolo de enlace de tres vías para establecer una conexión es para evitar que la "conexión histórica" ​​inicialice la conexión .

CONSEJO

Mucha gente ha preguntado si el servidor recibe el "nuevo mensaje SYN" antes de recibir el mensaje RST, es decir, el orden en que el servidor recibe el mensaje del cliente es: "mensaje SYN antiguo" -> "mensaje SYN nuevo", ¿qué sucederá en este momento?

Cuando el servidor recibe el mensaje SYN por primera vez, es decir, cuando recibe el "mensaje SYN antiguo", responderá con  SYN + ACK un mensaje al cliente. El número de confirmación en este mensaje es 91 (90 + 1).

Luego, cuando se reciba nuevamente un "nuevo mensaje SYN", se devolverá al cliente un mensaje de confirmación de desafío. Este mensaje de confirmación no confirma la recepción del "nuevo mensaje SYN", sino el último número de confirmación de confirmación, que es 91 ( 90+1). Por lo tanto, cuando el cliente recibe este mensaje ACK, descubre que el número de confirmación que espera recibir debe ser 101 en lugar de 91, por lo que devuelve un mensaje RST.

Si se trata de una conexión de dos protocolos de enlace, las conexiones históricas no se pueden bloquear , entonces, ¿por qué el protocolo de dos protocolos TCP no puede evitar las conexiones históricas?

Permítanme decir la conclusión primero, principalmente porque en el caso de dos apretones de manos, el servidor no tiene un estado intermedio para que el cliente evite conexiones históricas, lo que hace que el servidor potencialmente establezca una conexión histórica, lo que resulta en un desperdicio de recursos .

Piénselo, en el caso de dos apretones de manos, el servidor ingresa al estado ESTABLECIDO después de recibir el mensaje SYN, lo que significa que puede enviar datos a la otra parte en este momento, pero el cliente aún no ha ingresado al estado ESTABLECIDO. que esta La segunda vez es una conexión histórica. Si el cliente determina que esta conexión es una conexión histórica, devolverá un mensaje RST para desconectar la conexión. El servidor entrará en el estado ESTABLECIDO durante el primer protocolo de enlace, para que pueda enviar datos. Pero no sabe que se trata de una conexión histórica y se desconectará sólo después de recibir el mensaje RST.

imagen

Dos apretones de manos no pueden evitar conexiones históricas

Se puede ver que si se utiliza un protocolo de enlace doble para establecer una conexión TCP, el servidor no bloquea la conexión histórica antes de enviar datos al cliente, lo que hace que el servidor establezca una conexión histórica y envíe datos en vano. recursos.

Por lo tanto, para solucionar este fenómeno, lo mejor es bloquear las conexiones históricas antes de que el servidor envíe datos, es decir, antes de establecer una conexión, para no desperdiciar recursos, para implementar esta función se requiere un protocolo de enlace de tres vías .

Por lo tanto, la razón principal por la que TCP utiliza un protocolo de enlace de tres vías para establecer una conexión es para evitar que las "conexiones históricas" inicialicen la conexión.

CONSEJO

Alguien preguntó: el cliente puede enviar datos después de enviar el protocolo de enlace de tres vías (mensaje de confirmación), pero el lado pasivo todavía está en el estado syn_received. Si se pierde el reconocimiento, ¿se desperdiciarán los datos enviados por el cliente?

No, incluso si el servidor todavía está en el estado syn_received y recibe los datos enviados por el cliente, aún se puede establecer la conexión y el paquete de datos se puede recibir normalmente. Esto se debe a que hay un bit de identificación de reconocimiento en el mensaje de datos y un número de confirmación, que confirma la recepción del segundo protocolo de enlace. Como se muestra abajo:

imagen

imagen

Por lo tanto, cuando el servidor recibe este mensaje de datos, puede establecer una conexión normalmente y luego puede recibir el paquete de datos normalmente.

Razón 2: sincronizar los números de secuencia iniciales de ambas partes

Ambas partes que se comunican en el protocolo TCP deben mantener un "número de secuencia". El número de secuencia es un factor clave para una transmisión confiable. Su función es:

  • El receptor puede eliminar datos duplicados;

  • El receptor puede recibir los paquetes de datos en orden según sus números de secuencia;

  • Puede identificar cuáles de los paquetes de datos enviados han sido recibidos por la otra parte (conocido a través del número de secuencia en el mensaje ACK);

SYN Se puede ver que el número de serie juega un papel muy importante en la conexión TCP, por lo que cuando el cliente envía un mensaje con  el "número de serie inicial", el servidor debe  devolver un ACK mensaje de respuesta, indicando que el mensaje SYN del cliente ha sido servido Si el cliente lo recibe con éxito, cuando el servidor envía el "número de serie inicial" al cliente, aún necesita obtener una respuesta del cliente, de esta manera, los números de serie iniciales de ambas partes se pueden sincronizar de manera confiable.

imagen

Cuatro apretones de manos y tres apretones de manos

El protocolo de enlace de cuatro vías en realidad puede sincronizar de manera confiable los números de serie de inicialización de ambas partes, pero dado que el segundo y tercer paso se pueden optimizar en un solo paso , se convierte en un "apretón de enlace de tres vías".

Los dos apretones de manos solo garantizan que la otra parte pueda recibir con éxito el número de secuencia inicial de una parte, pero no hay forma de garantizar que los números de secuencia iniciales de ambas partes puedan ser confirmados y recibidos.

Razón 3: Evitar desperdiciar recursos

SYN Si solo hay "dos apretones de manos", cuando el mensaje  generado por el cliente está  bloqueado en la red y el cliente no recibe ACK el mensaje, lo reenviará  SYN . Como no hay un tercer apretón de manos, el servidor no sabe si el cliente lo ha recibido.  ACK Mensaje de respuesta, por lo que cada vez que el servidor recibe uno,  SYN solo puede establecer activamente una conexión primero . ¿Qué pasará?

SYN Si el mensaje  enviado por el cliente  está bloqueado en la red y el mensaje se envía varias veces, el servidor establecerá múltiples enlaces redundantes no válidosSYN  después de recibir la solicitud , lo que provocará un desperdicio innecesario de recursos.

imagen

Dos apretones de manos provocarán un desperdicio de recursos

Es decir, cuando dos apretones de manos causarán retención de mensajes, el servidor acepta repetidamente  SYN mensajes de solicitud de conexión inútiles, lo que resulta en una asignación repetida de recursos.

CONSEJO

Mucha gente pregunta: ¿no pueden los dos apretones de manos descartar también los mensajes históricos sintéticos en función de la información del contexto?

Los dos apretones de manos aquí se basan en el supuesto de que "dado que no hay un tercer apretón de manos, el servidor no sabe si el cliente ha recibido el  ACK mensaje de confirmación de establecimiento de conexión enviado por él mismo, por lo que cada vez que recibe uno,  SYN solo puede establecer activamente un conexión primero." Este escenario.

Por supuesto, debe implementarlo como un protocolo de enlace de tres vías. También es posible descartar los mensajes históricos de sincronización según el contexto. No existe una implementación específica del protocolo de enlace de dos vías, por lo que puede hacer cualquier suposición.

resumen

Cuando TCP establece una conexión, el protocolo de enlace de tres vías puede evitar el establecimiento de conexiones históricas, reducir la sobrecarga de recursos innecesaria en ambos lados y ayudar a ambas partes a sincronizar los números de secuencia de inicialización . Los números de secuencia garantizan que los paquetes de datos no se repitan, descarten ni transmitan en orden.

Razones para no utilizar "apretón de manos de dos vías" y "apretón de manos de cuatro vías":

  • "Dos apretones de manos": no puede evitar el establecimiento de conexiones históricas, lo que provocará un desperdicio de recursos en ambas partes, y no puede sincronizar de manera confiable los números de serie de ambas partes;

  • "Apretón de manos de cuatro vías": El apretón de manos de tres vías es el mínimo teórico para establecer una conexión confiable, por lo que no es necesario utilizar más tiempos de comunicación.

# ¿Por qué se requiere que el número de secuencia de inicialización sea diferente cada vez que se establece una conexión TCP?

Hay dos razones principales:

  • Para evitar que la siguiente conexión reciba mensajes históricos con la misma tupla de cuatro (aspecto principal);

  • Por razones de seguridad, la otra parte evita que los mensajes TCP con el mismo número de secuencia falsificados por piratas informáticos sean recibidos por la otra parte;

A continuación, hablemos del primer punto en detalle.

Supongamos que cada vez que se establece una conexión, los números de secuencia de inicialización del cliente y el servidor comienzan desde 0:

imagen

imagen

El proceso es el siguiente:

  • El cliente y el servidor establecen una conexión TCP, la red bloquea el paquete de datos enviado por el cliente y luego el paquete de datos se retransmite después de un tiempo de espera. En este momento, el dispositivo del servidor se apaga y se reinicia, y la conexión establecido con el cliente desaparece. , por lo que el mensaje RST se enviará al recibir el paquete de datos del cliente.

  • Inmediatamente después, el cliente estableció una conexión con el servidor con la misma tupla de cuatro que la conexión anterior;

  • Una vez establecida la nueva conexión, el paquete de datos bloqueado por la red en la conexión anterior simplemente llega al servidor y el número de secuencia del paquete de datos se encuentra dentro de la ventana de recepción del servidor, por lo que se recibirá el paquete de datos. normalmente por el servidor, lo que provocará confusión en los datos.

Se puede ver que si los números de secuencia de inicialización del cliente y del servidor son los mismos cada vez que se establece una conexión, es fácil que la siguiente conexión reciba mensajes históricos con la misma tupla de cuatro .

Si los números de secuencia de inicialización del cliente y del servidor son "diferentes" cada vez que se establece una conexión, existe una alta probabilidad de que el número de secuencia del mensaje histórico "no esté en" la ventana de recepción de la otra parte, evitando así que se envíen mensajes históricos. en gran medida, como la siguiente imagen:

imagen

imagen

Por el contrario, si los números de secuencia de inicialización del cliente y del servidor son "los mismos" cada vez que se establece una conexión, existe una alta probabilidad de que el número de secuencia del mensaje histórico simplemente "suceda" dentro de la ventana de recepción del otra parte, lo que hace que el mensaje histórico sea nuevo. La conexión se recibió exitosamente.

Por lo tanto, cada número de secuencia de inicialización es diferente en gran medida, lo que puede evitar en gran medida que el mensaje histórico sea recibido por la siguiente conexión con el mismo cuádruple. Tenga en cuenta que esto se evita en gran medida, pero no por completo (porque el número de secuencia tendrá el mismo número de secuencia). problema de ajuste, por lo que es necesario utilizar el mecanismo de marca de tiempo para juzgar los mensajes históricos)

# ¿Cómo se genera aleatoriamente el número de secuencia inicial ISN?

El arranque  ISN se basa en el reloj, +1 cada 4 microsegundos, y una revolución tarda 4,55 horas.

RFC793 menciona el algoritmo de generación aleatoria del número de secuencia de inicialización ISN: ISN = M + F (localhost, localport, remotohost, remotoport).

  • M Es un temporizador que aumenta en 1 cada 4 microsegundos.

  • F Es un algoritmo Hash que genera un valor aleatorio basado en la IP de origen, la IP de destino, el puerto de origen y el puerto de destino. Para garantizar que el mundo exterior no pueda calcular fácilmente el algoritmo Hash, utilizar el algoritmo MD5 es una mejor opción.

Se puede ver que el número aleatorio se incrementará según el temporizador del reloj y es básicamente imposible generar aleatoriamente el mismo número de secuencia de inicialización.

#Dado que la capa IP estará fragmentada, ¿por qué la capa TCP necesita MSS?

Primero conozcamos MTU y MSS.

imagen

MTU y MSS

  • MTU: La longitud máxima de un paquete de red, generalmente en  1500 bytes en Ethernet;

  • MSS: La longitud máxima de datos TCP que se pueden acomodar en un paquete de red después de eliminar los encabezados IP y TCP;

Si todo el mensaje TCP (encabezado + datos) se entrega a la capa IP para su fragmentación, ¿qué pasará?

Cuando la capa IP tiene  MTU datos que exceden el tamaño (encabezado TCP + datos TCP) a enviar, la capa IP fragmentará los datos en varias partes para garantizar que cada fragmento sea más pequeño que la MTU. Después de fragmentar un datagrama IP, la capa IP del host de destino lo vuelve a ensamblar y luego lo entrega a la capa de transporte TCP superior.

Esto parece estar en orden, pero existen peligros ocultos, por lo que cuando se pierde un fragmento de IP, se deben retransmitir todos los fragmentos del mensaje IP completo .

Debido a que la capa IP en sí no tiene un mecanismo de retransmisión de tiempo de espera, TCP en la capa de transporte es responsable del tiempo de espera y la retransmisión.

Cuando se pierde un fragmento de IP, la capa IP del receptor no puede ensamblar un mensaje TCP completo (encabezado + datos) y no puede enviar el mensaje de datos a la capa TCP, por lo que el receptor no responderá con ACK al remitente, porque el El remitente no puede recibir el mensaje de confirmación ACK durante mucho tiempo, se activará una retransmisión de tiempo de espera y se reenviará el "mensaje TCP completo (encabezado + datos)".

Por tanto, se puede saber que la transmisión de fragmentación por la capa IP es muy ineficiente.

Por lo tanto, para lograr el mejor rendimiento de transmisión, el protocolo TCP generalmente negocia el valor MSS de ambas partes al establecer una conexión . Cuando la capa TCP descubre que los datos exceden el MSS, primero los fragmentará. Por supuesto, la longitud del paquete IP formado por él No será más grande que la MTU y, naturalmente, no hay necesidad de fragmentación de IP.

imagen

Negociar MSS durante la fase de protocolo de enlace

Después de la fragmentación de la capa TCP, si se pierde un fragmento de TCP, se utilizará MSS como unidad al retransmitir , en lugar de retransmitir todos los fragmentos, lo que aumenta en gran medida la eficiencia de la retransmisión.

#¿Qué pasa si se pierde el primer apretón de manos?

Cuando el cliente quiere establecer una conexión TCP con el servidor, lo primero que debe enviar es el mensaje SYN y luego ingresar el  SYN_SENT estado.

Después de eso, si el cliente no recibe el mensaje SYN-ACK del servidor (el segundo protocolo de enlace), activará el mecanismo de "retransmisión de tiempo de espera" para retransmitir el mensaje SYN y el mensaje SYN retransmitido Los números de serie son todos iguales .

Diferentes versiones del sistema operativo pueden tener diferentes tiempos de espera, algunos son de 1 segundo y otros de 3 segundos. Este tiempo de espera está codificado en el kernel. Si desea cambiarlo, debe volver a compilar el kernel, lo cual es problemático.

Cuando el cliente no recibe el mensaje SYN-ACK del servidor después de 1 segundo, el cliente reenviará el mensaje SYN ¿Cuántas veces se reenviará?

En Linux, el número máximo de retransmisiones de mensajes SYN del cliente está  tcp_syn_retriescontrolado por los parámetros del kernel. Este parámetro se puede personalizar y el valor predeterminado es generalmente 5.

# cat /proc/sys/net/ipv4/tcp_syn_retries
5

Por lo general, la primera retransmisión del tiempo de espera es después de 1 segundo, la segunda retransmisión del tiempo de espera es después de 2 segundos, la tercera retransmisión del tiempo de espera es después de 4 segundos, la cuarta retransmisión del tiempo de espera es después de 8 segundos y la segunda retransmisión del tiempo de espera es después de 8 segundos. son después de 16 segundos de tiempo de espera de retransmisión. Así es, cada tiempo de espera es el doble que la última vez .

Después de la quinta retransmisión de tiempo extra, continuará esperando 32 segundos. Si el servidor aún no responde con ACK, el cliente ya no enviará paquetes SYN y luego desconectará la conexión TCP.

Por lo tanto, el tiempo total necesario es 1+2+4+8+16+32=63 segundos, aproximadamente 1 minuto.

Por ejemplo, suponiendo que el valor del parámetro tcp_syn_retries es 3, cuando los paquetes SYN del cliente se pierdan constantemente en la red, ocurrirá el siguiente proceso:

imagen

imagen

Proceso específico:

  • Cuando el cliente agota el tiempo de espera y retransmite mensajes SYN 3 veces, dado que tcp_syn_retries es 3, se ha alcanzado el número máximo de retransmisiones, por lo que esperará un rato (el tiempo es el doble del último tiempo de espera). la respuesta del servidor Después del segundo protocolo de enlace (mensaje SYN-ACK), el cliente se desconectará.

¿Qué pasa si se pierde la segunda ola?

Cuando el servidor recibe la primera ola del cliente, enviará un mensaje de confirmación ACK, en este momento la conexión del servidor ingresa al  CLOSE_WAIT estado.

También mencionamos anteriormente que el mensaje ACK no se retransmitirá, por lo que si se pierde la segunda ola del servidor, el cliente activará el mecanismo de retransmisión de tiempo de espera y retransmitirá el mensaje FIN hasta que reciba la primera ola del servidor. Salude dos veces o alcance el máximo número de retransmisiones.

Por ejemplo, suponiendo que el valor del parámetro tcp_orphan_retries es 2, cuando la segunda ola siempre se pierde, el proceso que ocurre es el siguiente:

imagen

imagen

Proceso específico:

  • Cuando el cliente agota el tiempo de espera y retransmite el mensaje FIN 2 veces, dado que tcp_orphan_retries es 2, se ha alcanzado el número máximo de retransmisiones, por lo que espera un rato (el tiempo es el doble del último tiempo de espera). la respuesta del servidor, la segunda ola (mensaje ACK), luego el cliente se desconectará.

Permítanme mencionar aquí que cuando el cliente recibe la segunda ola, es decir, después de recibir el mensaje ACK enviado por el servidor, el cliente estará en un estado,  FIN_WAIT2 en este estado debe esperar a que el servidor envíe la tercera ola. , es decir, el mensaje FIN del terminal de servicio.

Para la conexión cerrada por la función de cierre, dado que ya no se pueden enviar ni recibir datos, FIN_WAIT2 el estado no puede durar demasiado y  tcp_fin_timeout la duración de la conexión en este estado está controlada. El valor predeterminado es 60 segundos.

Esto significa que para la conexión cerrada llamando a cerrar, si no se recibe ningún mensaje FIN después de 60 segundos, la conexión del cliente (parte cercana activa) se cerrará directamente, como se muestra en la siguiente figura:

imagen

imagen

Sin embargo, tenga en cuenta que si la parte de cierre activa utiliza la función de apagado para cerrar la conexión y especifica que solo la dirección de envío está cerrada, pero la dirección de recepción no está cerrada, significa que la parte de cierre activa aún puede recibir datos.

En este momento, si la parte que cierra activamente no ha recibido la tercera ola, la conexión de la parte que cierra activamente permanecerá en el estado  FIN_WAIT2 ( tcp_fin_timeout la conexión cerrada por cierre no se puede controlar). Como se muestra abajo:

imagen

imagen

¿Qué pasa si se pierde la tercera ola?

Cuando el servidor (parte de cierre pasiva) recibe el mensaje FIN del cliente (parte de cierre activa), el kernel responderá automáticamente con ACK y la conexión estará en estado. Como sugiere el nombre, significa esperar a que llame el proceso de la aplicación  CLOSE_WAIT . la función de cierre para cerrar la conexión.

En este momento, el kernel no tiene derecho a cerrar la conexión en nombre del proceso, y el proceso debe llamar activamente a la función de cierre para activar el servidor para enviar un mensaje FIN.

Cuando el servidor está en el estado CLOSE_WAIT y se llama a la función de cierre, el kernel enviará un mensaje FIN y la conexión ingresará al estado LAST_ACK al mismo tiempo, esperando que el cliente devuelva ACK para confirmar que la conexión está cerrada.

Si no se recibe el ACK durante mucho tiempo, el servidor reenviará el mensaje FIN y el número de retransmisiones aún está  tcp_orphan_retriecontrolado por el parámetro s, que es el mismo que el método de control del número de retransmisiones del mensaje FIN reenviado. por el cliente.

Por ejemplo, suponiendo  tcp_orphan_retries = 3, cuando la tercera onda siempre se pierde, el proceso que ocurre es el siguiente:

imagen

imagen

Proceso específico:

  • Cuando el servidor retransmite el mensaje de la tercera ola 3 veces, dado que tcp_orphan_retries es 3, se alcanza el número máximo de retransmisiones, por lo que espera otro período de tiempo (el tiempo es 2 veces el último tiempo de espera). Si no recibe la cuarta ola (mensaje ACK), el servidor se desconectará.

  • Debido a que el cliente cierra la conexión a través de la función de cierre, hay un límite de tiempo en el estado FIN_WAIT_2. Si el cliente aún no recibe la tercera ola (mensaje FIN) del servidor dentro del tiempo tcp_fin_timeout, el cliente se desconectará.

#Se pierde la cuarta ola, ¿qué pasará?

Cuando el cliente recibe el mensaje FIN de la tercera ola del servidor, responderá con un mensaje ACK, que es la cuarta ola, en este momento la conexión del cliente entra en el estado  TIME_WAIT .

En los sistemas Linux, el estado TIME_WAIT durará 2 MSL antes de entrar en el estado de apagado.

Entonces, el servidor (parte de cierre pasivo) todavía está en el estado LAST_ACK antes de recibir el mensaje ACK.

Si el mensaje ACK de la cuarta ola no llega al servidor, el servidor reenviará el mensaje FIN y el número de reenvíos aún estará  tcp_orphan_retries controlado por los parámetros introducidos anteriormente.

Por ejemplo, suponiendo que tcp_orphan_retries es 2, cuando la cuarta ola siempre se pierde, el proceso que ocurre es el siguiente:

imagen

imagen

Proceso específico:

  • Cuando el servidor retransmite el mensaje de onda por tercera vez y llega a 2, debido a que tcp_orphan_retries es 2, se alcanza el número máximo de retransmisiones, así que espere un momento (el tiempo es el doble del tiempo de espera anterior), si aún falla para recibir El cliente saluda por cuarta vez (mensaje ACK), luego el servidor se desconectará.

  • Después de recibir la tercera ola, el cliente ingresará al estado TIME_WAIT e iniciará un temporizador con una duración de 2 MSL. Si el cliente recibe la tercera ola (mensaje FIN) nuevamente en el camino, reiniciará el temporizador. Cuando espere después de 2 MSL, el cliente se desconectará.

#¿Por qué el tiempo de espera de TIME_WAIT es 2MSL?

MSL Es la vida útil máxima del segmento, la vida útil máxima de un paquete , que es el tiempo más largo que existe cualquier paquete en la red, y el paquete se descartará después de este tiempo. Debido a que el mensaje TCP se basa en el protocolo IP, y hay un  TTL campo en el encabezado IP, que es el número máximo de rutas por las que puede pasar el datagrama IP, este valor se reduce en 1 cada vez que pasa por un enrutador que lo procesa. Cuando este valor es 0, el datagrama se descartará y se enviará un mensaje ICMP para notificar al host de origen.

La diferencia entre MSL y TTL: la unidad de MSL es el tiempo, mientras que TTL es el número de saltos de enrutamiento. Por lo tanto,  MSL debe ser mayor o igual al tiempo en que TTL consume 0 para garantizar que el paquete se haya destruido de forma natural.

El valor TTL es generalmente 64. Linux establece el MSL en 30 segundos, lo que significa que Linux cree que el tiempo para que un paquete de datos pase a través de 64 enrutadores no excederá los 30 segundos. Si excede, se considerará que el paquete ha desaparecido. en la red .

TIME_WAIT espera 2 veces el MSL. Una explicación más razonable es: puede haber paquetes de datos del remitente en la red. Cuando estos paquetes de datos del remitente son procesados ​​por el receptor, enviarán una respuesta a la otra parte, por lo que tienes que esperar de un lado a otro , el doble de tiempo .

Por ejemplo, si la parte de cierre pasiva no recibe el último mensaje ACK para la desconexión, se activará un tiempo de espera FIN para reenviar el mensaje y la otra parte reenviará el ACK a la parte de cierre pasiva después de recibir el FIN.MSL.

Se puede ver  que la duración de 2MSL  es en realidad equivalente a permitir que el paquete se pierda al menos una vez . Por ejemplo, si el ACK se pierde en un MSL, el FIN reenviado por la parte pasiva llegará al segundo MSL y la conexión en el estado TIME_WAIT puede manejarlo.

¿Por qué no 4 u 8 MSL de duración? Se puede imaginar una red defectuosa con una tasa de pérdida de paquetes del 1%. La probabilidad de que se pierdan dos paquetes consecutivos es solo de 1/10.000. Esta probabilidad es demasiado pequeña. Ignorarla es más rentable que resolverla.

2MSL El tiempo comienza desde que el cliente envía ACK después de recibir FIN . Si está dentro del tiempo TIME-WAIT, debido a que el ACK del cliente no se transmite al servidor y el cliente recibe el mensaje FIN reenviado por el servidor, se restablecerá el tiempo  2MSL .

En los sistemas Linux,  2MSL el valor predeterminado es  60 segundos, por lo que uno  MSL son  30 segundos. El sistema Linux permanece en TIME_WAIT durante un tiempo fijo de 60 segundos .

Su nombre definido en el código del kernel de Linux es TCP_TIMEWAIT_LEN:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT 
                                    state, about 60 seconds  */

Si desea modificar la longitud de TIME_WAIT, solo puede modificar el valor de TCP_TIMEWAIT_LEN en el código del kernel de Linux y volver a compilar el kernel de Linux.

¿Por qué necesitamos el estado TIME_WAIT?

Solo la parte que inicie activamente el cierre de la conexión tendrá  TIME-WAIT estado.

El estado TIME-WAIT es necesario principalmente por dos motivos:

  • Evitar que los datos de las conexiones históricas se reciban incorrectamente en conexiones posteriores con la misma cuádruple;

  • Asegúrese de que la parte que "cierra pasivamente la conexión" pueda cerrarse correctamente;

Razón 1: Para evitar que los datos de las conexiones históricas se reciban incorrectamente en conexiones posteriores con el mismo grupo de cuatro tuplas.

Para comprender mejor este motivo, primero comprendamos el número de secuencia (SEQ) y el número de secuencia inicial (ISN).

  • El número de secuencia es un campo de encabezado de TCP, que identifica un byte del flujo de datos desde el remitente de TCP al receptor de TCP. Debido a que TCP es un protocolo confiable para flujos de bytes, para garantizar la secuencia y confiabilidad de los mensajes, TCP cada Al byte en cada dirección de transmisión se le asigna un número, para facilitar la confirmación después de una transmisión exitosa, la retransmisión después de una pérdida y garantizar que no habrá desorden en el extremo receptor. El número de secuencia es un número sin signo de 32 bits, por lo que vuelve a 0 después de alcanzar 4G .

  • Número de secuencia inicial , cuando TCP establece una conexión, tanto el cliente como el servidor generarán un número de secuencia inicial, que es un número aleatorio generado en función del reloj para garantizar que cada conexión tenga un número de secuencia inicial diferente. El número de secuencia de inicialización se puede considerar como un contador de 32 bits: el valor del contador aumenta en 1 cada 4 microsegundos y un ciclo dura 4,55 horas .

Cogí un paquete para todos. La secuencia en la figura siguiente es el número de serie y los cuadros rojos son los números de serie iniciales generados por el cliente y el servidor respectivamente.

imagen

Diagrama de captura de paquetes TCP

Sabemos por lo anterior que el número de serie y el número de serie de inicialización no aumentan infinitamente y se ajustarán al valor inicial, lo que significa que los datos antiguos y nuevos no se pueden juzgar en función del número de serie .

Suponiendo que TIME-WAIT no tiene tiempo de espera o que el tiempo es demasiado corto, ¿qué pasará después de que llegue el paquete de datos retrasado?

imagen

El tiempo de espera es demasiado corto, se reciben paquetes de datos de conexiones antiguas

Como se muestra arriba:

  • SEQ = 301 El mensaje enviado por el servidor antes de cerrar la conexión  fue retrasado por la red.

  • Luego, el servidor vuelve a abrir una nueva conexión con las mismas 4 tuplas, y la previamente retrasada  SEQ = 301 llega al cliente, y el número de secuencia del paquete de datos está justo dentro de la ventana de recepción del cliente, por lo que el cliente normalmente recibirá este paquete de datos. , pero este paquete de datos es un remanente de la conexión anterior, lo que provoca graves problemas como confusión de datos.

Para evitar que los datos de las conexiones históricas sean recibidos incorrectamente por conexiones posteriores con la misma tupla de cuatro, TCP diseñó el estado TIME_WAIT. El estado durará mucho tiempo. Este tiempo es suficiente para que los paquetes de datos en ambas direcciones se  2MSL envíen . descartado, haciendo la conexión original Los paquetes de datos desaparecerán naturalmente en la red, y los paquetes de datos que reaparecen deben ser generados por conexiones recién establecidas.

Razón 2: Asegúrese de que la parte que "cierra pasivamente la conexión" pueda cerrarse correctamente

RFC 793 señala que otra función importante de TIME-WAIT es:

TIEMPO DE ESPERA: representa esperar a que pase el tiempo suficiente para asegurarse de que el TCP remoto haya recibido el acuse de recibo de su solicitud de terminación de conexión.

En otras palabras, la función de TIME-WAIT es esperar el tiempo suficiente para garantizar que la parte de cierre pasiva pueda recibir el ACK final, lo que le ayudará a cerrar normalmente.

Si el último mensaje ACK (la cuarta ola) del cliente (parte de cierre activa) se pierde en la red, entonces, de acuerdo con el principio de confiabilidad de TCP, el servidor (parte de cierre pasiva) reenviará el mensaje FIN.

Supongamos que el cliente no tiene el estado TIME_WAIT, pero ingresa directamente al estado CERRAR después de enviar el último mensaje ACK. Si el mensaje ACK se pierde, el servidor retransmitirá el mensaje FIN y el cliente ha ingresado al estado CERRAR en este momento. En el estado cerrado, después de recibir el mensaje FIN retransmitido por el servidor, se devolverá un mensaje RST.

imagen

El tiempo TIME-WAIT es demasiado corto y no garantiza que la conexión se cierre correctamente

El servidor recibe este RST y lo interpreta como un error (restablecimiento de conexión por parte del par), lo que no constituye una terminación correcta para un protocolo confiable.

Para evitar que esto suceda, el cliente debe esperar el tiempo suficiente para asegurarse de que el servidor pueda recibir el ACK. Si el servidor no recibe el ACK, activará el mecanismo de retransmisión TCP y el servidor reenviará un FIN. Se necesitan exactamente dos MSL para ir y regresar.

imagen

El tiempo TIME-WAIT es normal, asegurando que la conexión se cierre normalmente

Cuando el cliente recibe el mensaje FIN retransmitido por el servidor, el tiempo de espera en el estado TIME_WAIT se restablecerá a 2MSL.

¿Cuáles son los peligros de demasiado #TIME_WAIT?

Hay dos peligros principales de demasiados estados de TIEMPO DE ESPERA:

  • El primero es ocupar recursos del sistema, como descriptores de archivos, recursos de memoria, recursos de CPU, recursos de subprocesos, etc.;

  • El segundo es ocupar los recursos del puerto, los recursos del puerto también son limitados, generalmente los puertos que se pueden abrir son  32768~61000net.ipv4.ip_local_port_rangeel rango también se puede especificar mediante parámetros.

Demasiado TIME_WAIT en el cliente y el servidor tendrá impactos diferentes.

Si el estado TIME_WAIT del cliente (la parte que inicia el cierre de la conexión) es demasiado , ocupando todos los recursos del puerto, entonces es imposible iniciar una conexión al servidor con la misma "IP de destino + PUERTO de destino", pero el puerto usado aún se puede usar. Continúe para iniciar una conexión a otro servidor. Para obtener más información, lea mi artículo: ¿Se puede reutilizar el puerto del cliente? (abre una nueva ventana)

Por lo tanto, si el cliente (la parte que inicia la conexión) establece una conexión con el mismo servidor que "IP de destino + PUERTO de destino", cuando el estado TIME_WAIT del cliente se conecta demasiado, estará limitado por los recursos del puerto. Si todos los puertos Si el recurso está ocupado, ya no será posible establecer una conexión con el servidor con el mismo "IP de destino + PUERTO de destino".

Sin embargo, incluso en este escenario, siempre que haya diferentes servidores conectados, el puerto se puede reutilizar, por lo que el cliente aún puede iniciar conexiones a otros servidores. Esto se debe a que cuando el kernel localiza una conexión, la localiza a través de cuatro tuplas ( IP de origen, puerto de origen, IP de destino, puerto de destino) y no causará conflictos de conexión porque los puertos del cliente son los mismos.

Si el servidor (la parte que inicia activamente el cierre de la conexión) tiene demasiados estados TIME_WAIT , no causará restricciones de recursos del puerto, porque el servidor solo escucha un puerto y dado que una tupla de cuatro determina de forma única una conexión TCP, en teoría la servidor Se pueden establecer muchas conexiones, pero demasiadas conexiones TCP ocuparán recursos del sistema, como descriptores de archivos, recursos de memoria, recursos de CPU, recursos de subprocesos, etc.

#¿Cómo optimizar TIME_WAIT?

Aquí hay varias formas de optimizar TIME-WAIT, todas las cuales tienen ventajas y desventajas:

  • Active las opciones net.ipv4.tcp_tw_reuse y net.ipv4.tcp_timestamps;

  • net.ipv4.tcp_max_tw_buckets

  • SO_LINGER se usa en el programa y la aplicación se ve obligada a usar RST para cerrarse.

Método 1: net.ipv4.tcp_tw_reuse y tcp_timestamps

Una vez habilitados los siguientes parámetros del kernel de Linux, el socket en TIME_WAIT se puede reutilizar para nuevas conexiones .

Una cosa a tener en cuenta es que la función tcp_tw_reuse solo se puede usar en el cliente (iniciador de conexión). Debido a que esta función está activada, cuando se llama a la función connect (), el kernel encontrará aleatoriamente una conexión con un estado time_wait de más de 1 segundo para una nueva conexión. Reutilizar.

net.ipv4.tcp_tw_reuse = 1

También existe un requisito previo para usar esta opción, que es activar la compatibilidad con marcas de tiempo TCP, es decir

net.ipv4.tcp_timestamps=1(默认即为 1)

El campo de esta marca de tiempo está en la "opción" del encabezado TCP. Consta de un total de 8 bytes para representar la marca de tiempo, de los cuales el primer campo de 4 bytes se usa para guardar la hora en que se envía el paquete de datos. y el segundo campo de 4 bytes es El campo de estrofa se utiliza para almacenar la hora de los últimos datos de llegada enviados por la parte receptora.

Debido a la introducción de marcas de tiempo, el problema que mencionamos anteriormente  2MSL ya no existe, porque los paquetes de datos duplicados se descartarán naturalmente cuando la marca de tiempo expire.

Método 2: net.ipv4.tcp_max_tw_buckets

Este valor predeterminado es 18000. Una vez que la conexión TIME_WAIT en el sistema excede este valor, el sistema restablecerá el estado de la conexión TIME_WAIT posterior.Este método es más violento.

Método 3: usar SO_LINGER en el programa

Podemos configurar el comportamiento de llamar a close para cerrar la conexión configurando la opción socket.

struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));

Si l_onoffno es 0 y l_lingerel valor es 0, después closede llamar, enviará inmediatamente un RSTindicador al par y la conexión TCP omitirá cuatro apretones de manos, omitirá TIME_WAITel estado y se cerrará directamente.

TIME_WAITPero esto brinda la posibilidad de cruzar el estado, pero es un comportamiento muy peligroso y no vale la pena promoverlo.

Todos los métodos introducidos anteriormente intentan trascender  TIME_WAITel estado, lo que en realidad no es bueno. Aunque el estado TIME_WAIT dura un poco y parece muy hostil, está diseñado para evitar cosas complicadas.

El libro "Programación de redes UNIX" dice: TIME_WAIT es nuestro amigo, nos ayuda. No intentes evitar este estado, descúbrelo .

Si el servidor quiere evitar demasiadas conexiones en el estado TIME_WAIT, nunca debe desconectarse activamente, dejar que el cliente se desconecte y dejar que los clientes distribuidos en todas partes soporten el TIME_WAIT .

# ¿Cuáles son las razones por las que aparece una gran cantidad de estados TIME_WAIT en el servidor?

En primer lugar, debe saber que el estado TIME_WAIT aparece solo después de que la conexión se cierra activamente, por lo que si el servidor tiene una gran cantidad de conexiones TCP en el estado TIME_WAIT, significa que el servidor ha desconectado activamente muchas conexiones TCP.

La pregunta es, ¿en qué escenario se desconectará activamente el servidor?

  • El primer escenario: HTTP no utiliza conexiones largas

  • Segundo escenario: tiempo de espera de conexión HTTP prolongado

  • Tercer escenario: la cantidad de solicitudes de conexión persistentes HTTP alcanza el límite superior

A continuación, preséntalos respectivamente.

El primer escenario: HTTP no utiliza conexiones largas

Primero echemos un vistazo a cómo se habilita el mecanismo HTTP Keep-Alive.

Está deshabilitado de forma predeterminada en HTTP/1.0. Si el navegador desea habilitar Keep-Alive, debe agregarlo al encabezado de la solicitud:

Connection: Keep-Alive

Luego, cuando el servidor recibe la solicitud y responde, también se agrega al encabezado de respuesta:

Connection: Keep-Alive

Al hacer esto, la conexión TCP no se interrumpe sino que permanece conectada. Cuando el cliente envía otra solicitud, utiliza la misma conexión TCP. Esto continúa hasta que el lado del cliente o del servidor inicia una desconexión.

A partir de HTTP/1.1, Keep-Alive está activado de forma predeterminada . Ahora la mayoría de los navegadores usan HTTP/1.1 de forma predeterminada, por lo que Keep-Alive está activado de forma predeterminada. Una vez que el cliente y el servidor llegan a un acuerdo, se establece la conexión larga.

Connection:close Si desea desactivar HTTP Keep-Alive, debe agregar información al encabezado de la solicitud o respuesta HTTP  , es decir, siempre que haya información en el encabezado HTTP del cliente o del servidor  Connection:close , el No se puede utilizar el mecanismo de conexión larga HTTP .

Después de desactivar el mecanismo de conexión larga HTTP, cada solicitud debe pasar por el siguiente proceso: establecer TCP -> solicitar recursos -> responder a recursos -> liberar la conexión, entonces este método es una conexión corta HTTP, como se muestra a  continuación :

imagen

conexión corta HTTP

Sabemos anteriormente que mientras haya información en el encabezado HTTP de cualquiera de las partes  Connection:close , el mecanismo de conexión larga HTTP no se puede utilizar, por lo que después de completar una solicitud/procesamiento HTTP, la conexión se cerrará.

Surge la pregunta: ¿ el cliente o el servidor cierra activamente la conexión en este momento?

En el documento RFC, no está claro quién cierra la conexión. Ambas partes de la solicitud y la respuesta pueden cerrar activamente la conexión TCP.

Sin embargo, de acuerdo con la implementación de la mayoría de los servicios web, no importa qué parte deshabilite HTTP Keep-Alive, el servidor cerrará activamente la conexión y luego aparecerá una conexión en estado TIME_WAIT en el servidor en este momento.

El cliente ha deshabilitado HTTP Keep-Alive y el servidor ha habilitado HTTP Keep-Alive ¿Quién es el que lo cierra activamente?

Cuando el cliente deshabilita HTTP Keep-Alive, el encabezado de la solicitud HTTP tendrá  Connection:close información y el servidor cerrará activamente la conexión después de enviar la respuesta HTTP.

¿Por qué está diseñado de esta manera? HTTP es un modelo de solicitud-respuesta, y el iniciador es siempre el cliente. La intención original de HTTP Keep-Alive es reutilizar la conexión para solicitudes posteriores del cliente . Si definimos  información en el encabezado de la solicitud en una determinada solicitud HTTP, modelo de respuesta  , entonces connection:closeel único momento para no reutilizar esta conexión es en el lado del servidor , por lo que es razonable que cerremos la conexión al "final" del ciclo de solicitud-respuesta HTTP.

HTTP Keep-Alive está habilitado en el cliente y HTTP Keep-Alive está deshabilitado en el servidor ¿Quién es el que lo cierra activamente?

Cuando el cliente habilita HTTP Keep-Alive y el servidor deshabilita HTTP Keep-Alive, el servidor también cerrará activamente la conexión después de enviar la respuesta HTTP.

¿Por qué está diseñado de esta manera? Cuando el servidor cierra activamente la conexión, solo necesita llamar a close() una vez para liberar la conexión, y el trabajo restante es procesado directamente por la pila TCP del kernel. Solo hay una llamada al sistema en todo el proceso, si el cliente es necesario Para cerrar, el servidor Después de escribir la última respuesta, debe colocar el socket en la cola legible, llamar a select/epoll para esperar el evento, luego llamar a read() una vez para saber que la conexión se ha cerrado, que son dos syscalls y un programa más en modo de usuario se cierra. Active la ejecución y el tiempo de retención del socket será mayor.

Por lo tanto, cuando hay una gran cantidad de conexiones de estado TIME_WAIT en el servidor, puede verificar si tanto el cliente como el servidor han habilitado HTTP Keep-Alive. Debido a que cualquiera de las partes no ha habilitado HTTP Keep-Alive, hará que el servidor espere hasta que después de procesar un HTTP Después de la solicitud, la conexión se cierre activamente. En este momento, aparecerá una gran cantidad de conexiones en el estado TIME_WAIT en el servidor.

Para este escenario, la solución también es muy simple, de modo que tanto el cliente como el servidor habiliten el mecanismo HTTP Keep-Alive.

Segundo escenario: tiempo de espera de conexión HTTP prolongado

La característica de la conexión larga HTTP es que siempre que ninguno de los extremos proponga explícitamente desconectarse, se mantendrá el estado de la conexión TCP.

Las conexiones persistentes HTTP pueden recibir y enviar múltiples solicitudes/respuestas HTTP en la misma conexión TCP, evitando la sobrecarga de establecimiento y liberación de la conexión.

imagen

imagen

Algunos estudiantes pueden preguntar, si se utilizan conexiones persistentes HTTP, si el cliente completa una solicitud HTTP y ya no inicia nuevas solicitudes, ¿no sería un desperdicio de recursos mantener ocupada esta conexión TCP?

Así es, para evitar el desperdicio de recursos, el software de servicios web generalmente proporciona un parámetro para especificar el período de tiempo de espera de las conexiones HTTP largas, como el parámetro keepalive_timeout proporcionado por nginx.

Suponiendo que el período de tiempo de espera de la conexión HTTP larga se establece en 60 segundos, nginx iniciará un "temporizador". Si el cliente no inicia una nueva solicitud dentro de los 60 segundos posteriores a la última solicitud HTTP, el tiempo del temporizador Una vez que llega , nginx activará la función de devolución de llamada para cerrar la conexión y luego aparecerá una conexión en estado TIME_WAIT en el servidor .

imagen

Tiempo de espera largo de conexión HTTP

Cuando hay una gran cantidad de conexiones en el estado TIME_WAIT en el servidor, si el fenómeno es que una gran cantidad de clientes no han enviado datos durante mucho tiempo después de establecer una conexión TCP, entonces existe una alta probabilidad de que el servidor Cierra activamente la conexión debido al largo tiempo de espera de la conexión HTTP. Genera una gran cantidad de conexiones en estado TIME_WAIT.

Puede verificar en la dirección de los problemas de red, por ejemplo, si los datos enviados por el cliente no han sido recibidos por el servidor debido a problemas de red, por lo que la conexión HTTP larga se agota.

Tercer escenario: la cantidad de solicitudes de conexión persistentes HTTP alcanza el límite superior

El servidor web generalmente tiene un parámetro para definir el número máximo de solicitudes que se pueden procesar en una conexión HTTP larga. Cuando se excede el límite máximo, la conexión se cerrará activamente.

Por ejemplo, el parámetro keepalive_requests de nginx significa que después de establecer una conexión HTTP larga, nginx establecerá un contador para esta conexión para registrar la cantidad de solicitudes de clientes que se han recibido y procesado en esta conexión HTTP larga. Si se alcanza el valor máximo de esta configuración de parámetro, nginx cerrará activamente la conexión larga y luego aparecerá una conexión en el estado TIME_WAIT en el servidor.

El valor predeterminado del parámetro keepalive_requests es 100, lo que significa que cada conexión larga HTTP solo puede ejecutar hasta 100 solicitudes. La mayoría de las personas a menudo ignoran este parámetro, porque cuando el QPS (solicitudes por segundo) no es muy alto, el valor predeterminado un valor de 100 está bien.

Sin embargo, para algunos escenarios con QPS relativamente alto, como más de 10,000 QPS, o incluso 30,000, 50,000 o incluso más, si el valor del parámetro keepalive_requests es 100, nginx cerrará la conexión con mucha frecuencia y luego el servidor Una gran cantidad Aparecerán varios estados de TIME_WAIT .

Para este escenario, la solución también es muy simple: simplemente aumente el parámetro keepalive_requests de nginx.

¿Cuáles son las razones por las que el servidor tiene una gran cantidad de estados CLOSE_WAIT?

El estado CLOSE_WAIT es un estado que solo la "parte de cierre pasiva" puede tener, y si la "parte de cierre pasiva" no llama a la función de cierre para cerrar la conexión, entonces el mensaje FIN no se puede enviar y la conexión en CLOSE_WAIT El estado no se puede cambiar al estado LAST_ACK.

Por lo tanto, cuando hay una gran cantidad de conexiones en el estado CLOSE_WAIT en el servidor, significa que el programa en el servidor no ha llamado a la función de cierre para cerrar la conexión .

Entonces, ¿qué hará que el programa del lado del servidor cierre la conexión sin llamar a la función de cierre? En este momento, normalmente es necesario solucionar el problema del código.

Primero analicemos el proceso de un servidor TCP normal:

  1. Cree un socket de servidor, vincule el puerto de enlace y escuche el puerto de escucha

  2. Registre el socket del servidor para epoll

  3. epoll_wait espera a que llegue la conexión, cuando llegue la conexión, llame a accpet para obtener el socket conectado.

  4. Registre el socket conectado para epoll

  5. epoll_wait espera a que ocurra un evento

  6. Cuando la conexión de la otra parte se cierra, llamamos cerrar

Las posibles razones por las que el servidor no llama a la función de cierre son las siguientes.

La primera razón : el paso 2 no se realizó y el socket del servidor no se registró en epoll. De esta manera, cuando llega una nueva conexión, el servidor no puede detectar este evento y no puede obtener el socket conectado. Entonces, naturalmente, el servidor no oportunidad de llamar a la función de cierre en el socket.

Sin embargo, la probabilidad de que esto suceda es relativamente pequeña: se trata de un error obvio en la lógica del código que se puede descubrir en la etapa de vista de lectura inicial.

La segunda razón : el paso 3 no se realiza, cuando llega una nueva conexión, no se llama a accpet para obtener el socket de la conexión, lo que resulta en una gran cantidad de clientes que se desconectan activamente y el servidor no tiene posibilidad de llamar a la función de cierre. estos sockets. Esto da como resultado una gran cantidad de conexiones en el estado CLOSE_WAIT en el lado del servidor.

Esto puede suceder porque el código del servidor está atascado en una lógica determinada o genera una excepción por adelantado antes de ejecutar la función de aceptación.

La tercera razón : el paso 4 no se realiza. Después de obtener el socket conectado a través de accpet, no se registra en epoll. Como resultado, cuando se recibe el mensaje FIN posterior, el servidor no puede percibir este evento, por lo que el servidor no lo hará. Es hora de llamar a la función de cierre.

Esto puede suceder porque el código está atascado en una determinada lógica o porque se lanza una excepción por adelantado antes de que el servidor registre el socket conectado en epoll. He visto artículos prácticos de otras personas sobre cómo resolver el problema close_wait antes. Si está interesado, puede leerlo: Análisis de una gran cantidad de conexiones CLOSE_WAIT causadas por código Netty no robusto

La cuarta razón : no se realizó el paso 6. Cuando se descubrió que el cliente cerró la conexión, el servidor no ejecutó la función de cierre. Puede deberse a una fuga de código o el código estaba atascado en cierta lógica antes de ejecutar el función de cierre, como interbloqueos, etc.

Se puede encontrar que cuando hay una gran cantidad de conexiones en el estado CLOSE_WAIT en el servidor, generalmente es un problema de código, en este momento debemos verificar y ubicar el código específico paso a paso. La dirección principal del análisis Es por eso que el servidor no llamó a cerrar .

# ¿Qué pasa si se ha establecido la conexión pero el cliente falla repentinamente?

La falla del cliente se refiere al escenario en el que el host del cliente no funciona o tiene un corte de energía. Cuando esto sucede, si el servidor nunca envía datos al cliente, el servidor nunca podrá percibir el evento de tiempo de inactividad del cliente, es decir, la conexión TCP del servidor siempre estará en el estado, ocupando recursos del sistema  ESTABLISH .

Para evitar esta situación, TCP tiene un mecanismo de mantenimiento de actividad . El principio de este mecanismo es el siguiente:

Defina un período de tiempo. Durante este período de tiempo, si no hay actividad relacionada con la conexión, el mecanismo de mantenimiento de conexión de TCP comenzará a funcionar. Cada dos intervalos de tiempo, se enviará un mensaje de sonda. El mensaje de sonda contiene muy pocos datos. Si no hay respuesta a varios mensajes de detección consecutivos, se considera que la conexión TCP actual está inactiva y el kernel del sistema notifica el mensaje de error a la aplicación de capa superior.

Existen parámetros correspondientes en el kernel de Linux para establecer el tiempo de mantenimiento de actividad, el número de detecciones de mantenimiento de actividad y el intervalo de tiempo de las detecciones de mantenimiento de actividad. Los siguientes son los valores predeterminados:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75  
net.ipv4.tcp_keepalive_probes=9
  • tcp_keepalive_time = 7200: indica que el tiempo de mantenimiento es de 7200 segundos (2 horas), es decir, si no hay actividad relacionada con la conexión dentro de 2 horas, se activará el mecanismo de mantenimiento.

  • tcp_keepalive_intvl=75: indica que el intervalo entre cada detección es de 75 segundos;

  • tcp_keepalive_probes=9: Indica que después de no detectar respuesta 9 veces, se considera que la otra parte es inalcanzable, interrumpiéndose así esta conexión.

En otras palabras, en un sistema Linux, se necesitan al menos 2 horas, 11 minutos y 15 segundos para encontrar una conexión "inactiva".

imagen

imagen

Tenga en cuenta que si la aplicación desea utilizar el mecanismo de mantenimiento de actividad de TCP, debe configurar  SO_KEEPALIVE la opción a través de la interfaz de socket para que surta efecto. Si no está configurada, no se puede utilizar el mecanismo de mantenimiento de actividad de TCP.

Si TCP keepalive está habilitado, debe considerar las siguientes situaciones:

  • En primer lugar, el programa de pares funciona con normalidad. Cuando el mensaje de detección de mantenimiento de TCP se envía al par, el par responderá normalmente, por lo que  se restablecerá el tiempo de mantenimiento de TCP , esperando la llegada del siguiente tiempo de mantenimiento de TCP.

  • En segundo lugar, el host del mismo nivel falla y se reinicia. Cuando se envía el mensaje de prueba de mantenimiento de conexión TCP al par, el par puede responder, pero como no hay información válida sobre la conexión, se generará un paquete RST y pronto se descubrirá que la conexión TCP se ha restablecido. .

  • El tercer tipo es que el host par está inactivo ( tenga en cuenta que no es una falla del proceso. Después de que el proceso falla, el sistema operativo enviará un mensaje FIN al reclamar recursos del proceso. Sin embargo, el tiempo de inactividad del host no se puede detectar, por lo que un TCP Se requiere un mecanismo de mantenimiento de conexión (para detectar si la otra parte tiene un tiempo de inactividad del host ) o si los paquetes de la otra parte son inalcanzables por otras razones. Cuando el mensaje de detección de mantenimiento de conexión TCP se envía al par, no hay respuesta. Después de varias veces consecutivas y se alcanza el número de detecciones de mantenimiento de conexión, TCP informará que la conexión TCP ha finalizado .

El tiempo de detección del mecanismo de mantenimiento de conexión de TCP es un poco largo. Podemos implementar un mecanismo de latido en la capa de aplicación nosotros mismos.

Por ejemplo, el software de servicios web generalmente proporciona  keepalive_timeout parámetros para especificar el período de tiempo de espera de las conexiones HTTP largas. Si el tiempo de espera de conexión larga HTTP se establece en 60 segundos, el software del servicio web iniciará un temporizador . Si el cliente no inicia una nueva solicitud dentro de los 60 segundos después de completar una solicitud HTTP, el tiempo del temporizador será Cuando llegue, la devolución de llamada La función se activará para liberar la conexión.

imagen

Mecanismo de latido del servicio web.

#¿Qué sucede si se ha establecido la conexión, pero el proceso del servidor falla?

El kernel mantiene la información de la conexión TCP, por lo que cuando el proceso del servidor falla, el kernel necesita reciclar todos los recursos de conexión TCP del proceso, por lo que el kernel enviará el mensaje FIN de la primera oleada y los procesos de oleada posteriores también están en el kernel. La finalización no requiere la participación del proceso, por lo que incluso si el proceso del servidor sale, aún puede completar el proceso de cuatro ondas TCP con el cliente.

Yo mismo hice un experimento, usé kill -9 para simular una falla del proceso y descubrí que después de finalizar el proceso, el servidor enviará un mensaje FIN y saludará al cliente cuatro veces .


Programación de sockets

¿Cómo programar Socket para TCP?

imagen

Trabajo de cliente y servidor basado en el protocolo TCP.

  • Se inicializan el servidor y el cliente  sockety se obtiene el descriptor del archivo;

  • Llamado por el servidor  bindpara vincular el socket a la dirección IP y al puerto especificados;

  • Llamado por el servidor  listenpara monitorear;

  • Llamado por el servidor  accepty esperando que el cliente se conecte;

  • El cliente llama  connectpara iniciar una solicitud de conexión a la dirección y el puerto del servidor;

  • El servidor  accept devuelve el descriptor de archivo utilizado para la transmisión  socket ;

  • El cliente llama  write para escribir datos, el servidor llama  read para leer datos;

  • Cuando el cliente se desconecta, se llamará  close, luego cuando el servidor  read lea los datos, se leerá  EOF. Después de procesar los datos, el servidor llamará para  closeindicar que la conexión está cerrada.

Lo que hay que tener en cuenta aquí es que cuando el servidor llama  accept , si la conexión es exitosa, se devolverá un socket con una conexión completa, que se utilizará para transmitir datos más adelante.

Por lo tanto, el socket de escucha y el socket realmente utilizado para transmitir datos son "dos" sockets, uno se llama socket de escucha y el otro se llama socket de conexión completa .

Una vez que se establece una conexión exitosa, ambas partes comienzan a leer y escribir datos a través de las funciones de lectura y escritura, como escribir en una secuencia de archivos.

¿Cuál es el significado de la acumulación de parámetros al escuchar?

Se mantienen dos colas en el kernel de Linux:

  • Cola de semiconexión (cola SYN): recibe una solicitud de establecimiento de conexión SYN y se encuentra en el estado SYN_RCVD;

  • Cola de conexión completa (cola Accpet): el proceso de protocolo de enlace de tres vías TCP se ha completado y se encuentra en el estado ESTABLECIDO;

imagen

Cola SYN y cola Accpet

int listen (int socketfd, int backlog)
  • El parámetro uno, socketfd, es el descriptor del archivo socketfd.

  • El parámetro dos, el backlog, tiene ciertos cambios en las versiones históricas.

En los primeros días, el trabajo pendiente del kernel de Linux era el tamaño de la cola SYN, que era el tamaño de cola sobresaliente.

Después del kernel de Linux 2.2, el trabajo pendiente se convierte en la cola de aceptación, es decir, la longitud de la cola que ha completado el establecimiento de la conexión, por lo que ahora generalmente se considera que el trabajo pendiente es la cola de aceptación.

Pero el límite superior es el tamaño del parámetro del kernel somaxconn, lo que significa longitud de la cola de aceptación = min (acumulación, somaxconn).

¿En qué paso del apretón de manos de tres vías se acepta?

Primero echemos un vistazo a lo que envía el cliente cuando se conecta al servidor.

imagen

apretón de manos de tres vías del zócalo

  • La pila de protocolos del cliente envía un paquete SYN al servidor y le dice al servidor que actualmente está enviando el número de secuencia client_isn, y el cliente ingresa al estado SYN_SENT;

  • Después de que la pila de protocolos del servidor recibe este paquete, responde con un ACK al cliente. El valor de la respuesta es client_isn+1, lo que significa la confirmación del paquete SYN client_isn. Al mismo tiempo, el servidor también envía un Paquete SYN para decirle al cliente mi secuencia de envío actual. El número es server_isn y el servidor ingresa al estado SYN_RCVD;

  • Después de que la pila de protocolos del cliente recibe el ACK, el programa de aplicación  connect regresa de la llamada, lo que indica que la conexión unidireccional entre el cliente y el servidor se ha establecido con éxito y que el estado del cliente está ESTABLECIDO. Al mismo tiempo, el cliente La pila de protocolos también responderá al paquete SYN del servidor, los datos son server_isn+1;

  • Después de que el paquete de respuesta ACK llega al servidor, la conexión TCP del servidor ingresa al estado ESTABLECIDO y la pila de protocolos del servidor hace que la  accept llamada de bloqueo regrese. En este momento, la conexión unidireccional del servidor al cliente también se establece con éxito. Hasta ahora, la conexión entre el cliente y el servidor se ha establecido con éxito en ambas direcciones.

A partir del proceso de descripción anterior, podemos saber que la conexión del cliente regresa exitosamente después del segundo protocolo de enlace, y que el servidor acepta el retorno exitoso después del protocolo de enlace de tres vías.

El cliente llama para cerrar, ¿cuál es el proceso para desconectar la conexión?

Veamos si el cliente llama activamente  close, ¿qué pasará?

imagen

El cliente llama al proceso de cierre.

  • Llamado por el cliente  close, lo que indica que el cliente no tiene datos para enviar, luego enviará un mensaje FIN al servidor e ingresará al estado FIN_WAIT_1;

  • Cuando el servidor recibe el mensaje FIN, la pila del protocolo TCP insertará un carácter de fin de archivo en el  EOF búfer de recepción del paquete FIN. La aplicación puede  read percibir este paquete FIN llamando. Esto  EOF se colocará después de otros datos recibidos que se hayan puesto en cola , lo que significa que el servidor necesita manejar esta situación de excepción, porque EOF significa que no llegarán datos adicionales a la conexión. En este momento, el servidor ingresa al estado CLOSE_WAIT;

  • Luego, después de procesar los datos, naturalmente los leerá  EOF, por lo que también llama para  close cerrar su socket, lo que hará que el servidor envíe un paquete FIN y luego esté en el estado LAST_ACK;

  • El cliente recibe el paquete FIN del servidor y envía un paquete de confirmación ACK al servidor. En este punto, el cliente ingresará al estado TIME_WAIT;

  • Después de que el servidor recibe el paquete de confirmación ACK, ingresa al estado CERRADO final;

  • Una vez transcurrido el tiempo, el cliente  2MSL también ingresa al estado CERRADO;

¿Se puede establecer una conexión TCP sin aceptar?

Respuesta: .

La llamada al sistema accpet no participa en el proceso de protocolo de enlace de tres vías de TCP. Solo es responsable de sacar un socket con una conexión establecida de la cola de conexión completa de TCP. La capa de usuario obtiene el socket con una conexión establecida a través del sistema accpet. llamar y puede leer y escribir el socket.

imagen

Cola de semiunión y cola de unión completa

¿Se puede establecer una conexión TCP sin escuchar?

Respuesta: .

El cliente puede conectarse a sí mismo para formar una conexión ( autoconexión TCP ), o dos clientes pueden enviarse solicitudes entre sí para establecer una conexión al mismo tiempo ( TCP se abre simultáneamente ). Ambas situaciones tienen una cosa en común, es decir, no hay participación del servidor, es decir, sin escuchar, se puede establecer una conexión TCP.

Supongo que te gusta

Origin blog.csdn.net/liuxing__jacker/article/details/132026056
Recomendado
Clasificación