Previous Up Next

4.5  Transmission Control Protocol

TCP es un protocolo orientado a conexión, fiable. Específicamente, se dice que TCP proporciona un servicio de transporte de un flujo de bytes, orientado a conexión, fiable.

El concepto de transporte de un flujo de bytes, indica que la aplicación entrega a TCP los bytes que desea transmitir y que TCP no interpretará esos bytes, únicamente los hará llegar a la aplicación receptora en el orden en que se los entregaron. En otras palabras, si una aplicación entrega 20 bytes a TCP, seguido de una entrega de 30 bytes, seguido de una entrega de 60 bytes, la aplicación receptora no sabe cuántas entregas se hicieron al TCP, sino que puede ocurrir que al leer los bytes recibidos, lo haga en cinco lecturas de 22 bytes.

TCP proporciona fiabilidad utilizando los siguientes mecanismos:

El sistema que utiliza TCP para proporcionar fiabilidad se engloba en los llamados protocolos de ventana deslizante.

4.5.1  Protocolos de Ventana Deslizante

La pregunta que nos hacemos es, ¿cómo puede un protocolo proporcionar un servicio de transferencia fiable sobre un sistema de comunicaciones no fiable?. Una de las técnicas más utilizadas es el ARQ (Automatic Repeat Request) o petición de retransmisión automática. En ARQ, el receptor informa al emisor del éxito o fracaso de la última transmisión, a través de una trama de reconocimiento. Los reconocimientos pueden ser positivos (reconocimiento de éxito) y negativos (reconocimiento de errores en la trama recibida). No todos los protocolos utilizan simultáneamente ambos tipos de reconocimiento. Por ejemplo TCP sólo utiliza reconocimientos positivos y cuando hay errores no envía nada, utilizando temporizadores para las retransmisiones.

En la Figura 4.4 se muestra la transmisión de dos tramas con reconocimiento. Este sistema se conoce con el nombre de Stop & Wait porque después de enviar un paquete, el emisor debe parar de transmitir y esperar la recepción del reconocimiento. Obsérvese que el tiempo efectivo utilizado para transmitir una trama es desde el inicio de la transmisión de la trama hasta que se recibe el reconocimiento. En este caso la eficiencia de transmisión es baja, puesto que el hecho de que el emisor deba esperar el reconocimiento le imposibilita para seguir transmitiendo.


Figura 4.4: Stop & Wait

Supongamos que modificamos el algoritmo de manera que el emisor pueda tener como máximo F tramas no reconocidas. El emisor podrá enviar como máximo F tramas sin recibir reconocimiento de ellas. F se denomina el tamaño de la ventana y en cuanto reciba el reconocimiento de la primera trama (ACK(1)), podrá enviar una nueva trama, y así sucesivamente. Este es el caso representado en la Figura 4.5, para un tamaño de ventana F=6.


Figura 4.5: Ventana Deslizante

El hecho de utilizar ventana deslizante implica que las tramas de información deben ir numeradas para que los reconocimientos indiquen mediante este número qué trama ha sido recibida correcta o incorrectamente.

Es importante destacar que en un sistema de ventana deslizante, no es necesario enviar una trama de reconocimiento por cada trama de datos. Se puede reconocer con una sola trama de reconocimiento varias tramas de datos teniendo en cuenta que si se han enviado 3 tramas de datos y se reconoce la trama 3, entonces, de forma automática, quedan reconocidas la 1 y la 2.

Es habitual que en una comunicación entre dos ordenadores, cada uno de ellos actúe simultáneamente de emisor y receptor, ya que la información suele fluir tanto en un sentido como en el otro. Para aumentar la eficiencia, el paquete de información incluye un campo utilizado para reconocimiento, evitándose el tener que enviar tramas de reconocimiento y de información diferentes cuando ambas tramas viajan en el mismo sentido. A esta técnica se le conoce con el nombre de piggybacking.

4.5.2  TCP y la ventana deslizante

TCP utiliza un mecanismo especializado de ventana deslizante con objeto de mejorar la eficiencia de transmisión y realizar un control de flujo.

Como se ha comentado anteriormente, TCP ve los datos de usuario como un flujo de bytes que divide en segmentos para ser transmitidos. Por esta razón TCP no numera los segmentos sino los octetos transmitidos y a su vez, los reconocimientos reconocen octetos recibidos y no segmentos recibidos.

En TCP el tamaño de ventana indica el número de octetos que pueden ser recibidos y el receptor indica al emisor, en cada trama enviada, qué tamaño de ventana tiene disponible para recibir el siguiente segmento, realizando, de esta manera, el control de flujo. Si se indica un tamaño de ventana 0, el emisor deja de enviar segmentos hasta que recibe otro paquete indicando un tamaño de ventana distinto de 0.

4.5.3  El segmento TCP

La Figura 4.6 muestra cómo es el segmento TCP.


Figura 4.6: Segmento TCP

Los primeros 32 bits indican los puertos origen y destino. Los siguientes 32 bits son el número de secuencia. El número de secuencia identifica el byte del flujo de datos que está siendo enviado y que ocupa la primera posición en el campo de datos del segmento. El primer byte de un flujo en un segmento TCP no lleva el número 1 como número de secuencia, sino que se elige un número aleatorio (Inital Sequence Number) en cada conexión realizada y este número representa el número de secuencia del primer byte a transmitir durante esa conexión. Si el ISN de una conexión es el 1415531521, el primer byte estaría identificado por este mismo número y el segundo sería 1415531521+1, y así sucesivamente. De esta forma si un segmento TCP llega a una conexión equivocada (cosa bastante inverosímil), los números de secuencia de ambas conexiones no podrían a llegar a confundirse nunca, ya que ambas conexiones utilizaron un ISN muy diferente y los datos que llegaron a la conexión equivocada nunca serán pasados a la aplicación equivocada (el TCP generaría un error al recibir un número de secuencia no esperado).

El campo del número de reconocimiento (acknowledge number) se utiliza para almacenar el número del siguiente byte que se espera recibir. Este campo sólo se decodifica si el flag ACK está activado. Si se recibió un paquete de datos con el número de secuencia 1415531521 y con 100 bytes de datos (desde el 1415531521 hasta el 1415531620), el segmento TCP de respuesta llevaría activado el flag ACK y el número de reconocimiento sería el 1415531621, puesto que espera recibir el siguiente al 1415531620.

El campo hdr len (Header Length) indica la longitud de la cabecera que puede llevar opciones haciendo que la longitud de la cabecera varíe.

A continuación vienen 6 bits reservados (no se utilizan) y 6 bits que actúan de flags:

El campo window size es de 16 bits e indica el tamaño de ventana en bytes. Los 16 bits siguientes se utilizan para el checksum y los siguientes 16 bits para indicar un urgent pointer (el urgent pointer se explicará en la sección 4.5.5).

Existen varias opciones en TCP, y la más utilizada es el MSS (Maximum Segment Size). Cada extremo de una conexión indica al otro cuál es el tamaño máximo de segmento que desea recibir. Este valor se especifica dentro del campo de opciones al inicio de la conexión.

4.5.4  Establecimiento y cierre de una conexión TCP

4.5.4.1  Establecimiento de la conexión

El proceso que abre una conexión, lo hace enviando un segmento TCP con el flag SYN activado y un número de secuencia (ISN) que identificará los bytes transmitidos. No se transmiten datos de usuario. El flag ACK no está activado porque todavía no hay bytes que reconocer.

El proceso que recibe este segmento, contesta con un segmento TCP con el flag SYN activado, un número de secuencia aleatorio, el flag ACK activado y el número de reconocimiento igual al ISN recibido más uno (se supone que un segmento SYN consume un byte).

El proceso que inició la conexión responde al segmento recibido con un segmento con el flag ACK activado y el número de secuencia de reconocimiento igual al número de secuencia recibido más uno.

En este momento ya está establecida la conexión. Esta apertura de conexión se conoce con el nombre de “establecimiento de conexión a tres bandas”, porque se utilizan tres segmentos. La Figura 4.7 muestra el establecimiento de una conexión TCP.


Figura 4.7: Establecimiento de la conexión

4.5.4.2  Timeout en el establecimiento de la conexión

Puede ocurrir que el extremo remoto no responda al segmento SYN de apertura de conexión. En ese caso se reintenta la apertura de la conexión varias veces. Por ejemplo, en un S.O. Linux con un kernel 2.4.10, el resultado obtenido al intentar establecer una conexión fue:

No Time        Source         Destination      Prot Info

1  0.000000    192.168.69.5   192.168.69.19    TCP  32773 > 23 [SYN] 

                                                    Seq=4209576593 Ack=0 Win=5840

2  2.990898    192.168.69.5   192.168.69.19    TCP  32773 > 23 [SYN] 

                                                    Seq=4209576593 Ack=0 Win=5840

3  8.990899    192.168.69.5   192.168.69.19    TCP  32773 > 23 [SYN] 

                                                    Seq=4209576593 Ack=0 Win=5840

4  20.990900   192.168.69.5   192.168.69.19    TCP  32773 > 23 [SYN] 

                                                    Seq=4209576593 Ack=0 Win=5840

5  44.990899   192.168.69.5   192.168.69.19    TCP  32773 > 23 [SYN] 

                                                    Seq=4209576593 Ack=0 Win=5840

6  92.990900   192.168.69.5   192.168.69.19    TCP  32773 > 23 [SYN] 

                                                    Seq=4209576593 Ack=0 Win=5840

El tiempo de espera entre intentos de conexión es de la forma 3·2nn=0,…,4. El número de reintentos antes de decidir que la conexión no se puede realizar se controla a través de la variable /proc/sys/net/ipv4/tcp_syn_retries cuyo valor durante la prueba realizada era de 5.

4.5.4.3  Maximum Segment Size (MSS)

Una de las opciones que se incluyen en los segmentos SYN es el Maximum Segment Size. Cada uno de los extremos informa al otro del tamaño máximo de segmento que desea recibir. Como regla general, cuanto mayor sea el MSS, sin que se produzca fragmentación a nivel IP, mayor será la eficiencia ya que se amortiza las cabeceras de IP y TCP. Si el nivel físico es Ethernet con tramas Ethernet II, el mayor datagrama IP será de 1500 bytes. Si restamos la longitud de las cabeceras IP y TCP tendremos el MSS: 1500−20−20=1460 bytes. Si se utiliza un encapsulado IEEE 802.3 entonces el MSS tendría un valor de 1452 bytes.

4.5.4.4  Cierre de una conexión TCP.

El cierre de una conexión se realiza mediante segmentos TCP con el flag FIN activado. El cierre de una conexión TCP utiliza 4 segmentos. Esto es debido a que una conexión TCP es full-duplex, y cada una de las direcciones debe ser cerrada independientemente de la otra. La Figura 4.8 muestra el cierre de una conexión TCP.


Figura 4.8: Cierre de una conexión TCP

La idea en un cierre de conexión TCP es que cualquiera de los extremos que haya enviado todos los datos puede realizar un cierre de su conexión (half-close), mientras que puede seguir recibiendo datos del extremo remoto.

4.5.4.5  Diagrama de transición de estados de TCP

La Figura 4.9 muestra el diagrama de transición de estados de TCP. Algunos estados son específicos del servidor mientras que otros lo son del cliente. Por ejemplo, como se ha comentado en la sección 4.3, un servidor puede estar “esperando” que los clientes se conecten a él. La espera corresponde a un estado de “LISTEN”. En realidad se dice que el servidor está “escuchando”, en un puerto, nuevas conexiones. Cuando se ejecuta un servidor pasa de estado “CLOSED” a estado “LISTEN” esperando que le lleguen segmentos TCP con el flag SYN activado (establecimiento de conexión). Sin embargo, cuando se ejecuta un cliente, éste iniciará directamente la conexión con el servidor enviándole un segmento SYN.


Figura 4.9: Diagrama de transición de estados de TCP

La Figura 4.10 es un ejemplo de los estados y los segmentos del cliente y servidor en la fase de establecimiento y cierre de la conexión para un modo de operación normal.


Figura 4.10: Estados de TCP correspondientes a un establecimiento y cierre de conexión

4.5.4.6  2MSL Timeout

En la Figura 4.9, debajo del estado TIME_WAIT, puede leerse “2MSL timeout”. Esto quiere decir que cuando el TCP que realiza un cierre activo (active close) llega al estado TIME_WAIT debe esperar un cierto tiempo antes de dar por finalizada la conexión. Ese tiempo es 2· MSL donde MSL3 (Maximum Segment Lifetime) es el tiempo máximo de vida de un segmento. Dado que un segmento TCP viaja encapsulado en un datagrama IP, y éste tiene un campo TTL (time-to-live), un segmento TCP también tendrá un tiempo de vida en la red.

¿Por qué el TCP que realiza un cierre activo debe esperar un cierto tiempo?. El cierre de una conexión nunca es una cosa sencilla, ya que los paquetes de cierre pueden perderse y los extremos TCP pueden quedarse en un estado incorrecto. Refiriéndonos a la Figura 4.10, una vez que el cliente ha recibido el segmento FIN, ha pasado al estado TIME_WAIT y envía un segmento de reconocimiento. Puede suceder que ese segmento de reconocimiento se “pierda” y no llegue nunca a su destino. Si así fuera el caso, el servidor volvería a retransmitir el segmento FIN. Por tanto, el cliente espera un tiempo prudencial (2· MSL) de manera que pueda recibir esa retransmisión del segmento FIN si el reconocimiento se ha perdido.

Un efecto colateral de este estado de espera, es que durante ese tiempo el socket formado por la dirección IP del cliente, puerto del cliente, dirección IP del servidor, puerto del servidor, no puede ser usado para otra conexión. En realidad, la propia implementación del TCP implica que un puerto local que esté en un socket en estado TIME_WAIT no puede ser usado de nuevo hasta que expire ese estado.

4.5.4.7  Segmentos RESET

En general, TCP envía un segmento RESET siempre que recibe un segmento que no parece ser correcto para la conexión que tenía establecida.

Un caso bien conocido en el que se envía un segmento RESET es cuando se intenta realizar una conexión a un puerto no activo de un servidor (un puerto no activo significa que no hay ninguna aplicación que esté “escuchando” en ese puerto de ese servidor). En este caso, el servidor responde al segmento SYN con un segmento RST.

4.5.5  Transferencia de información

4.5.5.1  El flag PSH

Los segmentos TCP que transportan datos de usuario suelen llevar activado el flag de push (PSH) aunque no es estrictamente necesario. Recordemos que el flag PSH indica que los datos de ese segmento y los datos que hayan sido almacenados anteriormente deben ser pasados a la aplicación receptora lo antes posible. Se puede dar el caso que varios segmentos que transportan datos no lleven activado el flag PSH; el TCP receptor almacenará esos datos pero no los entregará a la aplicación receptora hasta que reciba un segmento con el PSH activado.

Sin embargo, la mayoría de implementaciones TCP activan el flag de PSH en cada segmento que transporta datos, haciéndolos disponibles a la aplicación inmediatamente.

4.5.5.2  El flag URG y el campo Urgent Pointer

El flag URG y el campo Urgent Pointer se utilizan cuando se desea enviar datos que se suponen que no pertenecen al flujo “normal” de bytes, sino que son datos urgentes. Estos bytes urgentes se insertan en medio del flujo “normal” de bytes, activando el flag URG y estableciendo el campo Urgent Pointer de la cabecera de TCP a un valor que sumado con el valor del campo de número de secuencia de la cabecera de TCP se obtiene el número de secuencia del último byte de los datos urgentes.

La especificación original de TCP no deja claro si el Urgent Pointer debe apuntar al último byte de los datos urgentes o al byte que sigue al último byte urgente. Aunque documentos posteriores aclararon que el Urgent Pointer debe apuntar al último byte urgente, la mayoría de implementaciones de TCP (derivadas de la realizada en Berkeley) continúan utilizando la interpretación equivocada. En Linux es posible indicar en qué forma se desea que funcione el Urgent Pointer variando el valor de la variable /proc/sys/net/ipv4/tcp_stdurg. Si el valor de esta variable es 0 se utiliza la interpretación de Berkeley, si vale 1 se utiliza la interpretación del estándar.

TCP no especifica mucho más sobre los datos urgentes, así no hay forma de saber cuándo se inician los bytes urgentes en el flujo de datos. Es la aplicación la que debe saber cómo extraer los datos urgentes.

4.5.6  RTT y Timeouts

En el funcionamiento de TCP y el mecanismo de ventana deslizante (sección 4.5.2) se indica que TCP realiza un reconocimiento positivo de los segmentos que ha recibido correctamente. Si el segmento se recibe con errores, el TCP receptor lo ignora. Al cabo de un cierto tiempo (timeout) el TCP emisor lo retransmite. El cálculo del timeout se realiza de forma dinámica para cada conexión TCP que se establece ya que las condiciones de transmisión pueden cambiar por diversas razones, como por ejemplo la congestión de la red o los diferentes caminos seguidos por los datagramas IP para diferentes conexiones remotas.

El cálculo del timeout se basa en el cálculo del RTT (Round Trip Time). El RTT, definido de forma genérica, es el tiempo que tarda un paquete en ir del emisor hasta el receptor sumado al tiempo que tarda en llegar la confirmación del receptor al emisor.

TCP calcula el RTT de dos formas diferentes (aunque complementarias):

  1. TCP inicia un temporizador cuando se envía un byte (aleatorio) que viaja en un segmento. Cuando TCP recibe el ACK del segmento donde viajaba el byte detiene el temporizador y obtiene el RTT.
  2. Una segunda forma de calcular el RTT es a través de una opción de TCP, el timestamp. El TCP emisor incluye una marca temporal (timestamp) como opción del TCP. El TCP receptor copia la marca temporal recibida en el segmento de reconocimiento enviado. De esta forma el TCP emisor puede calcular el RTT de forma más sencilla que el caso anterior.

El RTT que se utiliza para calcular el tiempo de timeout corresponde a un valor medio obtenido con los diferentes cálculos de RTT durante la misma conexión.

El timeout (RTO) se calcula de forma proporcional al RTT4.


Previous Up Next