Unidad 4: Entrada-Salida

Introducción

Hasta es punto en el curso hemos estudiado dos abstracciones fundamentales que brinda el sistema operativo: PROCESOS y MEMORIA VIRTUAL. La primera permite utilizar los recursos de procesamiento y la segunda memoria temporal para los programas. Ahora, vamos a presentar un nuevo servicio del sistema operativo: ENTRADA-SALIDA. Los servicios de entrada-salida le permiten al sistemas operativo comunicarse con el mundo exterior, ya sea para adquirir información o para producirla. En particular, en esta unidad, vamos a abordar los servicios de persistencia y de comunicación de procesos utilizando sockets.

Propósito de aprendizaje

Comprender y aplicar los conceptos de entrada-salida para persistir información y para comunicar procesos que no necesariamente se encuentra en la misma máquina.

Lecturas y ejercicios

Sesión 1: persistencia

Ejercicio 1: persistencia

En este enlace se encuentra el material sobre los servicios de persistencia.

Trabajo autónomo sesión 1: reto persistencia

(Tiempo estimado 2 horas 50 minutos)

Realizar un programa que:

  • Reciba como argumento la ruta absoluta o relativa de un directorio.
  • El programa deberá imprimir el nombre de todos los archivos y subdirectorios que contenga el directorio y los subdirectorios.
  • La idea es recorrer toda la jerarquía imprimiendo los nombres de subdirectorios y archivos.

Sesión 2: introducción a los sockets

En esta sesión vamos a resolver dudas del RETO anterior y a introducir el mecanismo de sockets para comunicar procesos.

Ejercicio 2: sockets

El material que estudiaremos en este ejercicio es tomado de este texto.

En este enlace encontrarás un material teórico FUNDAMENTAL para entender los ejercicios que sigue.

Trabajo autónomo sesión 2: análisis

(Tiempo estimado: 2 horas 50 minutos)

Vas a analizar detenidamente el código que te presento en el ejercicio 3.

Ejercicio 3: análisis de un ejemplo

En este ejemplo verás como comunicar dos procesos utilizando sockets TCP.

Server.c:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <signal.h>

 #define PORT 6666
 #define BUF_SIZE 128

 struct client_t{
     int socket;
     int rxState;
 };

 void * readThread(void *arg){
     struct client_t *client = ((struct client_t *)arg);
     ssize_t numOfBytes;
     char buf[BUF_SIZE];

     while(1){
         numOfBytes = read(client->socket, buf, BUF_SIZE);
         if(0 == numOfBytes){
             printf("client closed the socket end\n");
             break;
         }
         else if(-1 == numOfBytes){
             perror("ReadThread read() fails: ");
             break;
         }
         else{
             printf("from client: %s\n",buf);
         }
     }
     printf("Terminate Pthread for reading\n");
     client->rxState = 0;
     return NULL;
 }

 int main(int argc, char *argv[]){

     char buf[BUF_SIZE];
     int status;
     int enable = 1;
     int server_sd;
     int client_sd;
     pthread_t rxThreadId;
     struct client_t client;

     // 1. Ignore SIGPIPE
     signal(SIGPIPE, SIG_IGN);

     // 2. Create socket
     server_sd = socket(AF_INET, SOCK_STREAM, 0);
     if (server_sd == -1) {
         perror("Socket creation fails\n");
         exit(EXIT_FAILURE);
     }
     printf("Socket created\n");

     // 3. turn off bind address checking
     status = setsockopt(server_sd, SOL_SOCKET, SO_REUSEADDR,(int *) &enable, sizeof(enable));
     if (-1 == status){
         perror("setsockopt error: ");
     }

     //4. BIND the socket to an address
     // Prepare the address
     struct sockaddr_in addr;
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_addr.s_addr = INADDR_ANY;
     addr.sin_port = htons(PORT);

     status = bind(server_sd, (struct sockaddr*)&addr, sizeof(addr));
     if (-1 == status) {
         perror("Bind fails: ");
         close(server_sd);
         exit(EXIT_FAILURE);
     }
     printf("Socket binded\n");

     // 5. Set backlog

     status = listen(server_sd, 1);

     if (-1 == status) {
         perror("Listen fails: ");
         close(server_sd);
         exit(EXIT_FAILURE);
     }

     printf("Server listening\n");

     while(1){
         // 6. Accept:
         printf("Waiting for a client\n");
         client_sd = accept(server_sd, NULL, NULL);

         printf("Client connected\n");
         if(-1 == client_sd){
             perror("Accept fails: ");
             close(server_sd);
             exit(EXIT_FAILURE);
         }
         // 7. Create a thread for receiving messages from client
         client.socket = client_sd;
         client.rxState = 1;

         printf("Create Pthread for reading\n");
         status = pthread_create(&rxThreadId,NULL,&readThread,&client);
         if(-1 == status){
             perror("Pthread read fails: ");
             close(server_sd);
             exit(EXIT_FAILURE);
         }


         while(1){
             if(0 == client.rxState){
                 printf("Client closed the socket\n");
                 break;
             }

             if ( fgets(buf,BUF_SIZE,stdin) == NULL){
                 printf("Fgets NULL\n");
             }

             if( buf[ strlen(buf)-1 ] == '\n') buf[ strlen(buf) - 1 ] = 0;

             status = write(client.socket, buf, strlen(buf)+1);
             if(-1 == status){
                 perror("Server write to client fails: ");
                 break;
             }
         }
         close(client.socket);
     }

     exit(EXIT_SUCCESS);
 }

Client.c:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <signal.h>
 #include <arpa/inet.h>

 #define PORT 6666
 #define BUF_SIZE 128

 struct client_t{
     int socket;
     int rxState;
 };

 void * readThread(void *arg){
     struct client_t *client = ((struct client_t *)arg);
     ssize_t numOfBytes;
     char buf[BUF_SIZE];

     while(1){
         numOfBytes = read(client->socket, buf, BUF_SIZE);
         if(0 == numOfBytes){
             printf("Server closed the socket end\n");
             break;
         }
         else if(-1 == numOfBytes){
             perror("ReadThread read() fails: ");
             break;
         }
         else{
             printf("from server: %s\n",buf);
         }
     }
     printf("Terminate Pthread for reading\n");
     client->rxState = 0;
     return NULL;
 }

 int main(int argc, char *argv[]){

     char buf[BUF_SIZE];
     int status;
     int server_sd;
     pthread_t rxThreadId;
     struct client_t client;

     // 1. Ignore SIGPIPE
     signal(SIGPIPE, SIG_IGN);

     // 2. Create socket
     server_sd = socket(AF_INET, SOCK_STREAM, 0);
     if (server_sd == -1) {
         perror("Socket creation fails\n");
         exit(EXIT_FAILURE);
     }
     printf("Socket created\n");

     //3. Connect to the server 127.0.0.1:PORT
     // Prepare the address
     struct sockaddr_in addr;
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_addr.s_addr = inet_addr("127.0.0.1");
     addr.sin_port = htons(PORT);

     status = connect(server_sd, (struct sockaddr*)&addr, sizeof(addr));
     if(-1 == status){
         perror("Connect fails\n");
         close(server_sd);
         exit(EXIT_FAILURE);
     }

     printf("Server connected\n");

     // 4. Create a thread for receiving messages from client
     client.socket = server_sd;
     client.rxState = 1;
     printf("Create Pthread for reading\n");

     status = pthread_create(&rxThreadId,NULL,&readThread,&client);
     if(-1 == status){
         perror("Pthread read fails: ");
         close(server_sd);
         exit(EXIT_FAILURE);
     }

     while(1){
         if(0 == client.rxState){
             printf("Server closed the socket\n");
             break;
         }

         if ( fgets(buf,BUF_SIZE,stdin) == NULL){
             printf("Fgets NULL\n");
         }
         if( 0 == strncmp(buf,":exit",strlen(":exit")) ){
             printf("Clinet exit\n");
             break;
         }

         if( buf[ strlen(buf)-1 ] == '\n') buf[ strlen(buf) - 1 ] = 0;

         status = write(client.socket, buf, strlen(buf)+1);
         if(-1 == status){
             perror("Server write to client fails: ");
             break;
         }
     }
     close(client.socket);
     exit(EXIT_SUCCESS);
 }

Sesión 3: análisis del ejercicio anterior

En esta sesión vamos a analizar juntos el ejercicio anterior.

Evaluación de la Unidad 4

Consideraciones

  • Conforma un equipo de mínimo de 2 personas y máximo de 3 personas.
  • Vas a dedicar 14 horas 40 minutos para solucionar el problema y elaborar una presentación.
  • Tendrás un espacio de 10 minutos para presentar, con tu equipo, en la última sesión de clase en la semana 16.
  • Antes de la presentación, sube a este enlace la información necesaria para entregar tu trabajo.
  • Para la comunicación entre los diferentes procesos ES OBLIGATORIO utilizar sockets TCP.
  • En la presentación realizarás la demostración y explicación de cada uno de los Criterios de evaluación señalados.

Problema

Vas a construir dos aplicaciones que llamaremos servidor y cliente. Solo tendrás una instancia del servidor, pero una cantidad ARBITRARIA de clientes. El servidor publicará EVENTOS. Los clientes le manifestarán de manera explícita al servidor su interés en algunos eventos específicos; sin embargo, en un momento dado, también podrán indicarle que ya no están interesados en algunos en particular. Por cada evento, el servidor mantendrá una lista de interesados que irá cambiando a medida que entran y salen interesados. Al generarse un evento en el servidor, este publicará a todos los interesados.

Para desplegar las aplicaciones, lanzarás el servidor y cada cliente en una terminal para cada uno. No olvides hacer pruebas con VARIOS clientes.

Estas son las características a implementar en el servidor:

El servidor :

  • Debe recibir commandos desde la línea de comandos y al mismo tiempo debe ser capaz de escuchar las peticiones de los clientes.
  • Cada petición de un cliente será visualizada con un mensaje en la terminal.
  • Los comandos que recibirá el servidor son:
    • exit: termina el servidor y deberá publicar este evento a TODOS los clientes.
    • add event_name: adiciona el evento event_name.
    • remove event_name: elimina el evento event_name.
    • trigger event_name: publica el evento event_name.
    • list event_name: lista todos los clientes suscritos a event_name.
    • all: visualiza todos los eventos y clientes de cada evento.
    • save file_name: salva en file_name todos los eventos.
    • load file_name: carga todos los eventos almacenados en file_name.
  • El servidor debe ser capaz de detectar si un cliente se desconecta y en consecuencia debe purgar los eventos a los cuales este estaba suscrito.

Estas son las características a implementar en el cliente:

  • El cliente debe visualizar en la terminal cada que sea notificado de un evento.
  • El cliente debe soportar los siguientes comandos:
    • sub event_name: se suscribe al evento event_name
    • unsub event_name: se desuscribe del evento event_name
    • list: lista todos los eventos a los cuales está suscrito.
    • ask: le pregunta al servidor cuáles eventos hay disponibles.
    • save file_name: salva en file_name todos los eventos.
    • load file_name: carga todos los eventos almacenados en file_name.

Criterios de evaluación

  1. Orden y coherencia en la presentación: 0.5
  2. Implementación del mecanismo de comunicación: 1
  3. Implementación de la concurrencia: 0.5
  4. Comando exit en el server: 0.1
  5. Comando add en el server: 0.1
  6. Comando remove en el server: 0.2
  7. Comando trigger en el server: 0.4
  8. Comando list en el server: 0.2
  9. Comando all en el server: 0.2
  10. Comando save en el server: 0.2
  11. Comando load en el server: 0.2
  12. Comando sub en el cliente: 0.3
  13. Comando unsub en el cliente: 0.3
  14. Comando list en el cliente: 0.2
  15. Comando ask en el cliente: 0.2
  16. Comando save en el cliente: 0.2
  17. Comando load en el cliente: 0.2