por: elpamplinadecai@gmail.com
A diferencia de UDP, que no añade nada sustancial a la semántica de entrega no confiable de IP, el Protocolo de Control de Transmisiones (TCP, Transmission Control Protocol) añade la funcionalidad fundamental de la entrega confiable, a cambio de una mayor complejidad.
El diseño de TCP ha sido realizado con gran acierto, manteniendo una separación completa con la red subyacente, o cual permite que pueda ser utilizado con muchos sistemas de entrega, aparte de IP. Su importancia es tal que da nombre, junto con IP, a la familia completa de protocolos TCP/IP, y ha facilitado más que cualquier otro desarrollo la universalización de Internet como red de alcance global. Tiene su origen en los trabajos de Vinton G. Cerf y Bob Kahn en la década de 1970. Por ello en muchas ocasiones se ha reconocido a Cerf y Kahn como los "padres" de Internet.
TCP usa el mismo concepto de puerto de protocolo que UDP para implementar el concepto de punto de destino abstracto dentro de una máquina, lo que le permite mantener muchas conexiones en la misma máquina sin mezclarse. Los números de puerto UDP y TCP no se confunden, pues el código de protocolo de la cabecera IP hace que cada protocolo nunca trate con los paquetes del otro.
Un servicio de entrega confiable se ha de encargar de cinco funciones:
Los programas de aplicación normalmente requieren en envío de los datos en forma de flujos de octetos. El servicio de entrega de la máquina destino ha de pasar al proceso receptor exactamente el mismo flujo que se ha transmitido en origen.
Esto evita cualquier tipo de programación adicional en las aplicaciones, ni que éstas conozcan el formato de los paquetes de datos.
Antes de realizarse el envío de los datos, las máquinas implicadas han de negociar la apertura de una conexión o circuito virtual, equivalente al establecimiento de una llamada telefónica, en lugar de tratar por separado el envío de cada segmento.
TCP permite dos formas de abrir una conexión: activa o pasiva. Antes de recibir la petición de conexión, en un extremo se realiza una apertura pasiva en espera de que el otro extremo solicite la entrada mediante una apertura activa. La máquina pasiva establece previamente el puerto en el que va a aceptar conexiones.
Para optimizar el tráfico de red, el software de protocolo ha de recolectar datos suficientes del flujo que genera la aplicación, en una memoria intermedia o buffer, para llenar un segmento razonablemente largo antes de transmitirlo.
Igualmente, se ha de proporcionar un mecanismo push (empuje) que permita que la aplicación fuerce el envío inmediato del contenido del buffer, si fuera necesario. El uso típico de esta característica es en terminales interactivas.
Los programas de aplicación tienen la responsabilidad de ponerse de acuerdo para entender las fronteras entre registros dentro del flujo de datos; el protocolo de entrega no está obligado a respetar separaciones de registros en las divisiones de paquetes que realiza.
El servicio de transporte ha de permitir la transmisión concurrente de flujos independientes en ambas direcciones.
La tarea de proporcionar una transferencia confiable sobre un sistema subyacente de entrega no confiable es tarea complicada. La mayor parte de los protocolos existentes hacen uso de la técnica denominada asentimiento positivo con retransmisión.
Un asentimiento positivo implica que el receptor de un mensaje envíe de vuelta al remitente un acuse de recibo (ACK) indicándole que éste ha llegado correctamente. El remitente espera que cada mensaje que envía sea asentido y, en caso de no recibirlo en un tiempo prudencial, lo retransmite.
Esta técnica requiere acciones cuidadosas para controlar las duplicaciones, pues tanto los paquetes como los asentimientos se puede aparecer duplicados. Para controlar esto, se incluyen números de secuencia para que el receptor pueda asociar cada acuse de recibo con su paquete correspondiente y pueda desechar los duplicados.
Si el sistema de los asentimientos positivos tuviese que esperar cada acuse de recibo antes de enviar el siguiente paquete, se desperdiciaría una cantidad enorme de ancho de banda.
La técnica conocida como ventana deslizante permite que se envíen varios paquetes seguidos sin esperar asentimientos, optimizando el uso del ancho de banda.
Se establece una ventana de un tamaño fijo, la cual es el marco en el que cabe la máxima cantidad de paquetes que se pueden enviar sin recibir asentimiento. El tamaño de la ventana es proporcional a la memoria intermedia disponible, debido a que cada paquete se ha de mantener en memoria mientras no se haya acusado su recibo, pues puede ser necesaria su retransmisión. Se dice que los paquetes dentro de la ventana están en estado de espera de confirmación (unacknowledged)..
En un primer momento, se pueden transmitir todos los paquetes que caben en la ventana sin asentimiento. En el momento de recibir un acuse de recibo, la ventana se desliza para alcanzar el siguiente paquete y poder enviarlo. Al mismo tiempo, el deslizamiento saca fuera el paquete que se ha asentido, el cual no es necesario mantenerlo más en memoria.
Con un tamaño de ventana suficientemente ajustado a la capacidad de la red, es posible eliminar cualquier tiempo muerto, obteniéndose la velocidad máxima de entrega.
Los tres estados en que puede estar un paquete son:
La implementación que TCP hace del mecanismo de la ventana deslizante, además de dar confiabilidad a la comunicación, integra el control de flujo, de manera que el receptor pueda restringir el envío de datos según el espacio que tenga disponible y su capacidad de procesamiento.
Se mantienen ventanas separadas para el flujo de entrada y el de salida, para que así sean totalmente independientes (full-duplex). Por tanto, en una conexión hay cuatro ventanas implicadas: dos en cada extremo.
El trabajo se realiza a nivel de octeto, no por segmentos ni paquetes. Los octetos del flujo de datos se ordenan de manera secuencial, guardando el transmisor tres punteros asociados con cada conexión:
Una diferencia importante con la definición tradicional de ventana deslizante es que en TCP el tamaño de la ventana es variable. Esto se hace para adaptarse a los cambios en la capacidad de recepción del otro extremo (control de flujo). Un campo en la cabecera TCP de los asentimientos informa de cuántos octetos está preparado para aceptar el receptor en el próximo envío , lo cual permite redimensionar la ventana.
Debido a lo variable del tamaño de los segmentos y de la ventana, los acuses de recibo indican posiciones en el flujo de octetos, sin referirse a datagramas o segmentos completos. El receptor indica en el campo de asentimiento de la cabecera la posición siguiente a la parte de flujo que ha logrado reconstruir completamente, es decir, el número de secuencia del siguiente octeto que espera recibir.
A este tipo de acuse de recibo se le llama acumulativo, pues informa sobre la cantidad de flujo contiguo que ha acumulado el receptor. Su uso tiene las siguientes ventajas:
A pesar de estas ventajas, este esquema es menos eficiente que otros, pues el receptor no obtiene información sobre las transmisiones exitosas que aún no han producido un bloque contiguo. Esto puede hacer que se retransmitan segmentos inútilmente o bien no se use toda la capacidad de la ventana, según se tome una estrategia pesimista u optimista, respectivamente.
En la cabecera, un conjunto de seis bits llamado code bits, indica el tipo de operación que realiza el segmento:
URG: Contiene datos urgentes (acelerados), debiendo atenderse al puntero de urgencia que contiene la cabecera.
ACK: Contiene asentimientos, debiendo atenderse al campo correspondiente.
PSH: Solicitud de operación push (volcado del buffer)
RST: Iniciación de la conexión
SYN: Sincronización de números de secuencia
FIN: Final del flujo de octetos
Los datos urgentes, también llamados a veces fuera de banda, son aquellos que el programa destino debe atender aún sin haber consumido los octetos que ya están en el flujo (por ejemplo, la solicitud de interrupción de un programa).
El emisor activa el bit URG de la cabecera, e indica el lugar donde se envían los datos urgentes mediante un campo aparte. El software del receptor, al recibir este tipo de segmento, debe interrumpir al programa de usuario y ponerlo en modo urgente.
Los dos extremos han de ponerse de acuerdo en el tamaño máximo de segmento (MMS) a utilizar. Para ello se usan los campos de opciones de la cabecera.
Si ambas máquinas residen en la misma red, se intentará usar el tamaño que se ajuste a los paquetes de esa red física, mientras que en máquinas de distintas redes se intentará computar la mínima MTU de las redes que hay que atravesar.
Por defecto se escoge un tamaño de 536 octetos, que es el resultado de restarle los encabezados al tamaño por defecto de los datagramas IP (576).
La elección de el MMS es difícil en un entorno de red de redes, resultando crucial para lograr una buena utilización del ancho de banda. Si es demasiado pequeño, el peso de los encabezados aumenta, desperdiciandose ancho de banda; si es demasiado grande, el segmento se puede ver fragmentado en su viaje por redes intermedias, aumentando el riesgo de pérdida.
Cómputo de la suma de verificación (checksum)
El cómputo de la suma de verificación es exactamente el mismo que para el protocolo UDP, con el uso de un pseudo-encabezado.
Uno de los aspectos más complejos del protocolo TCP, que lo diferencia de muchos otros, es la forma en que maneja la temporización para retransmisión (time-out).
La razón por la que el algoritmo TCP es especial proviene del ambiente de red de redes para el que se ha diseñado. En este entorno, un segmento puede viajar a altísima velocidad (cuando atraviesa sólo una LAN) o bien pasar por varias redes intermedias, variando drásticamente el tiempo de llegada.
A esto ha que sumar el factor del tráfico, que puede provocar sustanciales diferencias incluso en la misma ruta en momentos diferentes.
Se llama tiempo de ida y vuelta (round trip time) de un segmento al tiempo que tarda en llegar a su destino más el que tarda su acuse de recibo en retornar al origen.
Con estas premisas, se llega a la conclusión de que es necesario incorporar un tipo de algoritmo que se vaya adaptando dinámicamente a los cambios en los tiempos de ida y vuelta. A esto se le llama algoritmo adaptable de retransmisión.
La recolección de datos para este algoritmo se realiza registrando la hora en la que se envía cada segmento y en la que se recibe su asentimiento. Con estos dos datos, se extrae el denominado muestreo del tiempo de ida y vuelta (round trip sample).
TCP guarda un valor promedio del tiempo de ida y vuelta, que es usado como valor de estimación, y que se va ajustando con la llegada de nuevas muestras. El tipo de fórmula que se suele usar para esto se basa en los siguientes parámetros: La constante a, tomada en el intervalo [0,1), hace que la adaptación a nuevas situaciones se haga más o menos gradualmente. El tiempo estimado de ida y vuelta RTT no se usa directamente como valor de terminación del temporizador, sino que se multiplica por un factor fijo o variable b. El ajuste de este factor es muy importante para evitar retransmisiones innecesarias o retardos excesivos.
En la especificación original de TCP se recomendaba un valor de b=2, aunque con el tiempo se han ido desarrollando mejores estimaciones.
El uso de un factor fijo para la obtención del tiempo de expiración no permite una buena adaptación en entornos donde se hay un amplio rango de variación en los retardos.
Para mejorar el algoritmo, en la especificación de 1989 de TCP se propone el cálculo de la denominada desviación estimada que, sin aumentar excesivamente los cómputos a realizar, consigue una mejor estimación del tiempo de expiración.
Aquí, la desviación estimada se basa en unas constantes δ, ρ y η indican qué tan rápidamente afectan las nuevas muestras al tiempo de ida y vuelta, la desviación y el tiempo de expiración estimados, respectivamente.
Para hacer los cómputos eficientemente, se recomienda que las constantes δ y ρ tomen valores inversos a potencias de dos, habiendose encontrado que los valores δ=1/23, ρ=1/22 y η=3 consiguen buenos resultados.
La posible presencia de duplicados de segmentos complica el cálculo de las muestras de tiempo de ida y vuelta. Si el temporizador para un segmento expira y éste es retransmitido, será imposible asociar un acuse de recibo con una de las dos o más retransmisiones, produciendose el fenómeno llamado ambigüedad de asentimiento (acknowledgement ambiguity). El resultado es que la muestra de tiempo de ida y vuelta obtenida no es fiable.
No hay una regla que solucione este problema al 100%:
Una solución más correcta es el llamado algoritmo de Karn, el cual consiste en simplemente desechar las muestras obtenidas de segmentos retransmitidos.
Es necesario, sin embargo, adaptar este algoritmo para evitar la situación en la que un aumento brusco en los retardos provoque una congelación del valor estimado, al generarse continuas retransmisiones. Para ello, se aplica una anulación de temporizador (timer backoff), consistente en que se aumenta la estimación en un factor g cada vez que hay una retransmisión.
Más detalladamente, el algoritmo de Karn se comporta de la siguiente manera:
Este algoritmo, que fue originalmente concebido para redes de radio-paquetes, ha demostrado muy buenos resultados incluso con redes muy inestables.
El congestionamiento es la saturación de datagramas en lugar de la red, normalmente en los gateways. Éstos tienen una capacidad de almacenamiento finita, lo que hace que vayan descartando datagramas en el momento en que se ven saturados.
Los puntos extremos de una comunicación, por lo general, no tienen conocimiento del congestionamiento que ocurre en las redes intermedias, no distinguiendose del retraso producido por otros factores. Además, los sistemas no orientados a conexión, como IP, no pueden evitar el congestionamiento reservando con antelación espacio en los gateways para cada conexión.
El comportamiento de los protocolos de temporización y retransmisión, como TCP, empeoran las situaciones de congestionamiento, pues responden a los retrasos aumentando más y más el número de datagramas en la red, pudiéndose llegar a la situación denominada colapso por congestionamiento.
Las técnicas recomendadas por el estándar TCP para evitar el congestionamiento son dos:
Se mantiene un segundo tamaño de ventana, llamado ventana de congestionamiento, eligiendose como tamaño efectivo en cada momento el mínimo entre este tamaño y el solicitado por el receptor.
Cuando se pierde un segmento, se reduce a la mitad la ventana de congestionamiento, hasta el mínimo de un segmento.
Para aquellos segmentos que se mantengan dentro de la ventana efectiva, se aplicará una anulación exponencial al temporizador en cada pérdida.
El resultado de esto es que, a medida que aumenta el congestionamiento, se reduce rápidamente el volumen de tráfico, así como la velocidad a que se retransmiten los segmentos perdidos.
Esta segunda técnica se aplica una vez se ha resuelto el congestionamiento. Para evitar que éste se reproduzca de nuevo rápidamente, el tamaño de la ventana de congestionamiento se va aumentando en sólo un segmento por cada asentimiento recibido correctamente.
Cuando se crea una nueva conexión o se pasa un periodo de congestionamiento, se establece el tamaño inicial de dicha ventana a un segmento, que irá aumentando uno a uno, como se ha explicado.
El desarrollo normal de este algoritmo llevaría, sin embargo, a un aumento exponencial, pues cada bloque de segmentos que se envía duplica en teoría la ventana (un segmento por cada asentimiento). Para que el arranque sea realmente tan lento como se espera, se aplica la siguiente regla:
Al llegar el tamaño de ventana a la mitad del que tenía antes del congestionamiento, el protocolo entra en fase de prevención de congestionamiento. Durante esta fase, se aumenta el tamaño en un solo segmento sólo si, para todos los segmentos de la ventana, se tiene acuses de recibo.
Por lo general, el software TCP en una máquina espera de forma pasiva la petición de conexión iniciada por la otra máquina. Sin embargo, el algoritmo de negociación (handshake) se ha diseñado para que también funcione cuando ambas máquinas inician la conexión al mismo tiempo.
Otra eventualidad que debe tener en cuenta este algoritmo es la naturaleza no confiable de la red IP subyacente. Los datagramas pueden perderse, retrasarse, duplicarse o entregarse en desorden. Estas dos premisas marcan el diseño de la negociación de conexión TCP.
TCP usa una negociación en tres etapas:
Este esquema de negociación impide que la pérdida, desorden o duplicación de estos mensajes pueda llegar a confundir a una de las partes, dejándola en un estado incorrecto.
El protocolo permite además que estos mensajes, que en principio estarían vacíos de datos, puedan transportar ya por adelantado los primeros datos, los cuales serían retenidos sin pasarlos al software de aplicación hasta que se complete la negociación.
En los tres pasos de la negociación, no sólo se ponen de acuerdo los extremos en iniciarla, sino también en qué números de secuencia van a usar inicialmente en cada uno de los flujos de octetos.
La elección de los números a usar es aleatoria. Se hace así para evitar determinados problemas derivados de la reiniciación de conexiones con los mismos números.
La negociación para la terminación de conexiones es una modificación del algoritmo en tres etapas:
Como se puede observar, la segunda etapa ha quedado dividida en dos. Debido a que el hecho de informar al programa de aplicación de la solicitud de cierre y obtener respuesta puede tomar un tiempo considerable, sobre todo si comprende intervención humana, se ha incluido un primer asentimiento que evita la retransmisión del primer mensaje FIN.
Tras este primer asentimiento, el protocolo da por terminado el flujo sólo en un sentido, pudiéndose aún enviar datos en el otro. El flujo recién cerrado rechazará cualquier segmento de datos, aunque aún dejará pasar los acuses de recibo correspondientes a los datos del flujo contrario.
A veces se presentan condiciones que obligan a un programa de aplicación o al software de red a interrumpir una conexión.
La secuencia de ruptura es la siguiente:
Puede ser que sea el propio software TCP el que genere el RST, por ejemplo si detecta un estado de operación no previsto en el protocolo o un comportamiento anormal del otro extremo.
Aunque fue diseñado para un entorno de red de redes, donde los retardos son relativamente grandes, diversas investigaciones han demostrado una excelente capacidad de TCP para adaptarse a las más diversas velocidades.
El rendimiento en el uso de una red local Ethernet típica de 10 Mbps es excelente, y se ha demostrado que puede llegar hasta 1 Gbps sin merma de rendimiento.
El haber sido desarrollado y probado en el trabajo real durante años es la clave de la fuerza de TCP, lo mismo que IP y otros elementos de la familia de protocolos. Durante su larga historia, TCP se ha tenido que enfrentar a los más diversos problemas, que han ido siendo subsanados a medida que se descubrían.
Uno de los más importantes problemas de rendimiento es el llamado síndrome de la ventana tonta (SWS, silly window syndrome), que se puede presentar cuando emisor y receptor operan a velocidades muy diferentes.
Si el receptor es muy lento procesando la información recibida, enviará acuses de recibo solicitando en el campo ventana tamaños muy pequeños, incluso de un sólo octeto. Si no se hace nada por evitarlo, el receptor adaptará su ventana de transmisión a un octeto y continuará transmitiendo datagramas con un sólo octeto de datos. Esto no viola las normas del protocolo, pero es nefasto para el rendimiento de la red, pues se desperdicia mucho ancho de banda y esfuerzo computacional de las máquinas implicadas.
La ventana tonta también se puede producir por culpa del emisor, por una implementación del protocolo demasiado agresiva, que envíe los datos según le llegan de la capa de aplicación. También un desajuste entre el tamaño de los bloques de datos enviados por una aplicación y el de los segmentos del protocolo puede hacer que último segmento de cada bloque esté casi vacío de datos.
En los primeros tiempos de TCP, este síndrome era un problema constante. Posteriormente, se han ido desarrollando diversas soluciones heurísticas que hoy en día se utilizan con éxito.
La solución en el receptor es retener el envío de los asentimientos hasta que el espacio libre en la ventana de recepción se haya hecho lo suficientemente grande.
La complicación es definir en cada momento qué es lo que se considera suficientemente grande. Normalmente esta cantidad suele ser la mitad de la ventana o el tamaño mayor de un segmento.
Antes de empezar a retardar el acuse de recibo, ha de enviarse uno indicando tamaño de ventana cero, lo cual suspenderá al emisor, permitiendo así que el receptor se recupere a medida que la aplicación cliente va leyendo datos.
El inconveniente del retardo en los acuses de recibo es que, si se retrasa mucho, el emisor dará por perdidos los datos y los retransmitirá, produciendo una carga innecesaria en la red. Otro efecto negativo es la confusión que se puede producir en el emisor al medir los tiempos de ida y vuelta.
Para minimizar estos efectos, el estándar TCP establece un tiempo máximo de retención de asentimientos de 500 milisegundos, recomendándose que se retorne un asentimiento para cada segmento recibido, con el objetivo de facilitar las estimaciones de tiempos de ida y vuelta.
La solución equivalente en el lado del emisor es retardar el envío de cada segmento hasta que se hayan acumulado suficientes datos para que el envío sea rentable. A esta técnica se le llama clumping (agrupamiento).
La dificultad está en que el protocolo no puede saber cuánto tardará el programa de usuario en llenar suficientemente el buffer, ni conoce cuál es el retardo mínimo que la aplicación requiere. Si espera demasiado, los retardos serán excesivos, si espera poco, los segmentos serán muy pequeños, empeorando el rendimiento.
La solución que enunció Nagle, ampliamente utilizada, es especialmente elegante y flexible, combinando diferentes retardos según las condiciones de la red en cada momento (self clocking, auto-temporización) con la estimación de las necesidades de la aplicación a partir de la llegada de acuses de recibo desde el receptor.
El procedimiento es el siguiente: cuando una aplicación genera datos adicionales para enviarse sobre una conexión por la que se han transmitido datos recientemente, pero de los cuales no hay aún acuse de recibo, deben retenerse éstos en memoria intermedia hasta que se haya alcanzado el tamaño máximo de un segmento, aún cuando la aplicación haya solicitado una operación push. Si en el intervalo de espera llega un acuse de recibo, se debe desencadenar el envío inmediatamente.
A través de esta técnica se consigue la adaptación a combinaciones arbitrarias de retardos de red, tamaños diversos de segmento y velocidades de las aplicaciones, sin disminuir en general el rendimiento.
@ElPamplina@masto.es
elpamplinadecai@gmail.com