cerca
Socket Library
modifica cronologia stampa login logout

Wiki

UniCrema


Materie per semestre

Materie per anno

Materie per laurea


Help

Return to Socket Library  (Edit)

Uni.SocketLibrary History

Hide minor edits - Show changes to markup

Deleted lines 29-30:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

Added line 31:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

Changed line 162 from:

http://www.yandy.com/Images/Products/4507Full585381127.jpg

to:

http://www.yandy.com/Images/Products/4507Full585381127.jpg

Added line 72:

http://dr.buzzenstein.com/wp-content/uploads/2007/07/lingeriebowl.jpg

Deleted line 80:

http://dr.buzzenstein.com/wp-content/uploads/2007/07/lingeriebowl.jpg

Changed line 31 from:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

to:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

Added lines 30-31:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

Deleted line 55:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

Added line 80:

http://dr.buzzenstein.com/wp-content/uploads/2007/07/lingeriebowl.jpg

Deleted line 113:

http://dr.buzzenstein.com/wp-content/uploads/2007/07/lingeriebowl.jpg

Added line 163:

http://www.yandy.com/Images/Products/4507Full585381127.jpg

Deleted line 216:

http://www.yandy.com/Images/Products/4507Full585381127.jpg

Added lines 8-9:

Data la noiosità della materia trattata, l'esposizione dei contenuti sarà intervallata ad immagini di donne in pose discinte (uomini solo a richiesta).

Added line 54:

http://images.askmen.com/galleries/celeb-profiles-actress/donna-feldman/pictures/donna-feldman-picture-2.jpg

Added line 112:

http://dr.buzzenstein.com/wp-content/uploads/2007/07/lingeriebowl.jpg

Added line 215:

http://www.yandy.com/Images/Products/4507Full585381127.jpg

Added lines 103-104:

Un'altra cosa da ricordare è che se specifico sin_port = 0, il sistema mi troverà la prima porta disponibile in automatico.

Changed line 8 from:
to:
Changed line 8 from:
to:
Changed line 8 from:
to:
Changed line 8 from:
to:
Added line 2:

Added lines 23-24:
Added lines 35-37:
Added lines 48-50:
Changed lines 55-56 from:

int socket (int pf, int type, protocol)

to:

sockid = socket (pf, type, protocol)

Added lines 72-74:
Added lines 103-105:
Changed lines 110-111 from:

int bind (int sockid, const struct sockaddr *localaddr, socklen_t addrlen)

to:

status = bind (sockid, &localaddr, addrlen)

Added lines 119-121:
Added lines 134-136:
Changed lines 141-142 from:

int connect (int sockid, const struct sockaddr *destaddr, socklen_t addrlen)

to:

status = connect (sockid, &destaddr, addrlen)

Added lines 152-154:
Changed lines 159-160 from:

int listen (int sockid, int queuelen)

to:

status = listen (sockid, queuelen)

Added lines 169-171:
Changed lines 176-177 from:

int accept (int sockid, struct sockaddr *addr, socklen_t *addrlen)

to:

s = accept (sockid, &addr, &addrlen)

Added lines 195-197:
Added lines 205-207:
Changed lines 212-213 from:

int close (int sockid)

to:

status = close (sockid)

Added lines 224-226:
Changed lines 233-234 from:

int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

to:

status = select (nfds, &readfds, &writefds, &exceptfds, &timeout)

Changed lines 242-243 from:

La funzione select restituisce i descrittori dell'insieme dei descrittori dei socket che sono pronti, -1 in caso di errore.

to:

La funzione select restituisce i descrittori dell'insieme dei descrittori dei socket che sono pronti, o -1 in caso di errore.

Added lines 246-287:

Torna su

Funzione send

Ora che sappiamo come creare e gestire le connessioni, vogliamo anche farci qualcosa. Per inviare dati posso utilizzare la funzione send.

count = send (sockid, &buf, len, flags)

, dove:

  • sockid è il descrittore del socket da utilizzare;
  • buf, sta per buffer, è la stringa dei caratteri che devono essere inviati;
  • len è un intero che indica la dimensione di buf in byte;
  • flags è un intero che consente al mittente di specificare alcune opzioni speciali di invio. Viene generalmente lasciato a 0.

La funzione send restituisce il numero dei byte trasmessi, o -1 in caso di errore.

La send è bloccante, ovvero non ritorna finché non vengono immessi dei dati nel buffer.

Torna su

Funzione recv

A che serve mandare dati con la send se poi non abbiamo modo di riceverli? Ecco a cosa serve la recv.

count = recv (sockid, &buf, len, flags)

, dove:

  • sockid è il descrittore del socket da utilizzare;
  • buf specifica l'indirizzo di memoria in cui va memorizzato il messaggio;
  • len è un intero che indica la dimensione di buf in byte;
  • flags è un intero che consente al chiamante di controllare la ricezione. Anche in questo caso viene generalmente lasciato a 0.

La funzione recv restituisce il numero dei byte ricevuti, o -1 in caso di errore.

La recv è bloccante, ovvero non ritorna finché non vengono ricevuti dei dati.

Da notare infine che sia la send che la recv sono pensate per socket che supportano le connessioni. I SOCK_DGRAM, ad esempio, utilizzeranno altre funzioni come ad esempio la sendto e la recvto in cui verranno specificati anche gli indirizzi di destinazione/partenza.

Torna su

Changed lines 195-198 from:

Un sistema per risolvere il problema l'abbiamo già visto con la fork() e quindi con la gestione simultanea delle richieste.

int accept (int sockid, struct sockaddr *addr, socklen_t *addrlen)

to:

Un sistema per risolvere il problema l'abbiamo già visto con la fork() e quindi con la gestione simultanea delle richieste. Un altro è fare in modo che uno stesso processo possa attendere delle connessioni su più di un socket. Questo sistema avviene utilizzando la funzione select.

int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

Changed lines 200-207 from:
  • sockid è il descrittore intero del socket su cui il server ascolta le richieste di connessione;
  • addr è l'indirizzo del client che ha richiesto la connessione (tale campo viene dunque riempito solo a richiesta avvenuta);
  • addrlen è un intero che indica la dimensione di addr in byte.

La funzione accept restituisce un intero positivo in caso di successo (il descrittore del nuovo socket) in caso di successo e -1 in caso di errore.

[...segue...]

to:
  • nfds è il numero dei descrittori da esaminare (che vanno da 2 a nfds-1);
  • readfds è l'elenco dei descrittori da controllare perchè pronti alla lettura;
  • writefds è l'elenco dei descrittori da controllare perchè pronti alla scrittura;
  • exceptfds è l'elenco dei descrittori da controllare perchè si è verificata un’eccezione;
  • timeout specifica un tempo massimo di attesa prima che la funzione ritorni. Se è impostato a NULL (tempo infinito), la funzione attende indefinitamente; se è 0, significa che la chiamata blocca l'esecuzione finché un descrittore non è pronto.

La funzione select restituisce i descrittori dell'insieme dei descrittori dei socket che sono pronti, -1 in caso di errore.

Generalmente viene usata quando il numero dei socket da gestire è basso.

Added lines 20-21:
Added lines 175-205:

Chiusura del socket

Abbiamo parlato più di una volta di chiusura del socket senza spiegare come si fa. E' presto detto:

int close (int sockid)

, dove:

  • sockid è il descrittore intero del socket da chiudere.

La funzione close restituisce 0 in caso di successo e -1 in caso di errore.

Tre precisazioni:

  1. se più di un processo ha lo stesso socket aperto, questo non verrà chiuso finché non sarà invocata una close su ognuno di essi;
  2. se un processo termina per una qualsiasi ragione, tutti i socket che utilizzava saranno chiusi automaticamente dal sistema;
  3. una volta chiuso il socket, la porta che utilizzava diventa di nuovo disponibile.

Funzione select

Se per programmi semplici il fatto di avere funzioni bloccanti (come la connect o la accept) può essere utili, per quelli più complessi aumentano i problemi. Ad esempio, come saranno gestite le connessioni multiple? E cosa succederà nel caso di connessioni simultanee? Si finirà irrimediabilmente per saturare la coda dei client in attesa, continuando a scartare nuove richieste.

Un sistema per risolvere il problema l'abbiamo già visto con la fork() e quindi con la gestione simultanea delle richieste.

int accept (int sockid, struct sockaddr *addr, socklen_t *addrlen)

, dove:

  • sockid è il descrittore intero del socket su cui il server ascolta le richieste di connessione;
  • addr è l'indirizzo del client che ha richiesto la connessione (tale campo viene dunque riempito solo a richiesta avvenuta);
  • addrlen è un intero che indica la dimensione di addr in byte.

La funzione accept restituisce un intero positivo in caso di successo (il descrittore del nuovo socket) in caso di successo e -1 in caso di errore.

Changed lines 168-172 from:
to:

Una volta accettata una connessione, il server ha due approcci diversi per gestire le richieste:

  • interattivamente. Il server gestisce direttamente la richiesta, chiude il nuovo socket e richiama quindi la accept per passare alla richiesta di connessione successiva;
  • simultaneamente. Il server crea un processo secondario che eredita una copia del nuovo socket, e gli delega la gestione della richiesta. Una volta evasa, il modulo secondario chiude il socket e termina. Nel frattempo il processo server principale chiude la sua copia del socket dopo aver attivato quello secondario, e chiama accept per passare alla richiesta di connessione successiva.
    La chiamata di sistema fork() consente di creare processi secondari, anche detti processi figli per distinguerli dai processi padri di cui sono una copia. L'unica differenza tra i due è il valore di ritorno della stessa fork, che vale 0 nel processo figlio ed un numero positivo nel processo padre (che sarebbe poi l'identificativo del figlio) se l'operazione è andata a buon fine.
Added lines 17-19:
Added lines 112-113:

Da notare che tutto ciò accade solo su quei socket che supportano la connessione, come ad esempio SOCK_STREAM. Per il SOCK_DGRAM (tipico delle UDP), ad esempio, non accade nulla di tutto questo.

Added lines 129-168:

Funzione listen

Una volta che il socket è stato creato e assegnato a una porta locale, al server per completare la procedura di apertura passiva non rimane altro che eseguire la funzione listen, che mette il socket in modalità passiva e specifica una lunghezza della coda per le richieste di connessione.

int listen (int sockid, int queuelen)

, dove:

  • sockid è il descrittore intero del socket pronto per essere usato dal server;
  • queuelen è il numero massimo di partecipanti attivi che possono attendere una connessione. Quando il limite viene raggiunto, le richieste di connessione successive vengono ignorate e scartate.

La funzione listen restituisce 0 in caso di successo e -1 in caso di errore.

La listen è non-bloccante, ovvero ritorna subito, e può essere usata solo per socket che supportano le connessioni.

Funzione accept

Completata l'apertura passiva, grazie alla funzione accept il server blocca tutto e si mette in attesa di richieste di connessione.

int accept (int sockid, struct sockaddr *addr, socklen_t *addrlen)

, dove:

  • sockid è il descrittore intero del socket su cui il server ascolta le richieste di connessione;
  • addr è l'indirizzo del client che ha richiesto la connessione (tale campo viene dunque riempito solo a richiesta avvenuta);
  • addrlen è un intero che indica la dimensione di addr in byte.

La funzione accept restituisce un intero positivo in caso di successo (il descrittore del nuovo socket) in caso di successo e -1 in caso di errore.

Vediamo passo passo cosa succede:

  1. la accept estrae da sockid la prima richiesta di connessione che attende in coda. Se non sono presenti, si blocca finché non ne arriva una;
  2. la chiamata della funzione viene completata riempendo l'argomento addr con l'indirizzo del client richiedente e l'argomento addrlen calcolato di conseguenza;
  3. viene creato un nuovo socket con le stesse caratteristiche di sockid, la cui destinazione è connessa al client chiamante. Il descrittore del nuovo socket è quello restituito dalla accept;
  4. viene ritornato il descrittore del nuovo socket al client richiedente.

Mentre vengono eseguiti i punti 3 e 4, il socket sockid originale non viene toccato e resta nello stato di listen.

La funzione accept, lo abbiamo visto, è decisamente bloccante.

Gestione delle richieste

Added lines 15-16:
Changed lines 54-55 from:

La funzione socket ritorna un intero positivo in caso di successo e -1 in caso contrario.

to:

La funzione socket ritorna un intero positivo in caso di successo (che diventerà il descrittore del socket) e -1 in caso di errore.

Changed lines 97-100 from:

La funzione bind restituisce 0 in caso di successo e -1 in caso contrario.

[... segue ...]

to:

La funzione bind restituisce 0 in caso di successo e -1 in caso di errore.

Creazione della connessione

Introduciamo le prossime funzioni di gestione del socket descrivendo le varie fasi di connessione che caratterizzano il protocollo TCP. Le due figure che entrano in gioco sono il partecipante passivo (un server), in fiduciosa attesa che qualcuno gli richieda la connessione, e un partecipante attivo (il client) che effettua tale richiesta.

Ecco in pratica cosa succede:

  1. (lato passivo) Il server esegue l'apertura passiva del socket, attraverso le funzioni socket, bind e listen;
  2. (lato attivo) Il client richiede l'inizio della connessione (apertura attiva) usando la funzione connect;
  3. (lato passivo) Il server accetta la connessione tramite la funzione accept e la sposta su un nuovo socket;
  4. (lato passivo e attivo) Invio e ricezione dei dati.

Funzione connect

Se la bind permetteva a un server di assegnare una porta ad un socket, la connect è la funzione che consente ad un client di collegarsi a quella porta specifica, ponendo un socket nello stato connesso.

int connect (int sockid, const struct sockaddr *destaddr, socklen_t addrlen)

, dove:

  • sockid è il descrittore intero del socket da connettere;
  • destaddr è la struttura che contiene l'indirizzo di destinazione cui il socket si dovrà collegare (indirizzo IP più porta);
  • addrlen è un intero che indica la dimensione dell'indirizzo in byte.

La funzione connect restituisce 0 in caso di successo e -1 in caso di errore.

La connect è inoltre bloccante, ovvero ritorna solo quando la connessione è stabilita o quando si è verificato un errore. Se ciò non avviene, il codice non va avanti.

[...segue...]

Added lines 13-14:
Changed lines 23-24 from:

I socket sono i due punti finali di tale canale, che forniscono l'interfaccia tra i programmi applicativi e il livello di trasporto (TCP, UDP, ...).

to:

I socket sono i due capi di tale canale, che forniscono l'interfaccia tra i programmi applicativi e il livello di trasporto (TCP, UDP, ...).

Changed lines 31-34 from:
  1. se è tutto ok, il server accetta la connessione e crea un nuovo socket "s2" su una porta diversa
  2. il servizio chiamato dal client viene gestito dal server sul socket "s2" (che viene chiuso una volta soddisfatte le richieste del client)
  3. mentre viene eseguito il punto 4, il server è tornato ad ascoltare su "s1" eventuali chiamate da parte di altri client.
to:
  1. quando questa arriva, se è tutto ok il server accetta la connessione e crea un nuovo socket "s2" su una nuova porta
  2. il servizio chiamato dal client viene gestito dal server sul socket "s2" (che verrà poi chiuso una volta soddisfatta la richiesta)
  3. mentre viene eseguito il punto 4, il server è tornato ad ascoltare su "s1" eventuali chiamate da altri client.
Changed lines 39-42 from:

int socket (int pf, int type, protocol)

,dove:

to:

int socket (int pf, int type, protocol)

, dove:

Changed lines 43-48 from:
  • type che consente la scelta del tipo di comunicazione, e che dipende dal protocollo utilizzato tra quelli disponibili nella famiglia scelta sopra. Ogni tipo ha un valore intero, anche in questo caso rappresentato da costanti. Ad esempio:
    • SOCK_STREAM: fornisce un canale di trasmissione bidirezionale, sequenziale e affidabile. Il nome stream deriva dal fatto che i dati vengono trattati come un flusso continuo di byte. Il SOCK_STREAM della PF_INET è TCP;
    • SOCK_DGRAM: usato per trasmettere pacchetti di dati (datagram) senza connessione e con trasmissione non affidabile. Il SOCK_DGRAM della PF_INET è UDP;
    • SOCK_RAW: dà accesso di basso livello ai protocolli di rete e alle varie interfacce. Riservato all'uso di sistema. Il SOCK_RAW della PF_INET è IPv4.
to:
  • type consente la scelta del tipo di comunicazione, che dipende dal protocollo utilizzato tra quelli disponibili nella famiglia scelta sopra. Ogni tipo ha un valore intero, anche in questo caso rappresentato da costanti. Ad esempio:
    • SOCK_STREAM: fornisce un canale di trasmissione bidirezionale, sequenziale e affidabile. Il nome stream deriva dal fatto che i dati vengono trattati come un flusso continuo di byte. Il SOCK_STREAM della PF_INET è TCP;
    • SOCK_DGRAM: usato per trasmettere pacchetti di dati (datagram) senza connessione e con trasmissione non affidabile. Il SOCK_DGRAM della PF_INET è UDP;
    • SOCK_RAW: dà accesso di basso livello ai protocolli di rete e alle varie interfacce. Riservato all'uso di sistema. Il SOCK_RAW della PF_INET è IPv4.
Changed lines 56-57 from:

[segue]

to:

Codifica degli indirizzi

Avrete notato che la funzione socket si disinteressa completamente degli indirizzi che identificano i due punti finali della comunicazione, occupandosi solo di specificare il tipo di famiglia dei protocolli da utilizzare. E' solo attraverso le altre funzioni di gestione del socket (bind, listen, ...) che vengono manipolati gli indirizzi, sotto forma di strutture (passate sempre per riferimento, tramite puntatori).

La struttura generica è la seguente:

struct sockaddr { 
   u_short sa_family; // Address Family
   char sa_data[14];  // Indirizzo (la cui forma dipende dall'AF scelta)
}; 

Va da sé che ogni famiglia di protocolli implementerà in modo diverso questa struttura generica, dal momento che ognuna di esse ha forme di indirizzamento specifiche e distinte. I nomi di tutte queste strutture iniziano per sockaddr_ più un suffisso finale che indica la famiglia di appartenenza.

Vediamo ad esempio la sockaddr_in, ovvero la sockaddr specifica per IP.

struct sockaddr_in { 
   short sin_family;  // Valore: AF_INET
   u_short sin_port;  // Numero di porta. Valore: 0 - 65535
   struct in_addr sin_addr;  // sin_addr è l'indirizzo IP
   char sin_zero[8];  // inutilizzato
}; 

Importante sottolineare come i bit degli indirizzi e dei numeri di porta possano essere ordinati in modo diverso a seconda dell'hardware degli host. Si parla di ordinamento big-endian quando si parte dai bit più significativi (adottato ad esempio da IBM, Sun, Motorola), little-endian quando parte da quelli meno significativi (es. Intel). Questo comporta la necessità di eseguire delle apposite routine di verifica dell'ordinamento ed eventuale riconversione per garantire la portabilità del codice.

Funzione bind

Abbiamo visto che nel momento della loro creazione i socket non sono associati a nessun indirizzo (IP o di porta). Se lato client non è sempre necessario conoscere l'indirizzo locale, lato server è spesso importante specificare la porta in ascolto di connessione. Ecco dunque lo scopo della funzione bind: assegnare un indirizzo locale ad un socket.

int bind (int sockid, const struct sockaddr *localaddr, socklen_t addrlen)

, dove:

  • sockid è il descrittore intero del socket da collegare all'indirizzo, quello ottenuto come valore di ritorno dalla funzione socket;
  • localaddr è la struttura che contiene l'indirizzo locale del socket (indirizzo IP più porta). Per specificare un indirizzo generico si usa il valore INADDR_ANY, che ha valore 0;
  • addrlen è un intero che indica la dimensione dell'indirizzo in byte.

La funzione bind restituisce 0 in caso di successo e -1 in caso contrario.

[... segue ...]

Changed lines 46-49 from:
  • SOCK_RAW: dà accesso di basso livello ai protocolli di rete e alle varie interfacce. Riservato all'uso di

sistema. Il SOCK_RAW della PF_INET è IPv4. Da notare che non tutte le combinazioni "famiglia di protocolli"-"tipo di socket" sono valide (ad esempio la PF_UNIX non ha SOCK_RAW).

to:
  • SOCK_RAW: dà accesso di basso livello ai protocolli di rete e alle varie interfacce. Riservato all'uso di sistema. Il SOCK_RAW della PF_INET è IPv4.
Nota: da notare che non tutte le combinazioni "famiglia di protocolli"-"tipo di socket" sono valide (ad esempio la PF_UNIX non ha SOCK_RAW).
Added lines 1-59:

(:title Socket Library:) Torna alla pagina di Sistemi Anticoncezionali delle Reti e dei Damiani


:: Socket Library ::

Introduzione

In soldoni, perché due processi residenti su macchine diverse possano comunicare tra loro, è necessario:

  1. verificare che le macchine siano in rete
  2. creare un canale di comunicazione

I socket sono i due punti finali di tale canale, che forniscono l'interfaccia tra i programmi applicativi e il livello di trasporto (TCP, UDP, ...).

Come avviene la comunicazione

Cosa siano client e server lo sappiamo tutti (no? Leggi qua), come comunichino (in generale) tramite socket è presto detto:

  1. ho un programma X che gira su un server, ed è associato a una certa porta tramite il socket "s1"
  2. il server continua ad ascoltare su "s1", aspettando che qualche client richieda la connessione
  3. se è tutto ok, il server accetta la connessione e crea un nuovo socket "s2" su una porta diversa
  4. il servizio chiamato dal client viene gestito dal server sul socket "s2" (che viene chiuso una volta soddisfatte le richieste del client)
  5. mentre viene eseguito il punto 4, il server è tornato ad ascoltare su "s1" eventuali chiamate da parte di altri client.

Creare un socket

La funzione necessaria per creare un socket è, manco a dirlo, socket.

int socket (int pf, int type, protocol)

,dove:

  • pf specifica la famiglia di protocolli da usare con il socket, che si rifletterà sul modo in cui verranno interpretati gli indirizzi forniti. Ogni famiglia è rappresentata da una costante intera, alla quale è associato un nome simbolico per facilitare la memorizzazione e aumentare la chiarezza. Per convenzione questi nomi iniziano per PF_ (Protocol Family) o AF_ (Address Family), la cui scelta nella pratica è del tutto equivalente. Ad esempio: PF_INET (per il TCP/IP) o PF_UNIX (per il file system UNIX).
  • type che consente la scelta del tipo di comunicazione, e che dipende dal protocollo utilizzato tra quelli disponibili nella famiglia scelta sopra. Ogni tipo ha un valore intero, anche in questo caso rappresentato da costanti. Ad esempio:
    • SOCK_STREAM: fornisce un canale di trasmissione bidirezionale, sequenziale e affidabile. Il nome stream deriva dal fatto che i dati vengono trattati come un flusso continuo di byte. Il SOCK_STREAM della PF_INET è TCP;
    • SOCK_DGRAM: usato per trasmettere pacchetti di dati (datagram) senza connessione e con trasmissione non affidabile. Il SOCK_DGRAM della PF_INET è UDP;
    • SOCK_RAW: dà accesso di basso livello ai protocolli di rete e alle varie interfacce. Riservato all'uso di

sistema. Il SOCK_RAW della PF_INET è IPv4. Da notare che non tutte le combinazioni "famiglia di protocolli"-"tipo di socket" sono valide (ad esempio la PF_UNIX non ha SOCK_RAW).

  • protocol, il protocollo. Essendo in genere indicato implicitamente nel parametro type, ha quasi sempre valore 0.

La funzione socket ritorna un intero positivo in caso di successo e -1 in caso contrario.

Esempio: sd = socket (AF_INET, SOCK_STREAM, 0);

[segue]


Torna alla pagina di Sistemi Anticoncezionali delle Reti e dei Damiani