Corso di Reti di Calcolatori

61
Corso di Reti di Calcolatori Interfaccia socket

description

Corso di Reti di Calcolatori. Interfaccia socket. Introduzione. La programmazione di rete è ciò che permette a due o più computer collegati da una rete fisica di comunicare tra loro. Per poter comunicare con un computer ho bisogno di: - PowerPoint PPT Presentation

Transcript of Corso di Reti di Calcolatori

Page 1: Corso di Reti di Calcolatori

Corso di Reti di Calcolatori

Interfaccia socket

Page 2: Corso di Reti di Calcolatori

[2]

Introduzione

• La programmazione di rete è ciò che permette a due o più computer collegati da una rete fisica di comunicare tra loro.

• Per poter comunicare con un computer ho bisogno di:

1. Un indirizzo a cui connettermi: ogni computer connesso in una rete dispone di un indirizzo univoco, che consente di localizzarlo a livello globale

2. Una porta aperta: il concetto di porta permette ad un computer di ricevere diverse connessioni, infatti tramite diverse porte è possibile dallo stesso computer fornire diversi servizi (posta, web, telnet, ecc.)

3. Un protocollo: che permetta di far capire al programma in ascolto le mie richieste

Page 3: Corso di Reti di Calcolatori

[3]

TCP/IP

• TCP/IP non include una definizione di API.

• C’è in giro una quantità di API da usare col TCP/IP:

– Sockets– TLI, XTI– Winsock– MacTCP

Page 4: Corso di Reti di Calcolatori

[4]

Funzioni necessarie:

• Specificare gli endpoints, locale e remoto, della comunicazione

• Initiare la connessione

• Attendere per le connessioni in arrivo

• Send e receive dei dati

• Terminare la connessione ‘gracefully’

• Gestione degli Errori

Page 5: Corso di Reti di Calcolatori

[5]

Comunicazione

• La comunicazione tra due pc funziona in questo modo:– un CLIENT si collega ad un SERVER chiedendo un servizio – il SERVER, se dispone del servizio, assolverà la richiesta del

CLIENT, inviandogli le risposte.

• Questa comunicazione avviene tramite un protocollo di comunicazione (insieme di regole da rispettare).

• Al di sopra di questi protocolli esiste un livello di astrazione, chiamato socket, che permette di trasmettere e ricevere informazioni senza doversi preoccupare della correttezza delle regole definite dal protocollo utilizzato.

Page 6: Corso di Reti di Calcolatori

[6]

Indirizzamento

• Ogni computer della rete è identificato da un hostname e da un indirizzo IP unico a 32 bit es: “www.google.com” 209.85.135.103

• L’indirizzo IP contiene sia l’identificativo della rete che quello della macchina

• Il comando nslookup offre la lista di tutti i server alias per un indirizzo.

– Esempio

>>> nslookup www.google.comRisposta da un server non di fiducia:Nome: www.l.google.comAddresses: 209.85.135.99, 209.85.135.103, 209.85.135.147, 209.85.135.104

Aliases: www.google.com

Page 7: Corso di Reti di Calcolatori

[7]

Come identificare i processi ?

• Si assegna ad ogni processo un numero di porta

• I numeri di porta sono assegnati a servizi – di sistema (porte da 0-1023)– dinamici o privati (1024 - 65535)

• I server (daemon) usano di solito porte ben note– es HTTP = 80 , Telnet = 25, FTP = 21

• I client di solito usano porte dinamiche

• Questa associazione viene usata dai protocolli di livello transport come TCP o UDP

Page 8: Corso di Reti di Calcolatori

[8]

TCP/IP: interfaccia di programmazione

Page 9: Corso di Reti di Calcolatori

[9]

Socket

• Un socket è un punto finale di un collegamento bidirezionale tra due programmi in esecuzione sulla rete.

• Un socket è legato a un numero di porta così che il livello TCP può identificare l’applicazione a cui i dati devono essere inviati.– Usando i socket la comunicazione in rete è molto simile all’ I/O su

file. La gestione dei socket viene trattata come la gestione di file. I comandi di lettura e scrittura sui flussi usati per l’I/O su file sono

applicabili anche all’I/O su socket.– La comunicazione basata su socket è indipendente dal linguaggio di

programmazione.– Un programma socket scritto nel linguaggio Java può comunicare

via rete con un programma non Java purché questo usi i socket.

Page 10: Corso di Reti di Calcolatori

[10]

Concetto di Socket

• Per attivare una connessione fra due processi si utilizza dunque lo schema Host-Port– il server è eseguito su un host e aspetta richieste su una

determinata porta– il client viene eseguito su un host e richiede l’uso di una porta per

effettuare richieste ad un server

• Specificando ora anche il protocollo per la comunicazione riusciamo a descrivere univocamente la connessione con una quintupla detta Association:

(proto, ind_locale, proc_locale, ind_remoto, proc_remoto)

es: (TCP, 123.23.4.221, 1500, 234.151.124.2, 4000)

Page 11: Corso di Reti di Calcolatori

[11]

Concetto di Socket

• La quintupla viene in realtà costituita a partire da due elementi simmetrici, un’associazione locale ed una remota:– Protocollo, Ind_locale, proc_locale

(TCP, 123.23.4.221,1500)

– Protocollo, Ind_remoto, proc_ remoto (TCP ,234.151.124.2,4000)

• Ciascuna di queste parti è detta socket

– Nota: 1040 per TCP 1040 UDP

Page 12: Corso di Reti di Calcolatori

[12]

Concetto di Socket

Quindi un socket è

1. un descrittore di risorsa che consente ad una applicazione di effettuare operazioni di lettura e scrittura verso un particolare dispositivo di I/O.

2. Astrazione di un canale di comunicazione tra processi distribuiti in rete

3. Interfaccia per la suite di protocolli TCP/IP

4. Punto di arrivo nella comunicazione in rete

Page 13: Corso di Reti di Calcolatori

[13]

Il protocollo TCP e l’interfaccia socket

• Nel modello TCP/IP l’interfaccia socket svolge tutte le operazioni necessarie per stabilire una connessione, ovvero:

– si occupa di creare i pacchetti TCP

– stabilisce una connessione con l’algoritmo del “three way handshake”

– gestisce tutte le problematiche della trasmissione dei pacchetti

Page 14: Corso di Reti di Calcolatori

[14]

Creazione di un socket

• Lato server– Creazione del socket– Bind ad una porta– Listen, predisposizione a ricevere sulla porta– Accept, blocca il server in attesa di una connesione– Lettura - scrittura dei dati– Chiusura

• Lato client– Creazione del socket– Richiesta di connessione– Lettura - scrittura dei dati– Chiusura

Page 15: Corso di Reti di Calcolatori

[15]

Comunicazione socket (1)

• Un server (programma) gira su un computer specifico e ha un socket legato ad una porta specifica.

• Un server aspetta e ascolta sul socket finché un client non esegue una richiesta di connessione.

Page 16: Corso di Reti di Calcolatori

[16]

Comunicazione socket (2)

• Se tutto funziona, il server accetta la connessione e ottiene dal sistema operativo un nuovo socket legato a una porta diversa.

– In questo modo un server può continuare ad ascoltare sul socket originale per eventuali altre richieste di connessione, mentre comunica con il client già connesso.

Page 17: Corso di Reti di Calcolatori

[17]

Indirizzi, porte e socket

• La metafora della posta– L’utente è l’applicazione.– Il suo indirizzo di casa è l’indirizzo IP.– La casella postale è la porta.– L’ufficio postale è la rete.– Il socket è la chiave che dà all’utente l’accesso alla

casella postale giusta.

• Nota: si presuppone che anche la posta in uscita venga depositata dall’utente nella sua casella postale invece di imbucarla in una buca delle lettere.

Page 18: Corso di Reti di Calcolatori

[18]

L'interfaccia dei socket • La socket API (Application Program Interface) è l'interfaccia

standard tra i programmi applicativi e i protocolli TCP/IP

• La base per l'I/O di rete dell'API è costituita da una generalizzazione del meccanismo UNIX di accesso ai file che invece di un descrittore di file fornisce un terminale di comunicazione.

• Quando un programma applicativo crea un socket e stabilisce una connessione TCP dal socket a una destinazione esterna, può usare write per inviare un flusso di dati attraverso la connessione (il programma all'altro lato può usare read per riceverla).

• Per rendere possibile usare le primitive come read e write sia con i file sia con i socket, il sistema operativo si assicura che se un intero è stato assegnato come descrittore di file, non sarà assegnato anche come descrittore di socket.

Page 19: Corso di Reti di Calcolatori

[19]

Creiamo un socket#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

• Ma cosa sono gli argomenti?1. domain deve essere posto a "PF_INET".2. type dice al kernel che tipo di socket è:

SOCK_STREAM o SOCK_DGRAM.

3. protocol = "0" per avere la socket()

• socket() restituisce – un “socket descriptor” o – -1 in caso di errore.

Page 20: Corso di Reti di Calcolatori

[20]

La specificazione di un indirizzo locale

• Inizialmente i socket vengono creati senza alcuna associazione agli indirizzi locale o di destinazione.

• In molti casi i programmi applicativi non si preoccupano dell'indirizzo locale che usano e vogliono consentire al software del protocollo di sceglierne uno per loro conto.

• Tuttavia, i server che funzionano su una porta ben nota devono essere in grado di indicare la porta al sistema.

• Una volta creato il socket, il server usa la funzione bind per specificare un indirizzo locale. Bind ha la forma seguente:– bind(socket, localaddr, addrlen)

• L'argomento socket è il descrittore intero del socket da collegare all'indirizzo, localaddr è una struttura che contiene l'indirizzo locale a cui il software dovrebbe essere collegato e addrlen è la lunghezza di quest’ultima

Page 21: Corso di Reti di Calcolatori

[21]

Funzione bind -- What port am I on?

• Associa e può riservare (in modo esclusivo) una porta TCP o UDP a uso di un socket.

int status = bind(sockid, &addrport, size);

– status: = -1, se bind non ha avuto successo;– sockid: descrittore del socket, intero;– addrport: struct sockaddr, l’indirizzo (IP) e la porta della

macchina; L’indirizzo di solito è impostato a INADDR_ANY

– size: le dimensioni (in byte) della addrport structure.

• Può essere evitata per entrambi i tipi di socket.

Page 22: Corso di Reti di Calcolatori

[22]

Uso di bind

SOCK_DGRAM (UDP)– Se si spedisce solamente, bind non serve.

Il sistema operativo trova lui una porta ogni volta che il socket invia un pkt.

– Se si riceve, bind è indispensabile.

SOCK_STREAM (TCP)– Destinazione determinata durante il setup della

connessione (three-way handshake).– Non è necessario conoscere la porta da cui avviene

l’invio. Durante il setup della connessione, il lato ricevente viene

informato del numero di porta.

Page 23: Corso di Reti di Calcolatori

[23]

Chiamata della funzione connect-- Hey, you!

int status = connect(sock, &name, &namelen);

– status: 0 se connect ha successo, altrimenti -1 ;– sock: intero, il socket che deve essere usato nella

connessione;– name: struct sockaddr, indirizzo del partecipante

passivo;– namelen: intero, sizeof(name).

• “connect” è bloccante.

Page 24: Corso di Reti di Calcolatori

[24]

listen()--Will somebody please call me?

• Come si comporta un server in attesa di ‘incoming connections’ e come le gestisce ?

• Il processo si svolge in due step: first listen(), then accept()

int listen(int sockfd, int backlog);

– sockfd: è il solito ‘socket file descriptor’ restituito dalla socket() system call.

– backlog: è il numero di connessioni contemporanee permesse sulla coda di ingresso.

• Che cosa vuole dire ? – Le ‘incoming connections’ restano in una coda di attesa finchè non

le si accept(). – Solitamente il limite delle connessioni accodate è tra 5 e 20

connessioni.

Page 25: Corso di Reti di Calcolatori

[25]

listen

• Come si può facilmente immaginare, c’è bisogno di chiamare bind() prima di chiamare listen() o il kernel sarà in ascolto su una porta random.

• Listening per connessioni in arrivo, la sequenza delle system calls è:

socket();

bind();

listen();

/* accept() goes here */

Page 26: Corso di Reti di Calcolatori

[26]

accept()--"Thank you for calling port 3490."

• Cosa succede ?1. Qualcuno, da remoto cerca di connect() alla vs.

macchina su una porta sulla quale siete in listen()ing.2. La connessione viene up waiting to be accept()ed.3. You call accept() and you tell it to get the pending

connection.4. It'll return to you a brand new socket file descriptor to

use for this single connection! That's right, suddenly you have two socket file descriptors for the price of one!

5. The original one is still listening on your port and the newly created one is finally ready to send() and recv(). We're there!

Page 27: Corso di Reti di Calcolatori

[27]

sendto() e recvfrom() -- Talk to me, DGRAM style

• Poichè i datagram sockets non sono connessi ad un host remoto, qual sarà l’informazione necessaria da fornire prima di inviare il pacchetto ?

• That's right! l’indirizzo IP di destinazione ! :

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, socklen_t tolen);

• Questa call è la stessa del caso send() con l’aggiunta di due informazioni.– to è un pointer ad una struct sockaddr che contiene l’ IP address di

destinazione e la porta.– tolen, ponetela a sizeof(struct sockaddr).

Page 28: Corso di Reti di Calcolatori

[28]

close() e shutdown() -- Get outta my face!

• Whow! Abbiamo fatto send() e recv() di dati per tutto il tempo, adesso siamo stufi. Siamo pronti a chiudere la connessione e a liberare il ns. socket descriptor. Facile: possiamo usare la funzione regolare di Unix per chiudere un file:

close(sockfd);

• Questo inibirà ogni read/write addizionale sul socket. Chiunque tenterà letture o scritture sul socket del sistema remoto riceverà un errore.

Page 29: Corso di Reti di Calcolatori

[29]

Shutdown• Se si vuole un maggiore controllo su come il socket viene

chiuso, si può usare la funzione shutdown(). Questa funzione permette di chiudere la comunicazione solo in una direzione, o in entrambe (come close())

• Synopsis:

int shutdown(int sockfd, int how);

– sockfd è il socket_descriptor che volete chiudere, e how è un valore tra: 0 – la ricezione è disabilitata 1 – la trasmissione è disabilitata 2 – sono disabilitate entrambe (come close())

• Nota: shutdown() non chiude il file_descriptor – ne cambia l’uso. Per liberare il socket_descriptor, usare close().

Page 30: Corso di Reti di Calcolatori

[30]

TCP Server–Client: Interazione

Page 31: Corso di Reti di Calcolatori

[31]

UDP Server–Client: Interazione

Page 32: Corso di Reti di Calcolatori

[32]

getpeername()--Who are you?

• La funzione getpeername() vi dice chi c’è all’altro capo di uno stream socket (connesso).

• Synopsis:

#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

• Una volta recuperato l’indirizzo, si può usare inet_ntoa() o gethostbyaddr() per avere maggiori informazioni.

Page 33: Corso di Reti di Calcolatori

[33]

gethostname() -- Who am I?

• Ancora più facile di getpeername() è la funzione gethostname(): restituisce il nome del computer su cui sta girando il vs. programma. Il nome può essere usato da gethostbyname() per determinare l’indirizzo IP della macchina locale.

• Ecco un esempio:

#include <unistd.h>

int gethostname(char *hostname, size_t size);

Page 34: Corso di Reti di Calcolatori

[34]

DNS -- You say "whitehouse.gov", I say 63.161.169.137

• "DNS sta per "Domain Name Service". In poche parole, voi dite al DNS un nome di un indirizzo (human-readable) di un sito, e lui vi restituisce l’ IP address (lo potete usare con bind(), connect(), sendto(), o per quello che vi serve.)

• Pertanto, se uno scrive:

– $ telnet whitehouse.gov

• telnet scoprirà che ha bisogno di connect()-ersi a "63.161.169.137".

• Ma come funziona ? Basta usare la funzione gethostbyname():

#include <netdb.h>

struct hostent *gethostbyname(const char *name);

Page 35: Corso di Reti di Calcolatori

[35]

send() e recv() -- Talk to me, baby!

• Queste due funzioni sono per la comunicazione su uno stream sockets o un datagram sockets di tipo connesso.

• Se volete usare i regolari unconnected datagram sockets, riferirsi alle funzioni sendto() e recvfrom().

• La send() call:

•int send(int sockfd, const void *msg, int len, int flags);

Page 36: Corso di Reti di Calcolatori

[36]

Esempio di Codice

char *msg = "Beej was here!";

int len, bytes_sent;

.

.

.

len = strlen(msg);

bytes_sent = send(sockfd, msg, len, 0);

Page 37: Corso di Reti di Calcolatori

[37]

recv

• La recv() call è simile:

int recv(int sockfd, void *buf, int len, unsigned int flags);– sockfd è il socket_descriptor da cui leggere,– buf è il buffer su cui memerizzare le informazioni,– len è la massima lunghezza del buffer e– flags può essere settato a 0.

• recv() restituisce il numero di bytes letti nel buffer, o -1 in caso di errore (errno viene settata di conseguenza.)

• recv() può restituire 0. Questo vuole dire una sola cosa: il sito remoto ha chiuso la connessione ! Il valore di 0 è il modo di recv() di informarvi su questo evento.

Page 38: Corso di Reti di Calcolatori

[38]

Invio e ricezione di dati (1)

• Con una connessione TCP (SOCK_STREAM)int count = send(socket, &buf, len, flags);– count: numero di byte trasmessi (-1 in caso di errore);– buf: char [], buffer che devono essere trasmessi;– len: intero, lunghezza di buffer (in byte) da trasmettere;– flags: intero, opzioni speciali, di solito solo 0.

int count = recv(socket, &buf, len, flags);– count: numero di byte ricevuti (-1 in caso di errore);– buf: void [], memorizza i byte ricevuti;– len: numero di byte ricevuti;– flags: intero, opzioni speciali, di solito solo 0.

• Le chiamate sono bloccanti: la chiamata ritorna solo dopo che i dati sono stati inviati (al socket buffer) o ricevuti.

Page 39: Corso di Reti di Calcolatori

[39]

Invio e ricezione di dati (2)

• Senza connessione, via UDP (SOCK_DGRAM)

int count = sendto(sock, &buf, len, flags, &addr, addrlen);– count, sock, buf, len, flags: uguale all’invio;– addr: struct sockaddr, indirizzo della destinazione;– addrlen: sizeof(addr).

int count = recvfrom (sock, &buf, len, flags, &name, namelen);– count, sock, buf, len, flags: uguale alla ricezione;– name: struct sockaddr, indirizzo della sorgente;– namelen: sizeof(name): parametro valore/risultato.

• Le chiamate sono bloccanti: la chiamata ritorna solo dopo che i dati sono stati inviati (dal buffer del socket) o ricevuti.

Page 40: Corso di Reti di Calcolatori

[40]

Funzione close

• Una volta terminato l’uso di un socket, lo si dovrebbe chiudere.

status = close(s)– status: 0 se l’operazione ha avuto successo, -1 in caso di

errore;– s: il descrittore di file (il socket che viene chiuso).

• La chiusura di un socket:– chiude la connessione TCP (nel caso SOCK_STREAM);– libera la porta usata dal socket.

Page 41: Corso di Reti di Calcolatori

[41]

Esercizio inaddr

Page 42: Corso di Reti di Calcolatori

[42]

Winsock

• Il winsock si basa su queste principali sette funzioni:

1. Socket(): crea una socket che verrà utilizzata per la comunicazione

2. Connect(): collega la socket ad un indirizzo remoto3. Read(), Write(); Send(), Sendto(), Recv(), Recvfrom():

funzioni di invio e ricezione dati4. Listen(): specifica le code di socket client5. Accept(): attende delle connessioni in una specifica

porta6. Bind(): collega la socket ad una porta specifica7. Close(): chiude la socket

Page 43: Corso di Reti di Calcolatori

[43]

Programmare le socket

• Otto sono i passi fondamentali per programmare le socket in windows:

– 1. Definizione delle librerie da utilizzare (Winsock.h)– 2. Inizializzazione della libreria socket (WSAStartup())– 3. Creazione della socket– 4. Descrizione delle caratteristiche della socket– 5. Connessione al sistema remoto– 6. Operazioni sulla socket– 7. Chiusura della socket– 8. Rilascio delle risorse della libreria (WSACleanup() )

Page 44: Corso di Reti di Calcolatori

[44]

L’ereditarietà dei socket• UNIX usa le chiamate di sistema fork ed exec per attivare

nuovi processi.

• È una procedura in due fasi: nella prima si crea una copia del programma in esecuzione al momento, mentre nella seconda la nuova copia è sostituita dal programma applicativo desiderato.

• Quando un programma chiama fork, la copia appena creata eredita l'accesso a tutti i socket aperti, così come l'accesso a tutti i file aperti. Quando un programma chiama exec la nuova applicazione mantiene l'accesso a tutti i socket aperti.

• I server principali usano l'ereditarietà dei socket quando creano server secondari per gestire una specifica connessione.

Page 45: Corso di Reti di Calcolatori

[45]

La struttura sockaddr• Si usa quando si passa un indirizzo TCP/IP all'interfaccia

dei socket.

• inizia con un campo ADDRESS FAMILY a 16 bit che identifica la famiglia di protocolli a cui appartiene l'indirizzo. È seguita da un indirizzo di 14 ottetti al massimo.

• Il valore nel campo ADDRESS FAMILY determina il formato degli ottetti dell'indirizzo rimanente. Ad esempio, il valore 25 nel campo ADDRESS FAMILY significa che gli ottetti degli indirizzi rimanenti contengono un indirizzo TCP/IP. Ogni famiglia di protocolli definisce come userà gli ottetti nel campo degli indirizzi.

• Per gli indirizzi TCP/IP l'indirizzo dei socket è noto come sockaddr_in e comprende sia l'indirizzo IP sia il numero di porta

Page 46: Corso di Reti di Calcolatori

[46]

La funzione gestsockopt

• La funzione gestsockopt consente all'applicazione di richiedere informazioni su un socket. La chiamata ha la forma seguente:– getsockopt(socket, level, optionid, optionval, lenght)

L'argomento socket specifica il socket per cui sono necessarie le informazioni,

level identifica se l'operazione è applicata al socket stesso o ai protocolli sottostanti e

optionid stabilisce l’opzione a cui si applica la richiesta. La coppia di argomenti optionval e lenght specifica due puntatori.

Il primo contiene l'indirizzo del buffer in cui il sistema pone il valore richiesto e il secondo l'indirizzo di un intero in cui il sistema pone la lunghezza del valore dell'opzione.

Page 47: Corso di Reti di Calcolatori

[47]

La funzione setsockopt

• La funzione setsockopt consente al programma applicativo di impostare un'opzione di un socket usando l'insieme di valori ottenuto con getsockopt. Il chiamante specifica il socket e il nuovo valore dell'opzione. La chiamata a setsockpt ha la forma seguente:– setsockpt(socket, level, optionid, optionval, lenght)

• Gli argomenti sono come quelli di getsockopt, tranne lenght che contiene la lunghezza dell'opzione che viene passata al sistema

Page 48: Corso di Reti di Calcolatori

[48]

Accettazione delle connessioni

• Come si è visto, i server usano le funzioni socket, bind e listen per creare un socket, collegarlo a una porta di protocollo ben nota e specificare una lunghezza della coda per le richieste di connessione.

• La chiamata a bind associa il socket a una porta di protocollo ben nota, ma il socket non è collegato a una destinazione esterna specifica. La destinazione viene specificata con un carattere jolly consentendo al socket di ricevere richieste di connessione da client arbitrari.

• Una volta che il socket è stato attivato, il server deve aspettare una connessione; per eseguire ciò utilizza la funzione accept

Page 49: Corso di Reti di Calcolatori

[49]

Un client di esempio

• Il seguente programma C di esempio illustra come usare l'API socket da programmare per accedere ai protocolli TCP/IP. È una semplice implementazione di un client e di un server whois.

• Come definito nella RFC 954, il servizio whois consente a un client di ottenere informazioni su un utente su un sistema remoto.

• In questa implementazione, il client è un programma applicativo che l’utente attiva con due argomenti: il nome di una macchina remota e il nome dell'utente di quella macchina su cui si desiderano le informazioni.

Page 50: Corso di Reti di Calcolatori

[50]

Un client d’esempio (II)

• Il client chiama gethostbyname per tradurre il nome della macchina remota nell'indirizzo IP e getservbyname per trovare la porta ben nota per il servizio whois.

• Una volta che ha risolto i nomi di servizio e di host, il client crea un socket, specificando che il socket userà la consegna affidabile a flusso (cioè, TCP).

• Infine, il client collega il socket alla porta del servizio whois sulla macchina di destinazione specificata.

Page 51: Corso di Reti di Calcolatori

[51]

Codice del client (1)/* whoisclient.c - main */

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

/*-----------------------------------------------------------

* Programma: whoisclient

* Scopo: programma applicativo UNIX che diventa un client

* per il servizio Internet "whois".

* Utilizzo: whois hostname username

* Autore: Barry Shein, Università di Boston

* Data: Tanto tempo fa in un universo molto lontano

*------------------------------------------------------------

*/

Page 52: Corso di Reti di Calcolatori

[52]

Codice del client (2)main(argc, argv)

int argc; /* dicharazioni di argomenti UNIX standard */

char *argv[];

{

int s; /* descrittore di socket */

int len; /* lunghezza dei dati ricevuti */

struct sockaddr_in sa; /* struttura Internet socket addr.*/

struct hostent *hp; /* risultato della ricerca del nome dell'host */

struct servent *sp; /* risultato della ricerca del servizio */

char buf[BUFSIZ+1]; /* buffer per leggere informazioni whois */

char *myname; /* puntatore al nome di questo programma */

char *host; /* puntatore al nome dell'host remoto */

char *user; /* puntatore al nome dell'utente remoto */

myname = argv[0];

Page 53: Corso di Reti di Calcolatori

[53]

Codice del client (3)/* Controlla che vi siano due argomenti sulla riga di comando */

if(argc != 3) {fprintf(stderr, "Usage: %s host username\n", myname);

exit(1);}

host = argv[1];

user = argv[2];

/* Cerca il nome di host specificato */

if((hp = gethostbyname(host)) == NULL) { fprintf(stderr,"%s: %s: no such host?\n", myname, host);

exit(1);}

/* Pone l'indirizzo dell'host e il tipo di indirizzo nella struttura socket */

bcopy((char *)hp->h_addr, (char *)&sa.sin_addr, hp->h_length);

sa.sin_family = hp->h_addrtype;

/* Cerca il numero di socket per il servizio WHOIS */

if((sp = getservbyname("whois","tcp")) == NULL) { fprintf(stderr,"%s: No whois service on this host\n", myname);

exit(1);}

/* Pone il numero di socket whois nella struttura socket. */

sa.sin_port = sp->s_port;

Page 54: Corso di Reti di Calcolatori

[54]

Codice del client (4)/* Assegna un socket aperto */

if((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)

{perror("socket"); exit(1);}

/* Si connette al server remoto */

if(connect(s, &sa, sizeof sa) < 0)

{perror("connect"); exit(1);}

/* Invia la richiesta */

if(write(s, user, strlen(user)) != strlen(user))

{ fprintf(stderr, "%s: write error\n", myname); exit(1);}

/* Legge la risposta e la mette nell'output dell'utente */

while( (len = read(s, buf, BUFSIZ)) > 0)

write(1, buf, len);

close(s);

exit(0);}

Page 55: Corso di Reti di Calcolatori

[55]

Un server di esempio

• Il server di esempio è solo leggermente più complicato del client.

• Il server ascolta la porta ben nota "whois" e ritorna le informazioni richieste in risposta alle interrogazioni dei client.

• Le informazioni vengono prese dal file delle password UNIX sulla macchina del server.

Page 56: Corso di Reti di Calcolatori

[56]

Un server di esempio (2)/* whoisserver.c - main */

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <pwd.h>

/*-----------------------------------------------------------

* Program: whoisserver

* Scopo: programma applicativo UNIX che funge da server per

* il servizio "whois" sulla macchina locale. Ascolta la

* porta ben nota WHOIS (43) e risponde alle interrogazioni

* dei client. Questo programma deve essere eseguito con i

* privilegi del super-user.

* Utilizzo: whois hostname username

* Autore: Barry Shein, Università di Boston

* Data: Tanto tempo fa in un universo molto lontano

*------------------------------------------------------------*/

Page 57: Corso di Reti di Calcolatori

[57]

Un server di esempio (3)#define BACKLOG 5 /* # di richieste che vogliamo accodare */

#define MAXHOSTNAME 32 /* lunghezza massima del nome di host */

main(argc, argv)

int argc; /* dichiarazioni di argomenti UNIX standard */

char *argv[];

{ int s, t; /* descrittori di socket */

int i; /* intero */

struct sockaddr_in sa, isa; /* struttura socket address */

struct hostent *hp; /*risultato della ricerca del nome dell'host */

char *myname; /* puntatore al nome di questo programma */

struct servent *sp; /* risultato della ricerca del servizio */

char localhost[MAXHOSTNAME+1];/* nome di host locale */

Page 58: Corso di Reti di Calcolatori

[58]

Un server di esempio (4)myname = argv[0];

/*Consulta l'immissione relativa al servizio WHOIS */

if((sp = getservbyname("whois","tcp")) == NULL)

{fprintf(stderr, "%s: No whois service on this host\n", myname); exit(1);}

/*Prende le informazioni sull'host locale */

gethostname(localhost, MAXHOSTNAME);

if((hp = gethostbyname(localhost)) == NULL)

{fprintf(stderr, "%s: cannot get local host info?\n", myname); exit(1);}

/* Pone il numero di socket WHOIS e le informazioni

* sugli indirizzi nella struttura socket */

sa.sin_port = sp->s_port;

bcopy((char *)hp->h_addr, (char *)&sa.sin_addr, hp->h_length);

sa.sin_family = hp->h_addrtype;

Page 59: Corso di Reti di Calcolatori

[59]

Un server di esempio (5)/* Assegna un socket aperto per connessioni in ingresso */

if((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)

{perror("socket"); exit(1);}

/* Associa il socket alla porta per rilevare le connessioni in ingresso */

if(bind(s, &sa, sizeof sa) < 0) {perror("bind");exit(1);}

/* Imposta il numero massimo di connessioni tralasciate */

listen(s, BACKLOG);

/* Entra in un ciclo infinito aspettando nuove connessioni */

while(1) {

i = sizeof isa;

/* Restiamo in accept() mentre aspettiamo nuovi client */

if((t = accept(s, &isa, &i)) < 0) { perror("accept"); exit(1); }

whois(t); /* esegue il servizio WHOIS effettivo */

close(t);

}

}

Page 60: Corso di Reti di Calcolatori

[60]

Un server di esempio (6)/* Riceve la richiesta WHOIS dall'host remoto e invia una risposta */

whois(sock)

int sock;

{ struct passwd *p;

char buf[BUFSIZ+1];

int i;

/* Riceve una richiesta di una linea */

if((i = read(sock, buf, BUFSIZ)) <= 0) return;

buf[i] = '\0'; /* Terminatore null */

/* Cerca l'utente richiesto e formatta la risposta */

if((p = getpwnam(buf)) == NULL) strcpy(buf,"User not found\n");

else sprintf(buf, "%s: %s\n", p->pw_name, p->pw_gecos);

/* Restituisce la risposta */

write(sock, buf, strlen(buf));

return; }

Page 61: Corso di Reti di Calcolatori

[61]

Conclusioni

• Poiché il software del protocollo TCP/IP risiede all'interno del sistema operativo, l'interfaccia esatta tra un programma applicativo e i protocolli TCP/IP dipende dai dettagli del sistema operativo e non è specificata dallo standard TCP/IP.

• I socket hanno adottato il paradigma UNIX open-read-write-close.

• L'API socket, che in origine fu progettata per BSD UNIX, ma è diventata di fatto uno standard utilizzato anche da fornitori come la Microsoft.