informatica:sol:laboratorio18:esercitazionib:fifoexample

Esempio di programma client/server che utilizza pipe con nome (FIFO)

Client e server comunicano con una pipe FIFO. Il server riceve i comandi da eseguire su una FIFO “pubblica” e restituisce le risposte su una FIFO privata del client il cui nome è stato inviato insieme al messaggio di richiesta. Il processo server “forka” un processo figlio per eseguire il comando richiesto dal client redirigendo lo standard output per ottenere il risultato.

msg.h

#if !defined(MSG_H)
#define MSG_H
#include <linux/limits.h>  /* Linux specific definisce PIPE_BUF */

#define F_SIZE  256               /* size massima del nome della FIFO del client */ 
#define B_SIZE  (PIPE_BUF-F_SIZE) /* size massima dei dati scambiati in modo atomico */

#define SYSCALL(r,c,e) \
    if((r=c)==-1) { perror(e);exit(errno); }

// FIFO pubblica per inviare le richieste al server
const char *PUBLIC = "/tmp/PUBLIC";

/* 
 * In un singolo messaggio vogliamo inviare sia il comando da eseguire 
 * che il nome della FIFO su cui vogliamo ottenere il risultato.
 */
struct message {
    char fifo_name[F_SIZE];
    char  cmd_line[B_SIZE];
};
#endif /* MSG_H */

client.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "msg.h"

// nome della FIFO privata
char fifo_name[F_SIZE];
void cleanup() {
    int n;
    SYSCALL(n, unlink(fifo_name), "cleanup: unlink private fifo");
}
int main() {
    static char buffer[PIPE_BUF];
    struct message msg;
    int publicfifo, privatefifo, n;

    // fifo privata per ricevere le risposte dal server 
    snprintf(fifo_name, F_SIZE, "/tmp/fifo%d", getpid());
    SYSCALL(n, mkfifo(fifo_name, 0666), "mkfifo");

    // qualora qualcosa vada storto
    if (atexit(cleanup) != 0) {
	fprintf(stderr, "ERRORE in atexit\n");
	exit(EXIT_FAILURE);
    }

    // apro la FIFO pubblica in sola scrittura
    SYSCALL(publicfifo, open(PUBLIC,O_WRONLY), "open public fifo");
    
    while(1) {
	SYSCALL(n,write(1, "\n cmd>", 6), "write 1");

	// resetto il messaggio
	memset(msg.fifo_name, 0x0, F_SIZE);
	memset(msg.cmd_line, 0x0, B_SIZE);
	// leggo il comando da inviare al server
	SYSCALL(n,read(0, msg.cmd_line, B_SIZE), "read 0");
	
	// devo uscire ?
	if(strncmp("quit", msg.cmd_line, n-1) == 0)  break;

	strncpy(msg.fifo_name,fifo_name, strlen(fifo_name)+1);

	// mando la richiesta al server
	SYSCALL(n, write(publicfifo, &msg, sizeof(msg)), "write public fifo");

	// apro la FIFO privata in sola lettura aspettando che il server la apra in scrittura
	SYSCALL(privatefifo, open(msg.fifo_name, O_RDONLY), "open private fifo");
	
	do {
	    memset(buffer, 0x0, PIPE_BUF);
	    SYSCALL(n ,read(privatefifo, buffer, PIPE_BUF), "read private fifo");
	    fprintf(stdout, "%s", buffer);
	} while(n>0);
	SYSCALL(n, close(privatefifo), "close private fifo");
    }
    SYSCALL(n, close(publicfifo), "close public fifo");    
    return 0;
}

server.c

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "msg.h"

#define MAXARGS 100   // massimo numero di argomenti di un comando

// costruisce un vettore di argomenti da una stringa
void buildargs(char *line, char * args[]) {
  int i = 0;
  args[i++] = strtok(line," \n");
  do {
    args[i] = strtok(NULL," \n");
  } while(args[i++]!=NULL);
}

int main() {    
    struct message msg;
    static char buffer[PIPE_BUF];
    
    /* creo la FIFO pubblica */
    if ((mkfifo(PUBLIC, 0666) == -1) && errno!=EEXIST) {
	perror("mkfifo public fifo");
	exit(errno);
    }
    
    int publicfifo;
    
    // apertura in sola lettura per ricevere i comandi dai clients, aspetto che
    // almeno uno dei client apra la FIFO in scrittura
    SYSCALL(publicfifo, open(PUBLIC, O_RDONLY), "open public fifo (read)");
    // apro la FIFO in scrittura per evitare di ricevere EOF sulla FIFO pubblica
    // che rimane sempre aperta
    int notused;
    SYSCALL(notused, open(PUBLIC, O_WRONLY|O_NONBLOCK), "open public fifo (write)");
    
    while(1) {
	int n, done;
	SYSCALL(n, read(publicfifo, &msg, sizeof(msg)), "read public fifo");
	if (n == 0) break; // ho letto End-Of-File (EOF), esco

	n=0, done=0;
	do {
	    int privatefifo;
	    // attendo che il client si connetta riprovando un po' di volte
	    if ((privatefifo = open(msg.fifo_name, O_WRONLY|O_NONBLOCK)) == -1) {
		sleep(2);
	    } else {
		int channel[2];
		int r;
		SYSCALL(r, pipe(channel), "pipe"); // creo una pipe senza nome
		
		char *args[MAXARGS];
		buildargs(msg.cmd_line, args);

		if (fork() == 0) { // figlio
		    SYSCALL(r, close(channel[0]), "close reader");
		    SYSCALL(r, dup2(channel[1],1), "dup2 writer");
		    SYSCALL(r, close(channel[1]), "close writer");

		    execvp(args[0], &args[0]);
		    fprintf(stderr, "execvp fallita");
		    exit(EXIT_FAILURE);
		} // padre

		SYSCALL(r, close(channel[1]), "close writer");
		SYSCALL(r, write(privatefifo,"\n",1), "write private fifo");
		do {
		    SYSCALL(n, read(channel[0], buffer, PIPE_BUF), "read channel[0]");
		    SYSCALL(r, write(privatefifo,buffer,n), "write private fifo");
		    memset(buffer, 0x0, PIPE_BUF);
		} while(n>0);
		
		SYSCALL(r, close(channel[0]), "close reader");
		SYSCALL(r, close(privatefifo), "close private fifo"); // invio EOF
		done = 1;
	    }
	}while(!done && n++ < 10);
	if (!done) {
	    fprintf(stderr, "Il client non ha inviato il comando, condizione di fallimento\n");
	    exit(EXIT_FAILURE);
	}	
    }
    return 0;
}
informatica/sol/laboratorio18/esercitazionib/fifoexample.txt · Ultima modifica: 17/04/2018 alle 08:25 (6 anni fa) da Massimo Torquati