programmazione socket

32
2-1 Programmazione socket

Upload: rhian

Post on 14-Jan-2016

43 views

Category:

Documents


0 download

DESCRIPTION

Programmazione socket. un’interfaccia situata nell’host, creata dall’applicazione 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. Programmazione socket. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Programmazione socket

2-1

Programmazione socket

Page 2: Programmazione socket

2-2

Programmazione socket

Socket API introdotte in UNIX BSD4.1,

1981 create, utilizzate e

rilasciate esplicitamente dalle applicazioni

paradigma client/server due tipi di servizi di

trasporto via API socket: unreliable datagram reliable, byte stream-

oriented

un’interfaccia situata nell’host, creata

dall’applicazione 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

Page 3: Programmazione socket

2-3

I due tipi principali di socket SOCK_STREAM

TCP affidabile ordine dati garantito orientato alla

connessione bidirezionale

SOCK_DGRAM UDP inaffidabile nessuna garanzia su ordine

dati nessuna nozione di

“connessione” – l’applicazione indica la destinazione di ogni pacchetto

può inviare o ricevereApp

socket3 2 1

Dest.123

App

socket3 2 1

D1

D3

D2

1

2

3

Page 4: Programmazione socket

2-4

Creazione di socket in C: socket 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 un’interfaccia!

Page 5: Programmazione socket

2-5

La funzione bind

associa e riserva un port alla socket int status = bind (sockid, &addrport, size);

status: error status, = -1 se il bind fallisce sockid: intero, socket descriptor addrport: struct sockaddr, l’indirizzo (IP) e il

port della macchina • es. indirizzo: INADDR_ANY sceglie l’indirizzo locale• es. port: 0 lascia al SO il compito di stabilire il port

size: la dimensione (in byte) della struttura addrport

usato dal server (opzionalmente dal client)

Page 6: Programmazione socket

2-6

Quando usare il bind

SOCK_DGRAM: in trasmissione il bind non è necessario. Il

SO trova un port ogni volta che la socket manda un pacchetto

in ricezione il bind è necessario SOCK_STREAM:

la destinazione è determinata durante il setup di connessione

non occorre conoscere il port attraverso cui vengono inviati i dati (durante il setup di connessione l’estremità ricevente è informata sul port del mittente)

Page 7: Programmazione socket

2-7

Connection Setup (SOCK_STREAM) Ricordare: nessun connection setup per il

SOCK_DGRAM I partecipanti alla connessione sono di due tipi:

passivo: aspetta che un partecipante attivo richieda la connessione

attivo: inizia la richiesta di connessione verso il lato passivo

Una volta che la connessione è stabilita, i partecipanti attivi e passivi sono “simili” entrambi possono mandare e ricevere dati ognuno può terminare la connessione

Page 8: Programmazione socket

2-8

Connection setup (cont.) Participante passivo (es.

server) step 1: listen (per arrivo

di richieste) step 3: accept (una

richiesta) step 4: trasferimento dati

La connessione viene accettata su una nuova socket

La vecchia socket continua ad aspettare la connessione di nuovi partecipanti

Three way handshaking

Participante attivo (es. client)

step 2: richiede & stabilisce connection

step 4: trasferimento dati

Passive Participant

l-socka-sock-1 a-sock-2

Active 1

socket

Active 2

socket

Page 9: Programmazione socket

2-9

Connection setup: listen & accept Usate dal partecipante passivo (server) 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

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

Page 10: Programmazione socket

2-10

connect call

Usata dal partecipante attivo (client) 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

Page 11: Programmazione socket

2-11

Sending / Receiving Data

Con connessione (SOCK_STREAM): 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

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

Le chiamate sono blocking [ritornano solo dopo che i dati sono inviati (al socket buf) / ricevuti]

Page 12: Programmazione socket

2-12

Sending / Receiving Data (cont.)

Senza connessione (SOCK_DGRAM): 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)

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

Le chiamate sono blocking [ritornano solo dopo che i dati sono inviati (al socket buf) / ricevuti]

Page 13: Programmazione socket

2-13

close

Quando si finisce di utilizzare una socket, la socket dovrebbe essere chiusa:

status = close (s); status: 0 se OK, -1 se errore s: il socket descriptor (della socket da

chiudere) Chiusura di una socket

Chiude una connessione (per SOCK_STREAM) Libera il port utilizzato dalla socket

Page 14: Programmazione socket

2-14

Client/server socket interaction: TCP

crea socketsocket()

Server

associa port

bind ()

aspetta richieste

listen ()

accetta richieste

accept ()

ricevi datirecv ()

manda dati

send ()

chiudi socket

close ()

Client

richiedi connessioneconnect ()

manda dati

send ()

ricevi dati

recv ()

chiudi socket

close ()

crea socketsocket()

TCP connection

setup

Page 15: Programmazione socket

2-15

Client/server socket interaction: UDP

crea socketsocket()

Server

associa port

bind ()

ricevi dati

recvfrom ()

manda dati

sendto ()

chiudi socket

close ()

Client

manda dati

sendto ()

ricevi dati

recvfrom ()

chiudi socket

close ()

crea socketsocket()

Page 16: Programmazione socket

2-16

La struct sockaddr 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

Specifica Internet:struct sockaddr_in {

short sin_family;u_short sin_port;struct in_addr sin_addr;char sin_zero[8];

}; sin_family = AF_INET sin_port: port # (0-65535) sin_addr: IP address sin_zero: non utilizzato

//Structure per ragioni storiche

struct in_addr {

u_long s_addr; //32-bit long

};

Page 17: Programmazione socket

2-17

Byte-ordering di indirizzo e port

Indirizzo e port sono memorizzati come interi u_short sin_port; (16 bit) in_addr sin_addr; (32 bit)

Problema: Macchine/SO usano differenti modalità per memorizzare i

dati• little-endian: lower bytes first• big-endian: higher bytes first

Queste macchine devono poter comunicare l’una con l’altra attraverso la rete

128.119.40.12

128

119

40 12

12.40.119.128

128

119

40 12

Big-Endianmachine Little-Endian

machine

SBAGLIATO

Page 18: Programmazione socket

2-18

Soluzione: Network Byte-Ordering Definizioni:

Host Byte-Ordering: il byte ordering usato dall’host (big o little)

Network Byte-Ordering: il byte ordering usato dalla rete – sempre big-endian

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)

D: Le socket devono effettuare la conversione automaticamente?

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?

Page 19: Programmazione socket

2-19

Funzioni di byte-ordering

u_long htonl(u_long x); u_short htons(u_short x);

u_long ntohl(u_long x); u_short ntohs(u_short x);

Sulle macchine big-endian, queste routine non fanno nulla

Sulle macchine little-endian, invertono il byte order

Lo stesso codice funziona indipendentemente dal tipo di “endian” della macchina

128.119.40.12

128

119

40 12

128.119.40.12

128

119

40 12

Big-Endianmachine Little-Endian

machine

hton

l nto

hl

128

119

40 12 128

119

4012

Page 20: Programmazione socket

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

dell’host sui cui il processo risiede gethostbyname (char *name): converte

l’hostname in una struttura (hostent) contenente l’indirizzo IP (utilizzando il servizio di DNS)

Page 21: Programmazione socket

2-21

#include <sys/types.h>[…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 <numero-della-porta>\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

Page 22: Programmazione socket

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 peril bind

dimensionola coda dibacklog

Page 23: Programmazione socket

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)

Page 24: Programmazione socket

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

Page 25: Programmazione socket

2-25

#include <stdio.h> […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 <host> <numero-della-porta>\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

Page 26: Programmazione socket

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

Page 27: Programmazione socket

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

Page 28: Programmazione socket

2-28

Gestione del blocco delle funzioni Molte delle funzioni esaminate si bloccano

finchè accade un determinato evento accept: fino all’arrivo 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 Per semplici programmi il blocco è conveniente Cosa accade ai programmi più complessi?

connessioni multiple invio e ricezione contemporaneo necessità di eseguire in contemporanea codice non

legato alla rete

Page 29: Programmazione socket

2-29

Gestione blocco delle funzioni (cont.) Opzioni:

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

Cosa fa la select? si può bloccare permanentemente, per un intervallo

limitato o non bloccarsi input: un set di file-descriptor output: info sullo stato dei file-descriptor cioè, può identificare le socket che sono “pronte

all’uso”: le funzioni che coinvolgono quelle socket ritornano immediatamente

Page 30: Programmazione socket

2-30

select function call 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

un’eccezione 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 )

Page 31: Programmazione socket

2-31

Da utilizzare con la select select utilizza una struct fd_set per le liste dei

descrittori è 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”

Page 32: Programmazione socket

2-32

Rilascio dei port

Qualche volta un’uscita “rude” da un programma (es. ctrl-c) non rilascia il port correttamente

In ogni caso il port dovrebbe essere rilasciato dopo alcuni minuti

Per ridurre la probabilità di questo inconveniente, includere il codice seguente:

#include <signal.h>

void cleanExit(){exit(0);} nel codice della socket:

signal(SIGTERM, cleanExit);

signal(SIGINT, cleanExit);