Applicazione distribuita
Transcript
Applicazione distribuita
Politecnico di Milano La programmazione di applicazioni distribuite in C • Il concetto di applicazione distribuita – L’architettura di una applicazione distribuita – Il paradigma a scambio di messaggi • Il paradigma client-server • Il paradigma peer-to-peer • Lo sviluppo di applicazioni distribuite in C – I socket TCP/ IP in C – Un esempio di uso dei socket TCP/ IP in C Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 1 Applicazione distribuita • Definizione: – Applicazione costituita da due o più processi che eseguono in parallelo su macchine distinte connesse da una rete di comunicazione • I processi che costituiscono una applicazione distribuita cooperano sfruttando i servizi forniti dalla rete di comunicazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 2 1 Politecnico di Milano Architettura di una applicazione distribuita • Una applicazione distribuita è caratterizzata dalla propria architettura run-time • Definizione: – Con il termine “architettura run-time” si indica l’organizzazione dei componenti (processi) che costituiscono l’applicazione a run-time • Elementi caratteristici di una architettura – Tipologia e ruolo dei componenti – Tipologia delle connessioni Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 3 Le architetture basate su scambio messaggi • Lo scambio messaggi è il paradigma di comunicazione più semplice per lo sviluppo di applicazioni distribuite – Identifica una categoria di architetture distinte • Caratteristiche: – Ogni componente dell’applicazione possiede uno o più indirizzi – Un componente A comunica con un componente B spedendo un messaggio ad uno degli indirizzi associati a B • Principali architetture basate su scambio messaggi: – Architetture client-server – Architetture peer-to-peer – Architetture a tre livelli (three-tiered architecture) Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 4 2 Politecnico di Milano L’architettura client-server • Caratteristiche: – Componenti distinti in due tipi: client e server • I server erogano un servizio • I client sfruttano tale servizio – Comunicazioni basate su scambio messaggi Browser • Esempio: il web – Client: il browser – Server: il demone http che fornisce i documenti ai client Richiesta documento Invio documento Demone http Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 5 L’architettura peer-to-peer • Caratteristiche – Componenti indifferenziati: agiscono tanto da richiedenti di un servizio quanto da fornitori di servizio – Comunicazioni basate su scambio messaggi • Esempio: talk Blah blah blah Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Blah blah blah 6 3 Politecnico di Milano L’architettura a tre livelli • Caratteristiche: – Tre tipologie di componenti: • Client • Server applicativo • Data base – Comunicazioni basate su scambio messaggi • Esempio: applicazioni di commercio elettronico Client Server applicativo (browser + ActiveX o Applet) (demone http+cgi o servlet) DB Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 7 Socket TCP e UDP • Con il termine “socket” si indica un’astrazione del sistema operativo per modellare la comunicazione tramite TCP o UDP • N ati in ambiente Unix BSD (1982) • Disponibili su tutte le piattaforme • Le librerie per la gestione dei socket sono fornite con tutti i principali linguaggi di programmazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 8 4 Politecnico di Milano Tipi di socket • Socket UN IX: permettono la comunicazione tra due processi in esecuzione sullo stesso host (tipo AF_UNIX) – Meccanismo di indirizzamento basato su pathname unix • Socket Internet o TCP/ IP: permettono la comunicazione tra processi in esecuzione su macchine diverse (tipo AF_INET) • Socket connection-oriented: modellano le connessioni con protocolli connection-oriented – Esempio: socket Stream • Socket connectionless: modellano le connessioni connectionless – -Esempio: socket Datagram – Meccanismo di indirizzamento basato su coppie: < indirizzo IP, porta> Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Stream Socket (TCP): meccanismo di funzionamento P1 Server P2 Politecnico di Milano 9 Client P1 Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 10 5 Datagram Socket (UDP): meccanismo di funzionamento P1 Server P2 Politecnico di Milano Client Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 11 Socket connection-oriented socket() bind() listen() socket() accept() connect() si sospende read() write() write() read() close() close() Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 12 6 Politecnico di Milano Socket connectionless socket() bind() socket() rcvfrom() bind() si sospende sendto() sendto() recvfrom() close() close() Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 13 Indirizzi di socket • Un socket ha un indirizzo specificato dalla struttura struct sockaddr { u_short sa_family; /* tipo del socket */ char sa_data[14]; /* indirizzo vero e proprio */ } • Per i socket Internet la struttura diventa: struct sockaddr_in { u_short sin_family; /* tipo del socket = AF_INET */ u_short sin_port; /* numero di porta */ struct in_addr sin_addr; /* struttura di 4 byte che contiene l’indirizzo IP dello host */ char sin_zero[8]; /* byte non usati (padding) */ } – sin_port e sin_addr devono essere in “network byte order” • Per i socket Unix la struttura diventa: struct sockaddr_un { u_short sun_family; /* tipo del socket = AF_UNIX */ char *sun_path; /* pathname usato come indirizzo del socket */ } Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 14 7 Politecnico di Milano API socket - socket • Essendo chiamate alla parte di gestione della rete del sistema operativo sono solitamente implementate come system call • #include <sys/types.h> #include <sys/socket.h> int socket(int family, int type, int protocol) – family specifica il tipo di socket (AF_UN IX, AF_IN ET) – type specifica il tipo di canale: SOCK_STREAM per un canale connection-oriented, SOCK_DGRAM per un canale connectionless – protocol specifica il tipo di protocollo usato: IPPROTO_UDP per indicare UDP (tipo DGRAM) o IPPROTO_TCP per TCP (tipo STREAM) • La primitiva ritorna un intero che rappresenta un descrittore di file socket Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 15 API socket - bind e listen • • • • • • #include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *myaddr, int addrlen) – sockfd specifica un descrittore di socket – myaddr è un puntatore ad un indirizzo di socket – addrlen specifica la lunghezza della struttura myaddr La primitiva associa al descrittore di socket sockfd un indirizzo bind ritorna un valore che indica se l’operazione è andata a buon fine #include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int maxconn) – sockfd specifica un descrittore di socket – maxconn specifica la lunghezza della coda delle richieste di connessione pendenti La primitiva specifica, per socket connection-oriented che il processo desidera ricevere richieste di connessione e quante richieste di connessione devono essere accodate prima di ritornare un errore (0 per infinite) listen ritorna un valore che indica se l’operazione è andata a buon fine Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 16 8 Politecnico di Milano API socket - accept • #include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *partner, int *len) – sockfd specifica un descrittore di socket – partner è un puntatore ad un indirizzo di socket vuoto che viene riempito con l’indirizzo del partner della comunicazione – len deve essere pari alla lunghezza della struttura partner • La primitiva estrae la prima richiesta di connessione dalla coda e ritorna un descrittore di socket associato a questa particolare connessione • Se non esistono richieste di connessione pendenti il processo si sospende fino a quando un processo client non esegua la corrispondente operazione connect Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 17 API socket - connect • • • • #include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, struct sockaddr *server, int len) – sockfd specifica un descrittore di socket – server è un puntatore ad un indirizzo di socket al quale ci si vuole connettere – len deve essere pari alla lunghezza della struttura server connect ritorna un valore che indica se l’operazione è andata a buon fine Se il canale è di tipo connection-oriented, tramite la primitiva connect viene inoltrata una richiesta di creazione di un nuovo canale. Se la richiesta viene accettata tramite la accept, la comunicazione potrà procedere usando il descrittore di file sockfd Se il canale è di tipo connectionless, tramite la primitiva connect si stabilisce l’indirizzo del partner della comunicazione. I messaggi spediti verranno consegnati all’indirizzo specificato da server e solo da tale indirizzo verranno accettati messaggi in arrivo. In questo caso l’uso della primitiva è facoltativo. Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 18 9 Politecnico di Milano API socket - send e sendto • • • • • int send(int sockfd, char *buf, int len, int flags) – sockfd specifica un descrittore di socket – buf è un puntatore ad un buffer di dati che verrà trasmesso al partner della comunicazione così come è stato determinato dalla connect – len specifica la lunghezza del buffer – flags specifica alcune caratteristiche della comunicazione (solitamente è 0) La primitiva ritorna il numero di byte trasmessi int sendto(int sockfd, char *buf, int len, int flags, struct sockaddr *to, int tolen) – sockfd, buf, len, flags come sopra – to è un puntatore ad un indirizzo di socket al quale si vuole trasmettere il datagramma buf – tolen deve essere pari alla lunghezza della struttura to La primitiva ritorna il numero di byte trasmessi N on è necessario aver prima eseguito una connect Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 19 API socket - recv e recvfrom • • • • • int recv(int sockfd, char *buf, int len, int flags) – sockfd specifica un descrittore di socket – buf è un puntatore ad un buffer di dati dove verrà memorizzato il datagramma ricevuto dal partner così come è stato determinato dalla connect – len specifica la lunghezza del buffer – flags specifica alcune caratteristiche della comunicazione (solitamente è 0) La primitiva ritorna il numero di byte ricevuti int recvfrom(int sockfd, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) – sockfd, buf, len, flags come sopra – from è un puntatore ad un indirizzo di socket nel quale viene ritornato l’indirizzo del mittente del datagramma buf – fromlen deve essere pari alla lunghezza della struttura from La primitiva ritorna il numero di byte ricevuti N on è necessario aver prima eseguito una connect Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 20 10 Politecnico di Milano API socket - read, read, write e close • int read(int sockfd, char *buf, int numbyte) – sockfd specifica un descrittore di socket – buf è un puntatore ad un buffer di dati dove verranno memorizzati i byte letti dal socket – numbyte specifica il numero di byte da leggere • • La primitiva ritorna il numero di byte effettivamente letti int write(int sockfd, char *buf, int numbyte) – sockfd specifica un descrittore di socket – buf è un puntatore ad buffer di dati dove sono memorizzati i byte da trasmettere – numbyte specifica il numero di byte da scrivere • • La primitiva ritorna il numero di byte effettivamente scritti int close(int sockfd) – sockfd specifica un descrittore di socket La primitiva chiude il socket • Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 21 API socket - gethostbyname e gethostname • Routine per la determinazione dell’indirizzo IP di un host noto il suo nome – #include <netdb.h> struct hostent *gethostbyname(const char *name) • Routine per la determinazione del nome dell’host locale – #include <unistd.h> int gethostname(char *hostname, size_t size) • La struttura hostent – struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; }; # define h_addr h_addr_list[0] Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 22 11 Politecnico di Milano API socket - Routine ausiliarie • Routine di conversione di byte order tra quello usato da TCP/ IP e quello locale – – – – u_long htonl(u_long hostlong) u_short htons(u_short hostshort) u_long ntohl(u_long netlong) u_short ntohs(u_short netshort) • Routine di trasformazione degli indirizzi dal formato stringa “131.112.45.67” a quello in 4 byte consecutivi e viceversa – u_long inet_addr(char* str) – char *inet_ntoa(struct in_addr addr) • Routine per l’impostazione delle proprietà di un file – #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, long arg) – invocata come fcntl(fd, F_SETFL, O_NONBLOCK) imposta il file di descrittore fd in maniera “non bloccante”. Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 23 La comunicazione attraverso socket UDP • Lo User Datagram Protocol è orientato ad implementare applicazioni che utilizzano il paradigma a scambio messaggi • Processo sender: genera il messaggio • Processo receiver: riceve il messaggio • Socket SOCK_DGRAM Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 24 12 Politecnico di Milano Client UDP #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <sys/socket.h> <netinet/in.h> <netdb.h> hp = gethostbyname(host); /* get address info */ if(hp == NULL) { perror("accessing host by name"); exit(-1); } sa.sin_family = AF_INET; sa.sin_port = htons(port); /* set port */ memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); /* set host address */ /* now create the socket */ sock = socket (PF_INET, SOCK_DGRAM, 0); if(sock < 0) { perror("opening the socket"); exit(-1); } /* now send data */ if (sendto(sock, message, strlen(message), 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("writing on stream socket"); exit(-1); } /* close */ close(sock); return 0; #define MAXHOSTNAME 128 #define BUFLEN 1024 int main(int argc, char *argv[]) { int sock, port; char *host, *message; struct sockaddr_in sa; struct hostent *hp; /* Parse command line arguments */ if(argc<3) { printf("Usage: client <host> <port> [<message>]\n"); exit(-1); } host = argv[1]; port = atof(argv[2]); if(argc == 3) message = "Let me try..."; else message = argv[3]; /* set the address the new socket have to be bound to */ memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear address */ } Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 25 Server UDP #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h> <netdb.h> sa.sin_port = htons(portNumber); sa.sin_addr.s_addr=htonl(INADDR_ANY); /* accept conn. from any host */ /* now create the socket */ sock = socket (PF_INET, SOCK_DGRAM, 0); if(sock < 0) {perror("opening the socket"); exit(-1);} /* now bind the socket to the specified address (i.e., sa) */ if (bind(sock, (struct sockaddr *) &sa, sizeof (sa)) < 0) { perror("binding the socket"); exit(-1); } /* wait for incoming requests and print them (main loop) */ while(1) { memset(buf,0,BUFLEN); /* receive incoming data */ recv(sock, buf, BUFLEN-1, 0); /* print data */ printf("Received message: %s\n", buf); } close(sock); return 0; #define MAXHOSTNAME 128 #define BUFLEN 1024 int main(int argc, char *argv[]) { int sock, portNumber; struct sockaddr_in sa; char buf[BUFLEN]; /* Parse command line arguments */ if(argc<2) {printf("Usage: server <port_number>\n");exit(-1);} portNumber = atof(argv[1]); /* set the address the new socket have to be bound to */ memset(&sa, 0, sizeof(struct sockaddr_in));/* clear our address */ sa.sin_family = AF_INET; } Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 26 13 Politecnico di Milano La comunicazione attraverso socket TCP • I socket TCP permettono l’implementazione di applicazioni client/ server basate su comunicazioni con connessione • In siffatte applicazioni è possibile strutturare il server in due modi – Server iterativo • Il server si pone in attesa di una richiesta • A fronte di una richiesta esegue il servizio e ritorna i risultati • Si rimette in attesa – Server a gestore • Il server si pone in attesa di una richiesta • A fronte di una richiesta crea un processo che si occupa di fornire il servizio richiesto e si rimette subito in attesa • Il processo appena creato fornisce il servizio e poi termina Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Politecnico di Milano 27 Client TCP #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <sys/socket.h> <netinet/in.h> <netdb.h> #define MAXHOSTNAME 128 #define BUFLEN 1024 int main(int argc, char *argv[]) { int sock, port; char *host, *message; struct sockaddr_in sa; struct hostent *hp; /* Parse command line arguments */ if(argc<3) {printf("Usage: client <host> <port> [<message>]\n"); exit(-1);} host = argv[1]; port = atof(argv[2]); if(argc == 3) message = "Let me try..."; else message = argv[3]; /* set the address the new socket have to be bound to */ memset(&sa, 0, sizeof(struct sockaddr_in)); hp = gethostbyname(host); /* get address */ if(hp == NULL) { perror("accessing host by name"); exit(-1); } sa.sin_family = AF_INET; sa.sin_port = htons(port); /* set port */ memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);/* set host address */ /* now create the socket */ sock = socket (PF_INET, SOCK_STREAM, 0); if(sock < 0) {perror("opening the socket"); exit(-1);} /* now connect the socket to the specified address (i.e., sa) */ if (connect(sock, (struct sockaddr *) &sa, sizeof (sa)) < 0) { perror("binding the socket"); exit(-1); } /* now send data */ if (write(sock, message, strlen(message)) < 0) { perror("writing on stream socket"); exit(-1); } /* close */ close(sock); return 0; } • Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 28 14 Politecnico di Milano Server TCP: schema iterativo #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <sys/socket.h> <netinet/in.h> <netdb.h> /* now bind the socket to the specified addr. */ if (bind(sock, (struct sockaddr *) &sa, sizeof (sa)) < 0) { perror("binding the socket"); exit(-1); } /* listen to the connection and accept incoming requests (main loop) */ if(listen(sock, 0) < 0) { perror("listening"); exit(-1); } while(1) { /* accept a new connection */ s = accept(sock,NULL,NULL); if(s < 0) { perror("accepting"); exit(-1); } /* read data */ while(1) { memset(buf, 0, sizeof(buf)); /* clear buf */ if ((n = read(s, buf, BUFLEN-1)) < 0) perror("reading stream message"); else if (n == 0) break; else { printf("%s\n",buf); /* stop if EOT char is sent */ if(buf[n-1]==4) break; } } close(s); } close(s); close(sock); return 0; #define MAXHOSTNAME 128 #define BUFLEN 1024 int main(int argc, char *argv[]) { int sock, s, n, portNumber; struct sockaddr_in sa; char buf[BUFLEN]; /* Parse command line arguments */ if(argc<2) {printf("Usage: server <port_number>\n"); exit(-1); } portNumber = atof(argv[1]); /* set the address the new socket have to be bound to */ memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear our address */ sa.sin_family = AF_INET; sa.sin_port = htons(portNumber); sa.sin_addr.s_addr = htonl(INADDR_ANY); /* now create the socket */ sock = socket (PF_INET, SOCK_STREAM, 0); if(sock < 0) {perror("opening the socket"); exit(-1); } } Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione Gianpaolo Cugola - Impianti di Elaborazione dell'Informazione 29 15