11.1 El servidor

Nuestro servidor es una aplicación escrita en C que mantiene una variable que puede ser modificada por los clientes. Cada cliente es servido en un hilo diferente que perdura tanto tiempo como la conexión con el cliente. Como puede haber más de un cliente conectados de forma simultanea, se trata, por tanto, de un servidor concurrente.

El servidor imprimirá por la consola el valor actual de una variable compartida. Este proceso se ejecutará hasta que pulsemos <CTRL> + <C> en la consola donde hemos ejecutado el servidor.

A continuación se muestra el código fuente del servidor ( http://www.ace.ual.es/\~{}vruiz/docencia/redes/practicas/progs/servidor.c):

/*  
 * servidor.c  
 *  
 * Acepta conexiones TCP a través del puerto PORT para que distintos  
 * clientes puedan modificar una variable compartida  
 * (shared_value). Las acciones que pueden realizar los clientes  
 * depende del valor especificado en cada interacción. Si el valor  
 * introducido es <= -2, el cliente es desconectado. Si el valor  
 * introducido es -1, el cliente es informado del valor actual de la  
 * variable compartida. En cualquier otro caso, el valor introducido  
 * es tomado por la variable compartida.  
 *  
 * Para detener el servidor, pulsar <CTRL>+<C> en la consola donde se  
 * lanzó el servidor.  
 *  
 * Compilar escribiendo:  
 *  
 * gcc servidor.c -o servidor -lpthread  
 *  
 * gse. 2007.  
 *  
 */  
 
/******************************************************************************  
 *  
 * Ficheros cabecera.  
 *  
 *****************************************************************************/  
 
/* Entrada y salida de streams buffereados. */  
#include <stdio.h>  
 
/* Biblioteca de funciones estándar (exit(), EXIT_SUCCESS,  
   EXIT_FAILURE, ...) */  
#include <stdlib.h>  
 
/* Manipulación de cadenas y movimiento de memoria (memset(), ...). */  
#include <string.h>  
 
/* Biblioteca estándar de funciones relacionadas con el sistema  
   operativo Unix (read(), write(), ...). */  
#include <unistd.h>  
 
/* POSIX Threads (pthread_t, pthread_create(), ...). */  
#include <pthread.h>  
 
/* Sockets. */  
/* Más info en:  
 *  
 * http://www.csce.uark.edu/~aapon/courses/os/examples/concurrentserver.c  
 * http://beej.us/guide/bgnet/  
 */  
 
/* Tipos de datos primitivos del sistema. */  
#include <sys/types.h>  
 
/* Sockets (socket(), listen(), ...). */  
#include <sys/socket.h>  
 
/* Direcciones IP, opciones y definiciones. */  
#include <netinet/in.h>  
 
/* Servicio de resolución de nombres. */  
#include <netdb.h>  
 
/* Signals (interrupciones) (signal(), SIGINT). */  
#include <signal.h>  /* signal(), SIGINT */  
 
/******************************************************************************  
 *  
 * Definiciones.  
 *  
 *****************************************************************************/  
 
/* Puerto de escucha del servidor. */  
#define PORT 6789  
 
/* Número máximo de clientes que esperan a que la conexión sea  
   establecida. */  
#define QLEN 1  
 
/******************************************************************************  
 *  
 * Variable globales.  
 *  
 *****************************************************************************/  
 
/******************************************************************************  
 *  
 * Funciones.  
 *  
 *****************************************************************************/  
 
/* Cuerpo principal del programa. */  
int main(int argc, char *argv[]) {  
  work(argc, argv);  
  return EXIT_SUCCESS;  
}  
 
/* Función que realiza todo el trabajo. */  
work(int argc, char *argv[]) {  
 
  /* Un semáforo para acceder "en exclusión mutua" a una zona de  
     código. */  
  pthread_mutex_t mut;  
 
  /* Creamos el semáforo. */  
  pthread_mutex_init(&mut, NULL);  
 
  /* La variable compartida que puede ser controlada por los clientes. */  
  int shared_value = 0;  
 
  /* Creamos el socket TCP de escucha. */  
  int listen_sd; {  
 
    /* Obtenemos el número del protocolo. */  
    struct protoent *ptrp; /* Pointer to a protocol table entry. */  
    ptrp = getprotobyname("tcp");  
    if((int)(ptrp) == 0) {  
      perror("getprotobyname");  
      exit(EXIT_FAILURE);  
    }  
 
    /* Creamos el socket. */  
    listen_sd = socket (PF_INET, SOCK_STREAM, ptrp->p_proto);  
    if(listen_sd < 0) {  
      perror("socket");  
      exit(EXIT_FAILURE);  
    }  
 
    /* Usaremos el puerto de servicio sin esperar a que éste esté  
       libre. */  
    int yes = 1;  
    if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {  
      perror("setsockopt");  
      exit(EXIT_FAILURE);  
    }  
  }  
 
  /* Asignamos una dirección (dir IP, puerto) al socket de escucha. */ {  
    struct sockaddr_in sad;              /* Dirección del servidor. */  
    memset((char  *)&sad,0,sizeof(sad)); /* Borramos la estructura. */  
    sad.sin_family = AF_INET;            /* Usaremos Internet. */  
    sad.sin_addr.s_addr = INADDR_ANY;    /* Cualquiera de las IP  
    asignadas al host vale. */  
    sad.sin_port = htons((u_short)PORT); /* Asignamos el puerto de  
    escucha. */  
    if (bind(listen_sd, (struct sockaddr *)&sad, sizeof (sad)) < 0) {  
      perror("bind");  
      exit(EXIT_FAILURE);  
    }  
  }  
 
  /* Comenzamos a escuchar. */  
  if (listen(listen_sd, QLEN) < 0) {  
    perror("listen");  
    exit(EXIT_FAILURE);  
  }  
 
  fprintf(stderr,"%s: esperando conexiones ...\n", argv[0]);  
 
  /* Si pulsamos CRTL+C, el programa acaba ejecutando la función  
     end(). */  
  void end() {  
    fprintf(stderr,"%s: <CTRL>+<C> detectado. Saliendo ...\n",argv[0]);  
 
    /* Cerramos el socket de escucha. */  
    close(listen_sd);  
 
    /* Salimos. */  
    exit(EXIT_SUCCESS);  
  }  
  signal(SIGINT, end);  
 
  /* Lazo del servidor. */  
  while(1) {  
 
    /* Socket para "servir" a los clientes. */  
    int serve_sd;  
 
    /* Esperamos a que un cliente se conecte. */ {  
      struct sockaddr_in cad;        /* Client’s address. */  
      socklen_t alen = sizeof(cad);  /* Tamaño de la dirección. */  
      serve_sd = accept(listen_sd, (struct sockaddr *)&cad, &alen);  
      if(serve_sd<0) {  
perror("accept");  
exit (EXIT_FAILURE);  
      }  
    }  
 
    fprintf(stderr,"%s: conexión aceptada!\n",argv[0]);  
 
    /* Hilo que controla a un cliente. */  
    void *service(void *arg) {  
 
      /* El socket de conmunicación con el cliente. Nótese que cada  
 cliente posee una variable "sd" diferente. */  
      int sd = (int)arg;  
 
      for(;;) {  
int new_shared_value;  
 
inform_to_client(sd, shared_value);  
receive_from_client(sd, &new_shared_value);  
 
/* Looping mientras new_shared_value > -2. */  
if(new_shared_value < -1) break;  
 
/* Modificamos shared_value mientras new_shared_value > -1. */  
if(new_shared_value > -1) {  
  /* Sección crítica. */  
  pthread_mutex_lock(&mut);  
  shared_value = new_shared_value;  
  fprintf(stderr,"%s: shared_value = %d\n", argv[0], shared_value);  
  pthread_mutex_unlock(&mut);  
  /* Fin de la sección crítica. */  
}  
 
      }  
 
      /* El cliente ha introducido -2. */  
 
      /* Cerramos la conexión con el cliente. */  
      close(sd);  
 
      /* Finalizamos el hilo. */  
      pthread_exit(0);  
    }  
 
    /* Lanzamos el hilo. */  
    pthread_t service_tid; /* Thread identificator. */  
    if(pthread_create(&service_tid, NULL, service, (void *)serve_sd) == -1) {  
      perror("pthread_create");  
      exit(EXIT_FAILURE);  
    }  
  }  
 
}  
 
/* Informa a un cliente sobre el valor de shared_value. */  
inform_to_client(int sd, int shared_value) {  
  char message[80];  
  memset(message, 80, 0);  
  sprintf(message,"shared_value = %d. Introduzca nuevo valor ... ", shared_value);  
  send(sd, message, strlen(message), 0);  
}  
 
/* Recibe del cliente un nuevo valor. */  
receive_from_client(int sd, int *shared_value) {  
  char message[80];  
  read_text_line(sd, message);  
  sscanf(message,"%d",shared_value);  
}  
 
/* Lee una línea de texto ASCII. */  
read_text_line(int sd, char *str) {  
  int n;  
  do {  
    n = recv(sd, str, 1, 0);  
  } while (n>0 && *str++ != ’\n’);  
}

__________________________________________________________________

Taller 11.1: Compile el servidor escribiendo:

gcc servidor.c -o servidor -lpthread

____________________________________________________________

Taller 11.2: Ejecute el servidor escribiendo:

./servidor

____________________________________________________________