La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

2-1 Programmazione socket. 2-2 Programmazione socket Socket API r introdotte in UNIX BSD4.1, 1981 r create, utilizzate e rilasciate esplicitamente dalle.

Presentazioni simili


Presentazione sul tema: "2-1 Programmazione socket. 2-2 Programmazione socket Socket API r introdotte in UNIX BSD4.1, 1981 r create, utilizzate e rilasciate esplicitamente dalle."— Transcript della presentazione:

1 2-1 Programmazione socket

2 2-2 Programmazione socket Socket API r introdotte in UNIX BSD4.1, 1981 r create, utilizzate e rilasciate esplicitamente dalle applicazioni r paradigma client/server r due tipi di servizi di trasporto via API socket: m unreliable datagram m reliable, byte stream- oriented uninterfaccia situata nellhost, creata dallapplicazione e controllata dal SO attraverso la quale un processo applicativo può sia inviare che ricevere messaggi a/da un altro processo applicativo situato in un altro host socket Obiettivo: imparare a costruire applicazioni client/server che comunicano tramite socket

3 2-3 I due tipi principali di socket r SOCK_STREAM m TCP m affidabile m ordine dati garantito m orientato alla connessione m bidirezionale r SOCK_DGRAM m UDP m inaffidabile m nessuna garanzia su ordine dati m nessuna nozione di connessione – lapplicazione indica la destinazione di ogni pacchetto m può inviare o ricevere App socket Dest App socket D1 D3 D

4 2-4 Creazione di socket in C: socket r int s = socket (domain, type, protocol); s : socket descriptor, un intero (come un file-handle) domain : intero, dominio di comunicazione es., AF_INET (IPv4 protocol) – usato di solito type : tipo di comunicazione SOCK_STREAM : affidabile, 2-vie, connection-based SOCK_DGRAM : inaffidabile, connectionless altri valori: servono permessi root, usati raramente o obsoleti protocol : specifica il protocollo, di solito settato a 0 (vedere file /etc/protocols per una lista di opzioni) NOTA: Una chiamata socket non specifica da dove verranno i dati o dove andranno, crea solamente uninterfaccia!

5 2-5 La funzione bind r associa e riserva un port alla socket r int status = bind (sockid, &addrport, size); status : error status, = -1 se il bind fallisce sockid : intero, socket descriptor addrport : struct sockaddr, lindirizzo (IP) e il port della macchina es. indirizzo: INADDR_ANY sceglie lindirizzo locale es. port: 0 lascia al SO il compito di stabilire il port size : la dimensione (in byte) della struttura addrport r usato dal server (opzionalmente dal client)

6 2-6 Quando usare il bind SOCK_DGRAM : m in trasmissione il bind non è necessario. Il SO trova un port ogni volta che la socket manda un pacchetto m in ricezione il bind è necessario SOCK_STREAM : m la destinazione è determinata durante il setup di connessione m non occorre conoscere il port attraverso cui vengono inviati i dati (durante il setup di connessione lestremità ricevente è informata sul port del mittente)

7 2-7 Connection Setup ( SOCK_STREAM ) Ricordare: nessun connection setup per il SOCK_DGRAM r I partecipanti alla connessione sono di due tipi: m passivo: aspetta che un partecipante attivo richieda la connessione m attivo: inizia la richiesta di connessione verso il lato passivo r Una volta che la connessione è stabilita, i partecipanti attivi e passivi sono simili m entrambi possono mandare e ricevere dati m ognuno può terminare la connessione

8 2-8 Connection setup (cont.) r Participante passivo (es. server) step 1: listen (per arrivo di richieste) step 3: accept (una richiesta) m step 4: trasferimento dati r La connessione viene accettata su una nuova socket r La vecchia socket continua ad aspettare la connessione di nuovi partecipanti r Three way handshaking r Participante attivo (es. client) step 2: richiede & stabilisce connect ion m step 4: trasferimento dati Passive Participant l-socka-sock-1a-sock-2 Active 1 socket Active 2 socket

9 2-9 Connection setup: listen & accept r Usate dal partecipante passivo (server) r int status = listen (sock, queuelen); status : 0 se si mette in ascolto, -1 se dà errore sock : intero, socket descriptor queuelen : intero, numero di partecipanti attivi che possono aspettare per una connessione listen è non-blocking: ritorna immediatamente r int s = accept (sock, &name, namelen); s : intero, la nuova socket (usata per il trasferimento dati) sock : intero, la socket originale, usata come prototipo per s name : struct sockaddr, indirizzo del partecipante attivo namelen : sizeof(name): valore/risultato deve essere settato in maniera appropriata prima della chiamata aggiustato dal SO quando la funzione ritorna accept è blocking: aspetta una connessione prima di ritornare

10 2-10 connect call Usata dal partecipante attivo (client) r int status = connect (sock, &name, namelen); status : 0 se connessione OK, -1 altrimenti sock : intero, socket da essere utilizzata nella connessione name : struct sockaddr : indirizzo del partecipante passivo namelen : intero, sizeof(name) connect è blocking

11 2-11 Sending / Receiving Data Con connessione ( SOCK_STREAM ): m int count = send (sock, &buf, len, flags); count : Numero byte trasmessi (-1 se errore) buf : char[ ], buffer da trasmettere len : intero, lunghezza buffer (in byte) da trasmettere flags : intero, opzioni speciali, di solito settate a 0 m int count = recv (sock, &buf, len, flags); count : Num. byte ricevuti (-1 se errore) buf : void[ ], immagazzina i byte ricevuti len : intero, lunghezza buffer (in byte) flags : intero, opzioni speciali, di solito settate a 0 m Le chiamate sono blocking [ritornano solo dopo che i dati sono inviati (al socket buf) / ricevuti]

12 2-12 Sending / Receiving Data (cont.) Senza connessione ( SOCK_DGRAM ): m int count = sendto (sock, &buf, len, flags, &addr, addrlen); count, sock, buf, len, flags : stesse di send addr : struct sockaddr, indirizzo della destinazione addrlen : sizeof(addr) m int count = recvfrom (sock, &buf, len, flags, &addr, addrlen); count, sock, buf, len, flags: stesse di recv name : struct sockaddr, indirizzo della sorgente namelen : sizeof(name): valore/risultato m Le chiamate sono blocking [ritornano solo dopo che i dati sono inviati (al socket buf) / ricevuti]

13 2-13 close r Quando si finisce di utilizzare una socket, la socket dovrebbe essere chiusa: r status = close (s); status : 0 se OK, -1 se errore s : il socket descriptor (della socket da chiudere) r Chiusura di una socket Chiude una connessione (per SOCK_STREAM ) m Libera il port utilizzato dalla socket

14 2-14 Client/server socket interaction: TCP crea socket socket() Server associa port bind () aspetta richieste listen () accetta richieste accept () ricevi dati recv () manda dati send () chiudi socket close () Client richiedi connessione connect () manda dati send () ricevi dati recv () chiudi socket close () crea socket socket() TCP connection setup

15 2-15 Client/server socket interaction: UDP crea socket socket() Server associa port bind () ricevi dati recvfrom () manda dati sendto () chiudi socket close () Client manda dati sendto () ricevi dati recvfrom () chiudi socket close () crea socket socket()

16 2-16 La struct sockaddr r Generica: struct sockaddr { u_short sa_family; char sa_data[14]; }; sa_family specifica quale famiglia di indirizzi deve essere usata determina come i 14 byte rimanenti saranno utilizzati r Specifica Internet: struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; m sin_family = AF_INET sin_port : port # ( ) sin_addr : IP address sin_zero : non utilizzato //Structure per ragioni storiche struct in_addr { u_long s_addr; //32-bit long };

17 2-17 Byte-ordering di indirizzo e port r Indirizzo e port sono memorizzati come interi m u_short sin_port; (16 bit) m in_addr sin_addr; (32 bit) r Problema: m Macchine/SO usano differenti modalità per memorizzare i dati little-endian: lower bytes first big-endian: higher bytes first m Queste macchine devono poter comunicare luna con laltra attraverso la rete Big-Endian machine Little-Endian machine SBAGLIATO

18 2-18 Soluzione: Network Byte-Ordering r Definizioni: m Host Byte-Ordering: il byte ordering usato dallhost (big o little) m Network Byte-Ordering: il byte ordering usato dalla rete – sempre big-endian r Ogni word inviata attraverso la rete dovrebbe essere convertita in Network Byte-Order prima della trasmissione (e viceversa in Host Byte-Order una volta riceuta) r D: Le socket devono effettuare la conversione automaticamente? r D: Dato che per le macchine big-endian non servono routine di conversione e per le macchine little-endian sì, come si può evitare di scrivere due versioni di codice?

19 2-19 Funzioni di byte-ordering r u_long htonl(u_long x); r u_short htons(u_short x); r u_long ntohl(u_long x); r u_short ntohs(u_short x); r Sulle macchine big-endian, queste routine non fanno nulla r Sulle macchine little-endian, invertono il byte order r Lo stesso codice funziona indipendentemente dal tipo di endian della macchina Big-Endian machine Little-Endian machine htonl ntohl

20 2-20 Altre funzioni utili atoi (char* s): converte la stringa s in un intero bcopy (void* s, void* d, int n): copia n byte di s in d bzero (char* c, int n): pone n byte a 0 a partire dal valore puntato da c gethostname (char *name, int len): ritorna il nome dellhost sui cui il processo risiede gethostbyname (char *name): converte lhostname in una struttura (hostent) contenente lindirizzo IP (utilizzando il servizio di DNS)

21 2-21 #include […altri include…] #define MAX_CODA 5 /* massimo backlog */ main(int argc, char* argv[]) /* prende in input la porta */ { int sock; /* socket in attesa */ int sockmsg; /* socket servente */ struct sockaddr_in server; if ( argc != 2 ) { printf("uso: %s \n", argv[0]); exit(EXIT_FAILURE); } sock = socket(AF_INET,SOCK_STREAM,0); /* socket prototipo */ if( sock <0 ) { printf("server: errore %s nella creazione del socket\n", strerror(errno)); exit(EXIT_FAILURE); } Server : Inizializzazione

22 2-22 server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(atoi(argv[1])); if( bind(sock, (struct sockaddr *)&server, sizeof(server)) ) { printf("server: bind fallita\n"); exit(EXIT_FAILURE); } printf("server: rispondo sulla porta %d\n", ntohs(server.sin_port)); if( listen(sock, MAX_CODA) <0 ) { printf("server: errore %s nella listen\n", strerror(errno)); exit(EXIT_FAILURE); } Server: Creazione della coda struttura per il bind dimensiono la coda di backlog

23 2-23 int totale=0; char input[256]; sockmsg = accept(sock, 0, 0); if( sockmsg <0 ) { printf("errore %s nella accept\n", strerror(errno)); exit(EXIT_FAILURE); } printf("server: accetto una nuova connessione\n); close(sock); printf("server: ho chiuso il socket\n"); } /* fine della funzione main */ Server: Gestione delle connessioni qui ci va il codice che presta il servizio (segue)

24 2-24 { /* questo e il codice di servizio del server */ int len; printf("server %d: iniziato \n", getpid() ); while( len = recv(sockmsg, input, sizeof(input), 0) ) { int numero; char tot[256]; input[len]='\0'; /* termina la stringa*/ numero = atoi(input); /* converti in intero */ printf("server: arrivato il numero: %d\n", numero); totale=totale+numero; /* calcolo totale*/ sprintf(tot, "%d", totale); /* prepara la stringa */ send(sockmsg, tot, sizeof(tot), 0); /* invia la stringa */ } close(sockmsg); /* prima di uscire chiudi il socket */ printf("server: socket chiuso\n); exit(EXIT_SUCCESS); /* connessione terminata */ } Server: Gestione del client

25 2-25 #include […altri include…] main(int argc, char* argv[]) { int sock; /* descrittore del socket */ struct sockaddr_in server; struct hostent *hp; char input[256]; if(argc!=3) { printf("uso: %s \n", argv[0]); exit(1); } sock = socket(AF_INET, SOCK_STREAM, 0); if( sock < 0 ) { printf("client: errore %s nella creazione del socket\n", strerror(errno)); exit(1); } Client: Inizializzazione

26 2-26 hp = gethostbyname(argv[1]); if( hp == NULL ){ printf("client: l'host %s non e' raggiungibile.\n", argv[1]); exit(1); } server.sin_family = AF_INET; bcopy(hp->h_addr, &server.sin_addr, hp->h_length); server.sin_port = htons(atoi(argv[2])); if( connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0 ) { printf("client: errore %s durante la connect\n", strerror(errno)); exit(1); } printf("client: connesso a %s, porta %d\n", argv[1], ntohs(server.sin_port)); Client: Connessione col server

27 2-27 printf("client: num. o quit? "); scanf("%s",&input); while( strcmp(input,"quit") != 0 ) { char result[256]; if( send (sock, (char *)&input, strlen(input), 0) <0) { printf("errore %s durante la write\n", strerror(errno)); exit(1); } if( recv(sock,(char *)&result, sizeof(result), 0) < 0 ) { printf("errore %s durante la read\n", strerror(errno)); exit(1); } printf("client: ricevo dal server %s\n", result); printf("client: num. o \"quit\"? "); scanf("%s",&input); } close(sock); printf("client: ho chiuso il socket\n"); } /* fine della funzione main */ Client: Gestione messaggi

28 2-28 Gestione del blocco delle funzioni r Molte delle funzioni esaminate si bloccano finchè accade un determinato evento accept : fino allarrivo di una connessione connect : fino a quando la connessione non è stabilita recv, recvfrom : fino a quando un pacchetto (di dati) non è ricevuto send, sendto : fino a quando i dati non vengono messi nel buffer della socket r Per semplici programmi il blocco è conveniente r Cosa accade ai programmi più complessi? m connessioni multiple m invio e ricezione contemporaneo m necessità di eseguire in contemporanea codice non legato alla rete

29 2-29 Gestione blocco delle funzioni (cont.) r Opzioni: m creazione di codice multi-process o multi-threaded eliminazione del blocking (es., usando la funzione di controllo del file descriptor fcntl ) uso della funzione select r Cosa fa la select? m si può bloccare permanentemente, per un intervallo limitato o non bloccarsi m input: un set di file-descriptor m output: info sullo stato dei file-descriptor m cioè, può identificare le socket che sono pronte alluso: le funzioni che coinvolgono quelle socket ritornano immediatamente

30 2-30 select function call r int status = select (nfds, &readfds, &writefds, &exceptfds, &timeout); status : # di oggetti pronti, -1 se errore nfds : 1 + il numero del più grande file descriptor da controllare readfds : lista dei descrittori pronti alla lettura writefds : lista dei descrittori pronti alla scrittura exceptfds : lista dei descrittori che registrano uneccezione timeout : intervallo dopo il quale la select ritorna, anche se non cè niente di pronto – può essere tra 0 e (settare il parametro timeout a NULL per )

31 2-31 Da utilizzare con la select select utilizza una struct fd_set per le liste dei descrittori m è un vettore di bit se il bit i è settato in [ readfds, writefds, exceptfds ], select controllerà che il file descriptor (cioè la socket) i è pronta per [reading, writing, exception] Prima di chiamare select : FD_ZERO (&fdvar) : azzera la struttura FD_SET (i, &fdvar) : aggiunge il file descriptor i alla lista FD_CLR (i, &fdvar) : rimuove il file descriptor i dalla lista Dopo aver chiamato select : int FD_ISSET (i, &fdvar) : booleano ritorna TRUE iff i è pronto

32 2-32 Rilascio dei port Qualche volta unuscita rude da un programma (es. ctrl-c ) non rilascia il port correttamente r In ogni caso il port dovrebbe essere rilasciato dopo alcuni minuti r Per ridurre la probabilità di questo inconveniente, includere il codice seguente: #include void cleanExit(){exit(0);} m nel codice della socket: signal(SIGTERM, cleanExit); signal(SIGINT, cleanExit);


Scaricare ppt "2-1 Programmazione socket. 2-2 Programmazione socket Socket API r introdotte in UNIX BSD4.1, 1981 r create, utilizzate e rilasciate esplicitamente dalle."

Presentazioni simili


Annunci Google