]> git.llucax.com Git - z.facultad/75.74/practicos.git/commitdiff
Corrección de práctica 3.
authorLeandro Lucarella <llucax@gmail.com>
Thu, 6 Jul 2006 16:12:21 +0000 (16:12 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Thu, 6 Jul 2006 16:12:21 +0000 (16:12 +0000)
21 files changed:
practicas/practica3-corregida/Makefile [new file with mode: 0644]
practicas/practica3-corregida/README [new file with mode: 0644]
practicas/practica3-corregida/common/Makefile [new file with mode: 0644]
practicas/practica3-corregida/common/common.c [new file with mode: 0644]
practicas/practica3-corregida/common/common.h [new file with mode: 0644]
practicas/practica3-corregida/libtcp/Makefile [new file with mode: 0644]
practicas/practica3-corregida/libtcp/libtcp.c [new file with mode: 0644]
practicas/practica3-corregida/libtcp/libtcp.h [new file with mode: 0644]
practicas/practica3-corregida/parte1/Makefile [new file with mode: 0644]
practicas/practica3-corregida/parte1/client.cpp [new file with mode: 0644]
practicas/practica3-corregida/parte1/gentest.py [new file with mode: 0755]
practicas/practica3-corregida/parte1/protocol.h [new file with mode: 0644]
practicas/practica3-corregida/parte1/server.cpp [new file with mode: 0644]
practicas/practica3-corregida/parte1/serverhandler.cpp [new file with mode: 0644]
practicas/practica3-corregida/parte1/test.sh [new file with mode: 0755]
practicas/practica3-corregida/parte2/Makefile [new file with mode: 0644]
practicas/practica3-corregida/parte2/gentest.py [new file with mode: 0755]
practicas/practica3-corregida/parte2/set.x [new file with mode: 0644]
practicas/practica3-corregida/parte2/set_client.cpp [new file with mode: 0644]
practicas/practica3-corregida/parte2/set_server.cpp [new file with mode: 0644]
practicas/practica3-corregida/parte2/test.sh [new file with mode: 0755]

diff --git a/practicas/practica3-corregida/Makefile b/practicas/practica3-corregida/Makefile
new file mode 100644 (file)
index 0000000..2756f0a
--- /dev/null
@@ -0,0 +1,51 @@
+
+SUBDIRS=libtcp common parte1 parte2
+
+.PHONY: clean
+all: readme.html readme.pdf
+       @set fnord $(MAKEFLAGS); amf=$$2; target=$@ ; \
+       dot_seen=no; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+       echo "Making $$target in $$subdir"; \
+       if test "$$subdir" = "."; then \
+       dot_seen=yes; \
+       local_target="$$target-am"; \
+       else \
+       local_target="$$target"; \
+       fi; \
+       (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+       || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+       done;
+
+clean:
+       @set fnord $(MAKEFLAGS); amf=$$2; target=$@ ; \
+       dot_seen=no; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+       echo "Making $$target in $$subdir"; \
+       if test "$$subdir" = "."; then \
+       dot_seen=yes; \
+       local_target="$$target-am"; \
+       else \
+       local_target="$$target"; \
+       fi; \
+       (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+       || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+       done;
+
+doc: readme.html readme.pdf
+
+docclean:
+       rm -f readme.html readme.pdf
+
+       rm readme.html readme.pdf
+readme.html: README
+       rst2html README > readme.html
+
+readme.latex: README
+       rst2latex README > readme.latex
+
+readme.pdf: readme.latex
+       pdflatex readme.latex
+       pdflatex readme.latex
+       rm -f readme.aux readme.latex readme.log readme.out
+
diff --git a/practicas/practica3-corregida/README b/practicas/practica3-corregida/README
new file mode 100644 (file)
index 0000000..21b5ac5
--- /dev/null
@@ -0,0 +1,139 @@
+===============================
+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 sobre TCP
+===================
+
+El protocolo tiene como objetivo manipular un set de datos (un conjunto de
+strings) en el servidor. Hay 3 operaciones soportadas: PUT, FIND, DEL 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 | payload     |
+  +----------+---------+----------+-------------+
+  /- 2 bits -/- 1 bit -/- 5 bits -/- 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.
+
+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 sobre 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 :
diff --git a/practicas/practica3-corregida/common/Makefile b/practicas/practica3-corregida/common/Makefile
new file mode 100644 (file)
index 0000000..cfd0c27
--- /dev/null
@@ -0,0 +1,16 @@
+
+TARGET=common.a
+LIBS=
+CFLAGS=-O0 -g
+CC=g++
+
+all: $(TARGET)
+
+$(TARGET): common.o
+       $(AR) cru $(TARGET) common.o
+
+common.o: common.h common.c
+
+.PHONY: clean
+clean:
+       rm -f *.o $(TARGET)
diff --git a/practicas/practica3-corregida/common/common.c b/practicas/practica3-corregida/common/common.c
new file mode 100644 (file)
index 0000000..b933cc7
--- /dev/null
@@ -0,0 +1,29 @@
+/* Released underOpen Software License v. 2.0
+ *
+ * This Open Software License (the "License") applies to any original work of authorship 
+ * (the "Original Work") whose owner (the "Licensor") has placed the following notice 
+ * immediately following the copyright notice for the Original Work:
+ *
+ *     Licensed under the Open Software License version 2.0
+ *
+ * See http://www.opensource.org/licenses/osl-2.0.php or the LICENSE file for more
+ * details.
+ */
+
+#include "common.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+void print_msg (FILE *fp, const char *fmt, ...)
+{
+       char linea[255];
+       va_list va;
+
+       va_start (va, fmt);
+               vsprintf(linea, fmt, va);
+       va_end (va);
+
+       write (fileno (fp), linea, strlen (linea));
+}
diff --git a/practicas/practica3-corregida/common/common.h b/practicas/practica3-corregida/common/common.h
new file mode 100644 (file)
index 0000000..a6652af
--- /dev/null
@@ -0,0 +1,21 @@
+/* Released underOpen Software License v. 2.0
+ *
+ * This Open Software License (the "License") applies to any original work of authorship 
+ * (the "Original Work") whose owner (the "Licensor") has placed the following notice 
+ * immediately following the copyright notice for the Original Work:
+ *
+ *     Licensed under the Open Software License version 2.0
+ *
+ * See http://www.opensource.org/licenses/osl-2.0.php or the LICENSE file for more
+ * details.
+ */
+
+#ifndef _ECHO_COMMON_H_
+#define _ECHO_COMMON_H_
+
+#include <stdio.h>
+
+void print_msg (FILE *fp, const char *fmt, ...);
+
+#endif // _ECHO_COMMON_H_
+
diff --git a/practicas/practica3-corregida/libtcp/Makefile b/practicas/practica3-corregida/libtcp/Makefile
new file mode 100644 (file)
index 0000000..61c1501
--- /dev/null
@@ -0,0 +1,17 @@
+# LibTcp - Internet stream version (TCP protocol)
+
+TARGET=libtcp.a
+LIBS=
+CFLAGS=-O0 -g
+CC=g++
+
+all: $(TARGET)
+
+$(TARGET): libtcp.o
+       $(AR) cru libtcp.a libtcp.o
+
+libtcp.o: libtcp.h libtcp.c
+
+.PHONY: clean
+clean:
+       rm -f *.o $(TARGET)
diff --git a/practicas/practica3-corregida/libtcp/libtcp.c b/practicas/practica3-corregida/libtcp/libtcp.c
new file mode 100644 (file)
index 0000000..9421dc6
--- /dev/null
@@ -0,0 +1,176 @@
+/* LibTcp - Released underOpen Software License v. 2.0
+ *
+ * This Open Software License (the "License") applies to any original work of authorship 
+ * (the "Original Work") whose owner (the "Licensor") has placed the following notice 
+ * immediately following the copyright notice for the Original Work:
+ *
+ *     Licensed under the Open Software License version 2.0
+ *
+ * See http://www.opensource.org/licenses/osl-2.0.php or the LICENSE file for more
+ * details.
+ */
+
+#include "libtcp.h"
+
+/** Abre un socket en modo activo para establecer una conexión con un servidor.
+ *
+ * Esta función es utilizada por los clientes para establecer una conexión TCP
+ * con un servidor. 
+ *
+ * \param servidor Nombre del host server
+ * \param port Número del puerto
+ * \return >0 El descriptor del socket, si se conecto al servidor.
+ * \return -2 Si no existe el nombre del servidor.
+ * \return -1 Si hubo error en la conexion y se debe consultar errno.
+ */
+int libtcp_open_activo (const char *server, int port)
+{
+       int sockfd; /* socket de la conexion */
+       struct sockaddr_in serv_addr;
+       struct hostent *ptr_server; /*puntero a dir del server(gethostbyname)*/
+
+       /* Borrar la estructura (poner en cero) */ 
+       bzero ((char *) &serv_addr, sizeof(serv_addr));
+       
+       /*      Inicializo familia de direcciones (protocolo IP) */
+       serv_addr.sin_family = AF_INET;
+
+       /* Cargar port en el socket: Convertir Host-TO-Network-Short integer */
+       serv_addr.sin_port = htons (port);
+
+       /* Cargo dirección del server en el socket. Convertir nombre del host en su direccion */
+       ptr_server = gethostbyname (server); 
+       if (ptr_server == NULL) {
+               /* No existe nombre de host. Posible falla en resolución de nombre */
+               return -2;
+       }
+       memcpy (&serv_addr.sin_addr, ptr_server->h_addr, ptr_server->h_length);
+
+       /* Abro como un socket de TCP (Internet stream socket) */
+       if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
+               /* Error en la creacion del socket */
+               return -1;
+       }
+
+       if (connect (sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
+               /* TODO : Deberia ser un codigo de error diferente, asi puedo diferenciarlos */
+               return -1;
+       }
+
+  return sockfd;
+}
+
+/** Abre un socket en modo pasivo usando protocolo TCP.
+ *
+ * La función se encarga de inicializar y crear el socket, y
+ * luego enlazarla con el SO.
+ * 
+ * \param port Puerto sobre el cual atiende este servidor
+ * \return >0 El socket, si la operacion fue exitosa
+ * \return <0 Si hubo un error (ver errno)
+ */
+int libtcp_open_pasivo (int port)
+{
+       char mostrar[80]; /* mensajes en la pantalla */ 
+       int     sockfd; /* socket que sirve como template */ 
+       struct sockaddr_in      serv_addr;
+
+       bzero ((char *)&serv_addr, sizeof (serv_addr));
+       serv_addr.sin_family = AF_INET; /* Familia protocolos TCP/IP */
+       serv_addr.sin_addr.s_addr = htonl (INADDR_ANY); /* Cualquier cliente */ 
+       serv_addr.sin_port = htons ((u_short)port); /* Port en formato red */
+
+       /* Crea un socket para TCP (un Internet stream socket) */
+       if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
+               return -1;
+       }
+
+       sprintf (mostrar, "LibTcp::ServerPasivo: socket creado %d\n", sockfd);
+       write (fileno(stdout), mostrar, strlen (mostrar));
+
+       /* Vincular el socket con la direccion local */
+       if (bind (sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
+               return -1;
+       }
+
+       sprintf (mostrar, "LibTcp::Server: se hizo el bind\n");
+       write(fileno(stdout), mostrar, strlen(mostrar));
+
+  /* Definir la cola de espera = hasta 5 clientes */
+       listen(sockfd, 5);
+
+       sprintf (mostrar, "LibTcp::Server: se hizo el listen con el socket %d\n", sockfd);
+       write(fileno(stdout), mostrar, strlen(mostrar));
+
+       return sockfd;
+}
+
+/** Lee de a un byte por vez a un buffer hasta encontrar un "\n".
+ * 
+ *  La cadena leída es terminada en "\0".
+ *
+ *  \return El tamaño, en caracteres, de la cadena leida.
+ *     \param fd       Descriptor del socket
+ *     \param ptr Puntero al buffer 
+ *     \param maxlong Tamaño del buffer (en bytes)
+ *     \TODO Soporte de caracteres multibyte
+ */
+int libtcp_receive (int fd, char *ptr, unsigned int maxlong)
+{
+       int     n, car;
+       char    c;
+
+       for (n = 0; n < maxlong; n++) {
+               if ((car = read (fd, &c, sizeof (char))) == 1) {
+                       *ptr++ = c;
+                       if (c == '\n')
+                               break;
+                       } else if (car == 0) {
+                               if (n == 1)
+                                       /* EOF, sin datos leidos */
+                                       return 0;
+                               else
+                                       break;          /* EOF, se leyo algo */
+               } else
+                       return -1;
+       }
+
+       *ptr = '\0';
+       return n;
+}
+
+/** Lee una informacion binaria */
+int libtcp_receive_bin (int fd, void *ptr, unsigned int maxlong)
+{
+       return read (fd, ptr, maxlong);
+}
+
+
+/** Escribe n bytes sobre un descriptor.
+ *
+ *  Si no se escribieron n bytes, trata de repetir hasta que se hayan escrito
+ *  los n bytes.
+ *
+ *  Se debe usar esta funcion cuando el descriptor en un stream socket.
+ *  
+ *     \param fd Descriptor del socket
+ *     \param ptr Puntero al mensaje
+ *     \param n cantidad de bytes
+ */
+int libtcp_send (int fd, const char *ptr, unsigned int n)
+{
+       int     nfaltan, nenviados;
+
+       nfaltan = n;
+       while (nfaltan > 0) {
+               nenviados = write (fd, ptr, nfaltan);
+               if (nenviados <= 0)
+                       return nenviados;
+
+               nfaltan -= nenviados;
+               ptr     += nenviados;
+       }
+
+       return n - nfaltan;
+}
+
diff --git a/practicas/practica3-corregida/libtcp/libtcp.h b/practicas/practica3-corregida/libtcp/libtcp.h
new file mode 100644 (file)
index 0000000..83754c0
--- /dev/null
@@ -0,0 +1,40 @@
+/* LibTcp - Released underOpen Software License v. 2.0
+ *
+ * This Open Software License (the "License") applies to any original work of authorship 
+ * (the "Original Work") whose owner (the "Licensor") has placed the following notice 
+ * immediately following the copyright notice for the Original Work:
+ *
+ *     Licensed under the Open Software License version 2.0
+ *
+ * See http://www.opensource.org/licenses/osl-2.0.php or the LICENSE file for more
+ * details.
+ */
+
+#ifndef _LIBTCP_COMMON_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define        LIBTCP_DEFAULT_UDP_PORT 5000
+#define        LIBTCP_DEFAULT_TCP_PORT 5000
+
+extern int errno;
+
+int libtcp_open_activo (const char *server, int port);
+int libtcp_open_pasivo (int port);
+int libtcp_receive (int fd, char *ptr, unsigned int maxlong);
+int libtcp_receive_bin (int fd, void *ptr, unsigned int maxlong);
+int libtcp_send (int fd, const char *ptr, unsigned int n);
+
+#endif // _LIBTCP_COMMON_H_
+
diff --git a/practicas/practica3-corregida/parte1/Makefile b/practicas/practica3-corregida/parte1/Makefile
new file mode 100644 (file)
index 0000000..3ca38ce
--- /dev/null
@@ -0,0 +1,27 @@
+
+CLIENTE=client
+SERVER_ITER=serverhandler
+SERVER_REAL=server
+LIBS=../libtcp/libtcp.a ../common/common.a
+CFLAGS=-g -Wall -I../libtcp -I../common
+CXXFLAGS=$(CFLAGS)
+TARGET=$(CLIENTE) $(SERVER_ITER) $(SERVER_REAL)
+CC=g++
+
+all: $(TARGET)
+
+$(CLIENTE): client.o $(LIBS)
+
+client.o: client.cpp
+
+$(SERVER_ITER): serverhandler.o $(LIBS)
+
+serverhandler.o: serverhandler.cpp
+
+$(SERVER_REAL): server.o $(LIBS)
+
+server.o: server.cpp
+
+.PHONY: clean
+clean:
+       rm -f *.o $(TARGET)
diff --git a/practicas/practica3-corregida/parte1/client.cpp b/practicas/practica3-corregida/parte1/client.cpp
new file mode 100644 (file)
index 0000000..aca17c3
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Cliente que envía operaciones al hash del servidor.
+ */
+
+#include "libtcp.h"
+#include "common.h"
+#include "protocol.h"
+#include <string>
+#include <sstream>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+    int sockfd;
+    int client_id;
+    char *server_name;
+    char *localhost = "localhost";
+    pid_t pid;
+    int port;
+    const char *pname = argv[0];
+
+    // mostrar el pid del cliente
+    pid = getpid();
+    print_msg(stdout, "%s Cliente con pid = %d\n", pname, pid);
+
+    // Verifico parametros de linea de comandos
+    if (argc > 2) 
+        port = atoi (argv[2]);
+    else
+        port = LIBTCP_DEFAULT_TCP_PORT;
+
+    if (port <= 0)
+    {
+        // valor incorrecto: aviso y terminar
+        print_msg(stdout, "%s (%d): Nro. de port invalido %d\n", pname, pid, port);
+        exit(1);
+    }
+
+    if (argc > 1)
+        server_name = argv[1];
+    else
+        server_name = localhost;
+
+    // id del cliente, por default calculado en base al pid
+    if (argc > 3)
+        client_id = atoi(argv[3]);
+    else
+        client_id = pid % 32;
+
+    // Abro el socket
+    sockfd = libtcp_open_activo(server_name, port); 
+    if (sockfd < 0)
+    {
+        // ERROR
+        if (sockfd == -2)
+        {
+            print_msg(stdout, "%s (%d): Nombre de server no existe %s\n", pname, pid, server_name);
+            exit(1);
+        }
+        else
+        {
+            perror("Error al llamar a libtcp_open_activo");
+            exit(1);
+        }
+    }
+
+    std::string line;
+    Protocol::Type type;
+    while (std::getline(std::cin, line))
+    {
+        std::istringstream iss(line);
+        std::string token;
+        if (!(iss >> token)) return 0;
+        if (token == "put")
+        {
+            type = Protocol::PUT;
+        }
+        else if (token == "find")
+        {
+            type = Protocol::FIND;
+        }
+        else if (token == "del")
+        {
+            type = Protocol::DEL;
+        }
+        else
+        {
+            print_msg(stderr, "%s (%d): Invalid token %s!\n", pname, pid, token.c_str());
+            exit(1);
+        }
+        while (iss >> token)
+        {
+            Protocol proto(type, 0, client_id, token.c_str());
+            print_msg(stdout, "%s (%d): escribiendo %s\n", pname, pid, token.c_str());
+            if (libtcp_send(sockfd, (char*) &proto, sizeof(Protocol)) != sizeof(Protocol))
+            {
+                print_msg(stderr, "%s (%d): error en envio sobre el socket\n", pname, pid);
+                exit(1);
+            }
+        }
+        Protocol proto(type, 1, client_id, NULL);
+        if (libtcp_send(sockfd, (char*) &proto, sizeof(Protocol)) != sizeof(Protocol))
+        {
+            print_msg(stderr, "%s (%d): error en envio sobre el socket\n", pname, pid);
+            exit(1);
+        }
+        // Esperando respuesta del servidor con el resultado
+        int response;
+        int n = libtcp_receive(sockfd, (char*) &response, sizeof(response));
+        if (n < 0)
+        {
+            print_msg(stderr, "%s (%d): error en recibir\n", pname, pid);
+        }
+        else
+        {
+            print_msg(stdout, "%s (%d): el server responde %d\n", pname, pid, response);
+        }
+    }
+
+    Protocol proto(Protocol::QUIT, 1, client_id, NULL);
+    if (libtcp_send(sockfd, (char*) &proto, sizeof(Protocol)) != sizeof(Protocol))
+    {
+        print_msg(stderr, "%s (%d): error en envio sobre el socket\n", pname, pid);
+        exit(1);
+    }
+
+    /* Limpio y salgo */
+    close(sockfd);    
+    return EXIT_SUCCESS;
+}
+
+// vim: set et sw=4 sts=4 :
diff --git a/practicas/practica3-corregida/parte1/gentest.py b/practicas/practica3-corregida/parte1/gentest.py
new file mode 100755 (executable)
index 0000000..ea3f89f
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# vim: set encoding=utf-8 :
+
+iteraciones = 10
+palabras = ('hola', 'chau', 'bueno', 'malo', 'cosa', 'prueba', 'socket')
+operaciones = ('put', 'find', 'del')
+
+from random import randint, shuffle
+from sys import argv
+
+try:
+       iteraciones = int(argv[1])
+except:
+       pass
+
+for i in xrange(iteraciones):
+       p = list(palabras)
+       shuffle(p)
+       p = p[0:randint(1, len(p)-1)]
+       print operaciones[randint(0, len(operaciones)-1)],
+       for pal in p:
+               print pal,
+       print
+
diff --git a/practicas/practica3-corregida/parte1/protocol.h b/practicas/practica3-corregida/parte1/protocol.h
new file mode 100644 (file)
index 0000000..d6948f3
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _PROTOCOL_H_
+#define _PROTOCOL_H_
+
+#include <cstring>
+
+#define PROTOCOL_MAXPAYLOAD 255
+
+struct Protocol
+{
+    enum Type { PUT, FIND, DEL, QUIT };
+    enum Result { OK, NOT_FOUND, EXISTS };
+
+    unsigned char type: 2;       // 2 bits para tipo
+    unsigned char end: 1;        // 1 bit para marca de FIN
+    unsigned char client_id: 5;  // 5 bits para id de cliente
+    char payload[PROTOCOL_MAXPAYLOAD];
+
+    Protocol() {}
+
+    Protocol(unsigned type, unsigned end, unsigned client_id, const char* p):
+        type(type), end(end), client_id(client_id)
+    {
+        if (p)
+            strncpy(payload, p, PROTOCOL_MAXPAYLOAD);
+        else
+            payload[0] = '\0';
+    }
+
+};
+
+#endif // _PROTOCOL_H_
+
+// vim: set et sw=4 sts=4 :
diff --git a/practicas/practica3-corregida/parte1/server.cpp b/practicas/practica3-corregida/parte1/server.cpp
new file mode 100644 (file)
index 0000000..b445c0e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Server concurrente usando protocolo TCP para manipular un set.
+ */
+
+#include "libtcp.h"
+#include "common.h"
+#include <csignal>
+
+void fin_hijos(int);
+
+int main (int argc, char *argv[])
+{
+    static char el_socket[15]; /* string que contiene el socket para el servidor de eco */
+
+    int sockfd;    /* socket que sirve como template */ 
+    int newsockfd; /* socket conectado al cliente */
+    int port;
+    unsigned int clilen; /* longitud dir. cliente */
+    unsigned int childpid; /* pid del hijo */
+    struct sockaddr_in cli_addr;
+
+    /* Verifico parametros de linea de comandos */
+    if (argc > 1)
+        port = atoi(argv[1]);
+    else
+        port = LIBTCP_DEFAULT_TCP_PORT;
+
+
+    /* Inicia Servidor - Open Pasivo */
+    if ((sockfd = libtcp_open_pasivo(port)) < 0)
+    {
+        perror("Server: no se puede abrir el stream socket");
+        exit(1);
+    }
+
+    print_msg(stdout, "server: se hizo el open pasivo, socket %d\n", sockfd);
+
+    signal(SIGCHLD, fin_hijos);
+
+    /* PROCESAMIENTO DEL SERVER */
+    while (1)
+    {
+        clilen = sizeof(cli_addr);
+        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
+        if (newsockfd < 0)
+        {
+            perror("server: error en el accept");
+            exit(1); 
+        }
+
+        if ((childpid = fork()) < 0)
+        { 
+            perror("server: error en el fork");
+            exit(1);
+        }
+        else if (childpid == 0)
+        {
+            /* PROCESO HIJO (child) que atiende al cliente */
+            close(sockfd); /* cerramos socket original */
+
+            print_msg(stdout, "server: socket armado con un cliente  %d\n", newsockfd);
+
+            /* pasarle el socket al hijo que atiende */
+            sprintf(el_socket, "%d\n", newsockfd);
+
+            /* Se lanza el proceso que atiende a ese cliente */
+            execlp("./serverhandler", "./serverhandler", el_socket, (char *)0);
+            perror("Server: error al lanzar el handler del servidor.");
+            exit(3);
+        }
+
+        /* PROCESO PADRE, se prepara para recibir otro cliente */
+        /* cerrar el socket pasado al hijo */
+        close(newsockfd);
+    }
+}
+
+/** Elimina Procesos-hijos que terminaron */
+void fin_hijos(int) 
+{
+    union wait status;
+    while (wait3((int *)&status, WNOHANG, (struct rusage *)0) >= 0);
+}
+
+// vim: set et sw=4 sts=4 :
diff --git a/practicas/practica3-corregida/parte1/serverhandler.cpp b/practicas/practica3-corregida/parte1/serverhandler.cpp
new file mode 100644 (file)
index 0000000..1568e7b
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Servidor de hash
+ */
+
+#include "libtcp.h"
+#include "common.h"
+#include "protocol.h"
+#include <set>
+#include <string>
+#include <cstdlib>
+
+int main(int argc, char *argv[])
+{
+    char *pname;
+    int sockfd;/* socket que sirve como template */ 
+    pid_t pid; /* pid del server iterativo */
+    char fin = 0, fin_cmd = 0;
+    int result = 0;
+    std::set< std::string > set;
+
+    pname = argv[0];
+    pid = getpid();
+
+    /* Verifico parametros de linea de comando */
+    if (argc > 1)
+        sockfd = atoi(argv[1]);
+    else
+    {
+        perror("Falta parametro con fd");
+        exit (1);
+    }
+
+    print_msg(stdout, "%s (%d): atendiendo a cliente por socket %d\n", pname, pid, sockfd);
+
+    while (!fin)
+    {
+        std::string buffer;
+        while (!fin_cmd)
+        {
+            Protocol proto;
+            int n = libtcp_receive_bin(sockfd, (char*) &proto, sizeof(Protocol));
+            if (n < 0)
+            {
+                print_msg(stdout, "%s (%d): error en recibir\n", pname, pid);
+                exit(1);
+            }
+            print_msg(stdout, "%s (%d): cliente %d envio operacion (%d, %d, %s)\n",
+                    pname, pid, proto.client_id, proto.type, proto.end, proto.payload);
+
+            buffer += proto.payload;
+            print_msg(stdout, "%s (%d): buffer: %s\n", pname, pid, buffer.c_str());
+
+            if (proto.end)
+            {
+                switch (proto.type)
+                {
+                    case Protocol::PUT:
+                        if (set.find(buffer) == set.end())
+                        {
+                            set.insert(buffer);
+                            result = Protocol::OK;
+                        }
+                        else
+                            result = Protocol::EXISTS;
+                        break;
+                    case Protocol::FIND:
+                        if (set.find(buffer) == set.end())
+                            result = Protocol::NOT_FOUND;
+                        else
+                            result = Protocol::OK;
+                        break;
+                    case Protocol::DEL:
+                        if (set.erase(buffer))
+                            result = Protocol::OK;
+                        else
+                            result = Protocol::NOT_FOUND;
+                        break;
+                    case Protocol::QUIT:
+                        result = Protocol::OK;
+                        print_msg(stdout, "%s (%d): Say no more\n", pname, pid);
+                        fin = 1;
+                        fin_cmd = 1;
+                        break;
+                    default:
+                        print_msg(stderr, "%s (%d): Operacion no soportada\n", pname, pid);
+                }
+                buffer.clear();
+                fin_cmd = 1;
+            }
+        }
+        fin_cmd = 0;
+
+        // Envío respuesta
+        libtcp_send(sockfd, (char*) &result, sizeof(int));
+        print_msg(stdout, "%s (%d) FIN (resultado = %d)\n", pname, pid, result);
+    }
+
+    close(sockfd);
+
+}
+
+// vim: set et sw=4 sts=4 :
diff --git a/practicas/practica3-corregida/parte1/test.sh b/practicas/practica3-corregida/parte1/test.sh
new file mode 100755 (executable)
index 0000000..37d1659
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+./gentest.py | ./client &
+./gentest.py | ./client &
+./gentest.py | ./client &
+./gentest.py | ./client &
diff --git a/practicas/practica3-corregida/parte2/Makefile b/practicas/practica3-corregida/parte2/Makefile
new file mode 100644 (file)
index 0000000..dcbce78
--- /dev/null
@@ -0,0 +1,48 @@
+
+# This is a template Makefile generated by rpcgen
+
+# Parameters
+
+CLIENT = set_client
+SERVER = set_server
+
+SOURCES_CLNT.c = 
+SOURCES_CLNT.h = 
+SOURCES_SVC.c = 
+SOURCES_SVC.h = 
+SOURCES.x = set.x
+
+TARGETS_SVC.c = set_svc.c set_xdr.c 
+TARGETS_CLNT.c = set_clnt.c set_xdr.c 
+TARGETS = set.h set_xdr.c set_clnt.c set_svc.c
+
+OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o) set_client.o
+OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o) set_server.o
+# Compiler flags 
+
+CFLAGS += -g -Wall -I../common
+LDLIBS += -lnsl ../common/common.a
+CC=g++
+RPCGENFLAGS = -a -N
+
+# Targets 
+
+all : $(CLIENT) $(SERVER)
+
+$(TARGETS) : $(SOURCES.x) 
+       rpcgen $(RPCGENFLAGS) $(SOURCES.x)
+       rm set_server.c set_client.c
+
+$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c) set_client.cpp
+
+$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c) set_server.cpp
+
+$(CLIENT) : $(OBJECTS_CLNT) 
+       $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS) 
+
+$(SERVER) : $(OBJECTS_SVC) 
+       $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)
+
+clean:
+        $(RM) *.o core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER) Makefile.set
+
diff --git a/practicas/practica3-corregida/parte2/gentest.py b/practicas/practica3-corregida/parte2/gentest.py
new file mode 100755 (executable)
index 0000000..ea3f89f
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# vim: set encoding=utf-8 :
+
+iteraciones = 10
+palabras = ('hola', 'chau', 'bueno', 'malo', 'cosa', 'prueba', 'socket')
+operaciones = ('put', 'find', 'del')
+
+from random import randint, shuffle
+from sys import argv
+
+try:
+       iteraciones = int(argv[1])
+except:
+       pass
+
+for i in xrange(iteraciones):
+       p = list(palabras)
+       shuffle(p)
+       p = p[0:randint(1, len(p)-1)]
+       print operaciones[randint(0, len(operaciones)-1)],
+       for pal in p:
+               print pal,
+       print
+
diff --git a/practicas/practica3-corregida/parte2/set.x b/practicas/practica3-corregida/parte2/set.x
new file mode 100644 (file)
index 0000000..c1ece18
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Programa para manipular un set con RPC
+ */
+program SETPRG {
+       version SETVERS {
+               /* importa solo el retorno del ultimo mensaje */
+               int PUT(string payload, int id, int end) = 1;
+               int FIND(string payload, int id, int end) = 2;
+               int DEL(string payload, int id, int end) = 3;
+               int QUIT(int id) = 4;
+       } = 1;
+} = 0x20000099;
diff --git a/practicas/practica3-corregida/parte2/set_client.cpp b/practicas/practica3-corregida/parte2/set_client.cpp
new file mode 100644 (file)
index 0000000..e12ab2d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * This is sample code generated by rpcgen.
+ * These are only templates and you can use them
+ * as a guideline for developing your own functions.
+ */
+
+#include "set.h"
+#include "../common/common.h"
+#include <string>
+#include <sstream>
+#include <iostream>
+
+enum { OK, NOT_FOUND, EXISTS };
+
+int
+main(int argc, char *argv[])
+{
+    int client_id;
+    char *server_name;
+    char *localhost = "localhost";
+    pid_t pid;
+    CLIENT* cl;
+    const char *pname = argv[0];
+
+    // mostrar el pid del cliente
+    pid = getpid();
+    print_msg(stdout, "%s Cliente con pid = %d\n", pname, pid);
+
+    if (argc > 1)
+        server_name = argv[1];
+    else
+        server_name = localhost;
+
+    // id del cliente, por default calculado en base al pid
+    if (argc > 2)
+        client_id = atoi(argv[2]);
+    else
+        client_id = pid % 32;
+
+
+    /* Crear el "handle" del cliente para llamar MESSAGEPROG en el 
+     * server. Usamos  "udp" para la  comunicacion. */
+    cl = clnt_create(server_name, SETPRG, SETVERS, "udp");
+    if (cl == NULL)
+    {
+        /* No se pudo hacer contacto error y adios. */
+        clnt_pcreateerror(server_name);
+        exit(1);
+    }
+
+    std::string line;
+    int* (*rem_proc)(char*, int, int, CLIENT*);
+    while (std::getline(std::cin, line))
+    {
+        std::istringstream iss(line);
+        std::string token;
+        if (!(iss >> token)) return 0;
+        if (token == "put")
+        {
+            rem_proc = put_1;
+            print_msg(stdout, "%s (%d): llamando a PUT:", pname, pid);
+        }
+        else if (token == "find")
+        {
+            rem_proc = find_1;
+            print_msg(stdout, "%s (%d): llamando a FIND:", pname, pid);
+        }
+        else if (token == "del")
+        {
+            rem_proc = del_1;
+            print_msg(stdout, "%s (%d): llamando a DEL:", pname, pid);
+        }
+        else
+        {
+            print_msg(stderr, "%s (%d): Invalid token %s!\n", pname, pid, token.c_str());
+            exit(1);
+        }
+        while (iss >> token)
+        {
+            /* llamar al procedure en el server */
+            int* r = rem_proc((char*)token.c_str(), client_id, 0, cl);
+            if (r == NULL)
+            {
+                /* error durante la comunicacion. */
+                clnt_perror(cl, server_name);
+                exit(1);
+            }
+            print_msg(stdout, " %s", token.c_str());
+        }
+        print_msg(stdout, "\n");
+        int* r = rem_proc("", client_id, 1, cl);
+        if (r == NULL)
+        {
+            /* error durante la comunicacion. */
+            clnt_perror(cl, server_name);
+            exit(1);
+        }
+        print_msg(stdout, "%s (%d): el server responde %d\n", pname, pid, *r);
+    }
+
+    int* r = quit_1(client_id, cl);
+    if (r == NULL)
+    {
+        /* error durante la comunicacion. */
+        clnt_perror(cl, server_name);
+        exit(1);
+    }
+
+    exit (0);
+}
+
+// vim: set et sw=4 sts=4 :
diff --git a/practicas/practica3-corregida/parte2/set_server.cpp b/practicas/practica3-corregida/parte2/set_server.cpp
new file mode 100644 (file)
index 0000000..6b38aae
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This is sample code generated by rpcgen.
+ * These are only templates and you can use them
+ * as a guideline for developing your own functions.
+ */
+
+#include "set.h"
+#include "../common/common.h"
+#include <set>
+#include <map>
+#include <string>
+#include <cstdlib>
+#include <cstdio>
+
+std::map< int, std::set< std::string > > sets;
+
+enum { OK, NOT_FOUND, EXISTS };
+
+static
+void
+get_file_name(int id, char* fname)
+{
+    sprintf(fname, "cliente_%d", id);
+}
+
+static
+FILE*
+open_file(int id, char* mode)
+{
+    char fname[32];
+    get_file_name(id, fname);
+    FILE* fp = fopen(fname, mode);
+    if (!fp)
+    {
+        perror("Error al abrir archivo");
+        exit(1);
+    }
+    return fp;
+}
+
+static
+void
+write_token(int id, const char* token)
+{
+    FILE* fp = open_file(id, "a");
+    if (fprintf(fp, "%s ", token) != (signed)strlen(token)+1)
+    {
+        perror("Error al escribir archivo");
+        exit(1);
+    }
+    fclose(fp);
+}
+
+int *
+put_1_svc(char *payload, int id, int end,  struct svc_req *rqstp)
+{
+    static int  result;
+
+    result = OK;
+    write_token(id, payload);
+    print_msg(stdout, "%d: recibido PUT '%s', end=%d\n", id, payload, end);
+    if (end)
+    {
+        FILE* fp = open_file(id, "r");
+        std::string buffer;
+        char token[256];
+        while (fscanf(fp, "%s", token) != EOF)
+            buffer += token;
+        fclose(fp);
+        print_msg(stdout, "%d: procesando PUT '%s'\n", id, buffer.c_str());
+        if (sets[id].find(buffer) == sets[id].end())
+            sets[id].insert(buffer);
+        else
+            result = EXISTS;
+        get_file_name(id, token);
+        remove(token); // Elimino archivo temporal
+    }
+
+    return &result;
+}
+
+int *
+find_1_svc(char *payload, int id, int end,  struct svc_req *rqstp)
+{
+    static int  result;
+
+    result = OK;
+    write_token(id, payload);
+    print_msg(stdout, "%d: recibido FIND '%s', end=%d\n", id, payload, end);
+    if (end)
+    {
+        FILE* fp = open_file(id, "r");
+        std::string buffer;
+        char token[256];
+        while (fscanf(fp, "%s", token) != EOF)
+            buffer += token;
+        fclose(fp);
+        print_msg(stdout, "%d: procesando FIND '%s'\n", id, buffer.c_str());
+        if (sets[id].find(buffer) == sets[id].end())
+            result = NOT_FOUND;
+        get_file_name(id, token);
+        remove(token); // Elimino archivo temporal
+    }
+
+    return &result;
+}
+
+int *
+del_1_svc(char *payload, int id, int end,  struct svc_req *rqstp)
+{
+    static int  result;
+
+    result = OK;
+    write_token(id, payload);
+    print_msg(stdout, "%d: recibido DEL '%s', end=%d\n", id, payload, end);
+    if (end)
+    {
+        FILE* fp = open_file(id, "r");
+        std::string buffer;
+        char token[256];
+        while (fscanf(fp, "%s", token) != EOF)
+            buffer += token;
+        fclose(fp);
+        print_msg(stdout, "%d: procesando DEL '%s'\n", id, buffer.c_str());
+        if (!sets[id].erase(buffer))
+            result = NOT_FOUND;
+        get_file_name(id, token);
+        remove(token); // Elimino archivo temporal
+    }
+
+    return &result;
+}
+
+int *
+quit_1_svc(int id,  struct svc_req *rqstp)
+{
+    static int  result;
+
+    result = OK;
+    print_msg(stdout, "%d: recibido QUIT, limpiando set\n", id);
+    if (!sets.erase(id))
+        result = NOT_FOUND;
+
+    return &result;
+}
+
+// vim: set et sw=4 sts=4 :
diff --git a/practicas/practica3-corregida/parte2/test.sh b/practicas/practica3-corregida/parte2/test.sh
new file mode 100755 (executable)
index 0000000..d671aea
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+./gentest.py | ./set_client &
+./gentest.py | ./set_client &
+./gentest.py | ./set_client &
+./gentest.py | ./set_client &