|
Home /Docencia /Ing. Informática /Redes y Servicios de Comunicaciones /Práctica | |
Práctica Redes y Servicios de Comunicaciones: Implementación de TCP sobre UDPFecha: 5 de Noviembre de 2003.Conceptos: entramado, estados TCP, establecimiento y cierre de conexiones TCP, ventana deslizante, control de congestión en TCP, control de flujo en TCP Programación: sockets UDP, colas circulares, procesos, manejadores de señales, temporizadores, comunicación entre procesos. Plazo de entrega: 5 de Febrero de 2004 (instrucciones) Enunciado comprimido: Profesores: Carlos García Rubio Andrés Marín López y Norberto Fernández García Guillermo Díez-Andino Sancho |
|
|
El objeto de esta práctica es ilustrar los conceptos vistos en las clases de teoría sobre el nivel de control de transporte en Internet: TCP. La práctica propone la utilización de sockets UDP para la construcción de una biblioteca que proporcione a las aplicaciones que la utilicen una funcionalidad similar a la ofrecida por TCP, aunque con restricciones. Para la realización de la práctica se requieren conocimientos básicos sobre sockets UDP, y dependiendo del lenguaje de programación elegido, también serán necesarios conocimientos adicionales sobre gestión y comunicación entre procesos, hebras, manejadores de señales y temporizadores. La práctica proporciona algunos interfaces para implementar en la biblioteca, también se proporcionan algunas funciones auxiliares y código fuente de ejemplo. También se proporcionan ficheros de pruebas para poder auto-evaluar la práctica de forma bastante aproximada. |
|
Para poder conseguir una implementación de TCP sobre UDP el primer paso es poder tener un formato de trama en el que incluir la información de:
La definición en C de esta cabecera se proporciona con el resto del material, y corresponde a la estructura: typedef struct ttcp_udp_header{ |
|
La abstracción de sockets TCP_UDP que maneja la biblioteca a desarrollar debe de tener una serie de propiedades que nos permitirán implementar la funcionalidad de TCP. En los ficheros entregados se propone una estructura denominada stcp_socket que contiene las propiedades recomendadas que debe de tener una conexión:
|
|
Debemos de implementar una máquina de estados que refleje como debe de actuar la conexión en cada momento dependiendo de las entradas. No es necesario implementar el total de estados de TCP, pues nos limitaremos a establecimientos y liberaciones de conexiones no simultáneas (three way handshake). En cuanto al interfaz programático, debe de realizarse igual que en TCP:
El comportamiento de la biblioteca al recibir segmentos fuera de secuencia es el siguiente:
|
|
Cada conexión tiene un contador de números de secuencia que utiliza para numerar cada uno de los bytes que envía. Distintas conexiones tendrán distintos números de secuencia. En el caso de los servidores, hay que tener en cuenta que el número de secuencia junto con la dirección del otro extremo, se utilizan para distinguir los segmentos recibidos de una u otra conexión. Estos números de secuencia también nos sirven para saber a que datos se refiere un asentimiento recibido. |
|
De la aplicación iremos recibiendo buffers con datos para enviar al otro extremo. A la hora de ir haciendo los sendto, deberemos de ir teniendo en cuenta el tamaño de la ventana de congestión, su umbral, y el de la ventana de recepción que anuncia el otro extremo. Debemos de implementar el mecanismo de slow start, a fin de garantizar que no vamos a congestionar la red y que en caso de congestión disminuiremos nuestra tasa de envío y cwnd. |
|
Cuando recibamos un datagrama con la indicación de error (ERR) debemos pedir retransmisión, enviando un ack duplicado. Los temporizadores tendrán un valor fijo de 1 segundo. El numero de retransmisiones que deberemos de hacer será de seis intentos. En caso de que venza el temporizador de envío y no hayamos recibido el ack de un datagrama, deberemos realizar la retransmisión del datagrama en cuestión. Además estos eventos también pueden influir en la percepción de congestión, afectando al parámetro ssthresh. Hay que considerar también los temporizadores de los segmentos de establecimiento (SYN) y liberación (FIN) de conexión, y el temporizador final de 2MSL (que a efectos prácticos valdrá 2 segundos). |
|
Es conveniente utilizar algún mecanismo de comunicación entre procesos (IPC) para compartir datos entre los procesos que utilizan la biblioteca. Por ejemplo, si una aplicación llama a tcp_accept() sobre un socket pasivo, obtiene un nuevo socket conectado (hijo) y posteriormente hace un fork(), ambos procesos deben tener acceso al socket pasivo y al hijo. Además ambos comparten el mismo socket UDP, que es único para todos los sockets que se obtengan de sucesivas llamadas a tcp_accept sobre un mismo socket pasivo. Lo que proponemos es utilizar memoria compartida de forma que todos los objetos stcp_socket que gestiona la biblioteca se encuentren en una zona de común acceso a todos los procesos, y que los descriptores de los stcp_socket sean un offset dentro de la memoria compartida a la zona correspondiente al socket en cuestión. En el material entregado se encuentran las funciones para crear y liberar la memoria compartida. |
|
En la estructura de socket que se propone se introducen diversas variables para controlar las colas circulares necesarias para implementar el algoritmo de ventana deslizante que utiliza TCP. En concreto se definen:
![]() En la figura se muestra que podemos tener una gestión de las colas más sencilla si podemos sacrificar un poco de memoria. Por ejemplo, se propone que la estructura de almacenamiento (rx_buf/tx_buf) se divida en trozos de tamaño MSS y se copien los datos de cada segmento en cada trozo, de forma que el inicio del segmento y el fin del segmento se apuntan desde el array auxiliar (rx/tx) donde también se indica si un segmento esta libre o no. La razón de ser del índice al inicio de segmento es que la aplicación al consumir datos puede solicitar una lectura de un tamaño inferior al del bloque en cuestión, y este índice nos facilita este tipo de situaciones. El motivo del indicador de segmento libre es el siguiente: en la cola de recepción, indica que la aplicación ya ha consumido este segmento; en la cola de transmisión indica que se ha recibido el asentimiento correspondiente. En ambos casos, el espacio en la cola de datos correspondiente está libre y se puede utilizar para almacenar un nuevo segmento que se va a enviar o un segmento que se ha recibido. En el material entregado se propone una implementación de varias funciones para:
|
|
En el nivel TCP existen diversos temporizadores y es necesario gestionarlos de manera coordinada y mínimamente eficiente. En general cada segmento que se envía tiene asociado un temporizador (RTO) que en caso de vencimiento marca la retransmisión del segmento. Puesto que podemos enviar varios segmentos (dependiendo de cwnd y nagle) antes de recibir sus asentimientos correspondientes, es necesario un mecanismo para gestionar todos. En la figura se muestra una propuesta de utilización de una cola para almacenar los temporizadores. ![]() En la figura se muestran los instantes de envío de cuatro segmentos (T0, T1, T1-2, T1-3). Cada segmento tiene asociado su propio tiempo de retransmisión (RTO_0, RTO_1, RTO_2, RTO_3) aunque en general serán muy parecidos a no ser que ocurran pérdidas o congestión en la red. Lo que se propone es almacenar en la cola de temporizadores la diferencia de los tiempos previstos de retransmisión entre cada segmento y el anterior. Esto permite que en el sistema solo haya un temporizador activo, para el evento que va a ocurrir primero. Cuando activamos dicho temporizador almacenamos el instante en una variable (en la figura corresponde a T0). Cuando salta el temporizador, se retransmite el segmento y se vuelve a activar el temporizador con el valor del siguiente elemento en la cola. En caso de que sea necesario quitar el temporizador activo (como en la figura que ha llegado el ACK_1 que asiente el segmento 0), tenemos que:
|
|
Para la gestión de temporizadores se recomienda el uso de las funciones setitimer y getitimer con el ITIMER_REAL. La señal SIGALRM se entregará al proceso en cuyo socket hay que retransmitir un segmento, y si se ha registrado previamente un manejador para esta señal (con sigaction) se ejecutará la función indicada. También resulta aconsejable registrar manejadores para cuando haya datos disponibles en el socket UDP. En caso de que los datos puedan ser para varios sockets distintos, se puede optar por dos alternativas.
|
|
Para homogeneizar las prácticas se entrega un fichero con las definiciones de funciones a implementar por la práctica, asi como el entramado: tcp_udp.h. En caso que se opte por la utilización de manejadores de señales se da un ejemplo de parte de un servidor. |
|
|
|
Especificaciones:
|
Última actualización: Thu, 03 Oct 2002 15:09:52 GMT |
|