=============================== Sistemas Distribuidos I (75.74) =============================== ------------------------- Práctica de sockets y RPC ------------------------- :Author: Leandro Lucarella (77891) Organización ============ La práctica se divide en componentes comunes y específicos: common Funciones comunes, para salida formateada usando write(2). libtcp Funciones generales de sockets. parte1 Programas pertenecientes a la primera parte, el servidor iterativo concurrente utilizando TCP. Se compone de 3 programas: client Programa cliente. server Servidor concurrente tipo inetd (hace un fork(2) para cada nueva conexión llamando a serverhandler). serverhandler Servidor iterativo encargado de manejar la conexión. parte2 Programas pertenecientes a la segunda parte, el servidor utilizando RPC Se compone de 2 programas: set_client Programa cliente. set_server Servidor RPC con las funciones remotas. Compilación =========== Para compilar los programas basta con usar `make`. Uso === Ambas versiones del programa se utilizan de igual forma. Hay que correr primero el servidor y luego el cliente. El cliente toma la entrada del usuario por entrada estándar. Se aceptan comandos del tipo:: OPERACION PARTE1 PARTE2 ... PARTEN Donde OPERACION es: put, find o del. put agrega un string al set, enviando N mensajes al servidor (uno por cada parte y enviando una marca de fin en el último para que se procese), el string es la concatenación de todas las partes). put devuelve 0 si tuvo éxito o 1 si ya había un string igual. find se fija si un string determinado, retornando 0 si está o 1 si no. del elimina un string del set, retornando 0 si se tuvo éxito o 1 si no se encontró. El programa se detiene (enviando un comando QUIT) cuando se ingresa una línea vacía o se cierra la entrada estándar. Para hacer pruebas exhaustivas se provee de un script llamado test.sh (en ambas variantes de la práctica) que corre varios clientes simultáneamente realizando operaciones al azar. El servidor debe ser lanzado antes. Protocolo TCP ============= El protocolo tiene como objetivo manipular un set de datos (un conjunto de strings) en el servidor. Hay 3 operaciones soportadas: PUT, FIND y QUIT. Cada operación viene acompañada por un id de cliente, una marca de fin, y una porcion del string a agregar/buscar/quitar. La operación se concreta recién al recibir la última porción del string (indicado por la marca de fin) y el string utilizado para la operación es la concatenación de las porciones recibidas en cada mensaje. El paquete de "request" tienen entonces la siguiente estructura:: +----------+---------+----------+----------+---------------+ | type | end | clientid | lenght | payload (opt) | +----------+---------+----------+----------+---------------+ /- 2 bits -/- 1 bit -/- 5 bits -/- 1 byte -/- 0-255 bytes -/ type Es el tipo de mensaje: PUT (0), FIND (1), DEL (2), QUIT (3). end Marca de fin, indica cuando se está enviando la última porción de un comando. clientid Id del cliente. lenght Tamaño del payload en bytes. payload Fragmento del string que compone el comando. Al recibir la última porción, el servidor procesa el pedido y envía una respuesta con el resultado. El paquete de la respuesta se compone únicamente de un int (entero de 4 bytes). Cada valor es un código de resultado. En general siempre el valor 0 se usa para indicar operación exitosa. En caso de error los siguientes resultados están definidos: 1 El string no existe 2 El string ya existe Protocolo RPC ============= Esta variante es prácticamente igual a la anterior sólo que se utilizan llamadas a procedimientos remotos para enviar cada comando. La firma de estos procedimientos (hay uno por cada comando o "type") es:: int prodedimiento(string payload, int end, int clientid); A excepción del QUIT que sólo lleva como parámetro al clientid. Al ser cada fragmento de un comando una llamada a procedimiento remoto, siempre se obtiene un resultado, pero el resultado que vale es el de la última llamada (la que lleva end en 1) ya que es en este momento cuando se realiza el procesamiento. Para implementar esto se utilizaron archivos temporales para ir "acumulando" los fragmentos del comando, utilizando un archivo por cada cliente conectado para evitar "colisiones". Los códigos de retorno utilizados son los mismos que en la variante anterior y toda la semántica del protocolo es también la misma. .. vim: filetype=rst :