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