Si realizzi una infrastruttura software per lo scambio di messaggi tra processi client (d'ora in avanti semplicemente “client”) gestita da un processo server (d’ora in avanti semplicemente “server”). La comunicazione tra client avviene nel seguente modo: un client può inviare un messaggio testuale a un altro client o ad un gruppo di client affidandolo al server, che si occupa di recapitarlo.
Tutte le comunicazioni fra tutti i processi in gioco andranno realizzate utilizzando segnali e pipe con nome.
Il server svolge le seguenti funzioni:
• mantiene la lista dei client connessi.
• riceve messaggi testuali da inoltrare ai client.
Il client esegue le seguenti attività:
• si connette al server, si disconnette dal server.
• invia e riceve messaggi testuali.
Il funzionamento del software dovrà essere il seguente: viene mandato in esecuzione il server, che rimane in attesa di informazioni da parte dei client (connessioni, disconnessioni, messaggi testuali da inoltrare). Vengono poi avviati alcuni processi client (ogni client sarà avviato in terminali distinti). Quindi i client propongono un menu con le scelte possibili, che sono:
1. Connessione, con la quale il client si registra presso il server.
2. Richiesta elenco ID dei client registrati, con la quale si richiede al server l'elenco dei client attualmente registrati.
3. Invio di un messaggio testuale a un altro client o a un insieme di client (specificandone l'ID).
4. Disconnessione, con la quale il client richiede la cancellazione della registrazione presso il server.
5. Uscita dal programma.
Il server riconosce il tipo di richiesta del client ed esegue ciò che essa prevede. In particolare, in corrispondenza di una richiesta di connessione il server assocerà al client un ID univoco di registrazione, che dovrà poi essere rimosso nel caso in cui lo stesso client richieda esplicitamente la disconnessione dal server (tramite la scelta 4) o nel caso in cui il client venga terminato (utilizzando la scelta 5 oppure premendo Ctrl-C da terminale).
Le infrastrutture da utilizzare per lo scambio di informazioni fra i processi sono le seguenti:
• un (unico) pipe con nome, creato dal server quando viene mandato in esecuzione, che viene utilizzato (in maniera condivisa) dai client in scrittura (per comunicare le proprie richieste al server) e dal server in lettura; il server è l’unico processo a cui è permessa la lettura dal pipe, i client potranno solo scrivere su tale pipe.
• un pipe con nome (uno per ogni client), creato appositamente dal server quando necessario, e utilizzato dal server in scrittura e dal client in lettura per inviare l'elenco degli ID dei client attualmente registrati e per inviare messaggi testuali (il server usa il pipe associato al destinatario per recapitarlo).
• una serie di segnali, inviati dal server ai vari client, per notificare eventi quali la ricezione di un messaggio per un client (notifica al client destinatario) o l'errore per aver richiesto l'inoltro di un messaggio verso un client inesistente (notifica al client mittente).
Si gestiscano anche le situazioni di errore che si possono verificare più frequentemente.
DEVO RIUSCIRE A FARE COMUNICARE I CLIENT..
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
int fd, lung_server, result;
char buff[255];
int port = 12121;
struct sockaddr_in mysocket;
struct sockaddr* serverSockAddrPtr;
serverSockAddrPtr = (struct sockaddr*) &mysocket;
if(argc<2) /*testa se è stato inserito un comando*/
{
printf("Inserisci un comando da inviare\n");
return 1;
}
lung_server=sizeof (mysocket);
fd=socket(AF_INET, SOCK_STREAM, 0); /*crea la socket*/
mysocket.sin_family=AF_INET;
mysocket.sin_port=htons(port);
mysocket.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
int prove=0, risultato=0;
risultato=connect(fd, serverSockAddrPtr, lung_server); /*tenta la connessione max 5 volte, poi
termina*/
while(risultato==-1 && prove<5)
{
printf("Server irraggiungibile, riprovo...\n");
prove++;
sleep (1);
risultato=connect(fd, serverSockAddrPtr, lung_server);
}
if(risultato==-1)
{
printf("ERRORE. Server irraggiungibile\n");
exit(0);
}
else
printf("Connesso\n\n");
sprintf(buff, "%d", argc-1); /*invia al server la quantità di parole che compongono il comando*/
write(fd, buff, strlen(buff)+1);
int i;
for(i=1; i<argc; i++) /*invia al server il comando*/
write(fd, argv, strlen(argv)+1);
int letti;
char buff_rcv[512];
while(1) /*legge i caratteri ricevuti sul descrittore di file dal server e stampa sullo std_output*/
{
letti=read(fd, buff_rcv, 512);
if(letti==0)
{
printf("\n\nConnessione terminata\n\n");
close(fd);
return 0;
}
printf("%s",buff_rcv);
}
}
E QUESTO E' IL SERVER
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int leggi_linea(int fd, char *str) /*legge i caratteri dal descrittore di file, ovvero quelli che
arrivano dai client*/
{
int letti;
letti=read(fd, str, 1);
while(letti>0 && *str++!='\0')
letti=read(fd, str, 1);
return letti;
}
int esegui(int clientFd, int argc) /*esegue il comando ricevuto dal client e reindirizza l'output*/
{
int fd[2], letti=0, i=0;
char **argv, buff[512];
argv=malloc((argc+1)*sizeof(char*));
for(i=0; i<argc+1; i++) /*alloca memoria per contenere il comando e i parametri*/
argv = malloc(256*sizeof(char));
for(i=0; i<argc; i++) /*comando e parametri ricevuti dal client*/
leggi_linea(clientFd, argv);
argv[argc]=NULL;
pipe(fd); /*crea una pipe*/
if(fork()==0) /*figlio, esegue il comando redirigendo lo standard output*/
{
close(fd[0]);
dup2(fd[1], 1);
dup2(fd[1],2);
close(clientFd);
execvp(argv[0], &argv[0]);
perror("Questo è un errore che non si deve mai presentare\n");
exit(0);
}
else /*padre, legge output ricevuto dalla pipe e lo ritrasmette al client tramite il descrittore di
file*/
{
close(fd[1]);
while(1)
{
letti=read(fd[0], buff, 512);
if(letti==0)
{
close(fd[0]);
close(clientFd);
return 1;
}
write(clientFd, buff, letti);
}
}
exit(0);
}
int main (void)
{
int mySocket, clientFd, lung_server, lung_client;
int port = 12121;
struct sockaddr_in serverINETAddress; /*crea la socket, imposta le connessioni max (5) e
rimane in ascolto per le richieste*/
struct sockaddr_in clientINETAddress;
struct sockaddr* serverSockAddrPtr;
struct sockaddr* clientSockAddrPtr;
signal(SIGCHLD, SIG_IGN);
serverSockAddrPtr=(struct sockaddr*) &serverINETAddress;
lung_server=sizeof(serverINETAddress);
clientSockAddrPtr=(struct sockaddr*) &clientINETAddress;
lung_client=sizeof(clientINETAddress);
mySocket=socket(AF_INET, SOCK_STREAM, 0);
serverINETAddress.sin_family=AF_INET;
serverINETAddress.sin_port=htons(port);
serverINETAddress.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
bind(mySocket, serverSockAddrPtr, lung_server);
listen(mySocket, 5);
printf("Server ONLINE, in attesa di connessioni...\n");
while(1)
{
clientFd=accept(mySocket, clientSockAddrPtr, &lung_client);
if(clientFd)
printf("Connessione accettata\n");
if (fork()==0) /*figlio, si occupa di gestire una richiesta ricevuta*/
{
int argc;
char buff2[256];
leggi_linea(clientFd, buff2);
argc=atoi(buff2);
esegui(clientFd, argc);
exit(0);
}
else /*padre, chiude da parte sua il descrittore di file*/
close (clientFd);
}
}