--- /dev/null
+
+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
+
--- /dev/null
+===============================
+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 :
--- /dev/null
+
+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)
--- /dev/null
+/* 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));
+}
--- /dev/null
+/* 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_
+
--- /dev/null
+# 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)
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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_
+
--- /dev/null
+
+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)
--- /dev/null
+/*
+ * 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 :
--- /dev/null
+#!/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
+
--- /dev/null
+#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 :
--- /dev/null
+/*
+ * 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 :
--- /dev/null
+/*
+ * 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 :
--- /dev/null
+#!/bin/sh
+
+./gentest.py | ./client &
+./gentest.py | ./client &
+./gentest.py | ./client &
+./gentest.py | ./client &
--- /dev/null
+
+# 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
+
--- /dev/null
+#!/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
+
--- /dev/null
+/*
+ * 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;
--- /dev/null
+/*
+ * 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 :
--- /dev/null
+/*
+ * 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 :
--- /dev/null
+#!/bin/sh
+
+./gentest.py | ./set_client &
+./gentest.py | ./set_client &
+./gentest.py | ./set_client &
+./gentest.py | ./set_client &