Torna alla pagina di Sistemi per l'elaborazione delle informazioni
:: Temi d'esame di Sistemi - 9/9/2005 ::
Esercizio 1
Un server TCP a connessione multipla è stato realizzato usando la socket library come segue:
listen(sd,5); /* AL PIU’ 5 CONNESSIONI */
do
{
nsd = accept(sd,&(work.s),&addrlen);
pid = fork();
if (pid == 0)
{
/* QUI IL PROCESSO FIGLIO GESTISCE IL DIALOGO USANDO IL DESCRITTORE 'nsd' */
close(nsd);
exit(0); /* end of child process */
}
else close(nsd); /* IL PADRE NON USA 'nsd' */
} while(1);
Fornire lo pseudocodice di un’implementazione alternativa che usi la chiamata select() supponendo che il numero massimo dei possibili client sia pari a 3 e che il loro indirizzo di rete e porta siano noti a priori.
SOLUZIONE
Lato server dovrò creare tre socket e memorizzarli in un array sockset. A questo punto faccio la select:
status = select (0, &sockset, NULL, NULL, NULL);
// INIZIO CICLO SUI SOCKET DI SOCKSET
//per capire quali socket hanno richiesto l'accesso scorro l'array di sockset
//e si applica fd_isset, che restituisce un valore non zero se il socket è stato marcato
if (FD_ISSET (sockset[i], &sockset)) //verifica per ogni socket che siano arrivati dati
handleTCPservice(socket[i]); //...se sì, chiama la funzione richiesta
// FINE CICLO
Commenti al codice:
- select:
- il primo parametro, pur essendo definito come il numero di descrittori da esaminare, è posto a 0 perché nelle Winsock viene ignorato;
- l'ultimo NULL passato come parametro è relativo al timeout, ed è così settato perché do per scontato che non ci siano richieste in attesa;
- è una chiamata bloccante, in quanto rileva solo la presenza dei dati sul primo socket pronto;
- evito il controllo sulla corretta esecuzione della select, dando per scontato che qualcuno effettui una connessione su uno dei socket del sockset);
- FD_ISSET, restituisce un valore non zero se il socket esaminato è stato marcato. Quindi permette di capire quali socket hanno richiesto di essere serviti;
- handleTCPservice() è una pseudo-funzione che si applica a un qualsiasi servizio richiesto dal client.
Da notare che se con la accept avevo un unico socket attivo, con la select ne avrò più di uno contemporaneamente. Inoltre, se la accept garantiva la simultaneità grazie alla fork (più client serviti alla volta), con la select ho invece un unico processo in esecuzione (che può dunque servire un solo handleTCPservice per volta).
Tuttavia, nulla mi impedisce di utilizzare una fork anche nella select. In questo modo avrei sì la simultaneità, ma non è poi così indispensabile dato che usiamo la select quando ci sono pochi socket da gestire. Può avere un senso solo quando il tempo di servizio è alto, così invece di dover eseguire più volte lo stesso servizio (facendo aspettare gli altri client) lo smisto su processi diversi simultaneamente.
Come si implementa?
// CICLO PRIMARIO DI SELECT
status = select (0, &sockset, NULL, NULL, NULL);
// CICLO SECONDARIO SUI SOCKET DI SOCKSET
for(i=0; i<=2; i++) //scandisco i tre socket e...
if(FD_ISSET (sockset[i], &sockset)) //...se c'è almeno un socket pronto...
if((c = fork()) == 0) //...fai una fork, e nel processo figlio...
handleTCPservice(sockset[i]); //...esegui il servizio chiamato
//il socket verrà chiuso dentro la procedura di servizio
// FINE CICLO SECONDARIO
// FINE CICLO PRIMARIO
Esercizio 2
La più semplice API verso il DNS è quella fornita dalla socket library con le chiamate gethostbyname() e gethostbyaddr(). Spiegatene il significato e usatele per scrivere lo pseudocodice di un programma che fornisca la stessa funzionalità di query diretta del comando nslookup.
SOLUZIONE
Le API sono l'Interfaccia di Programmazione di un'Applicazione, ovvero un insieme di procedure disponibili al programmatore per svolgere un determinato compito. Nslookup (Name Server Lookup) è uno strumento presente in tutti i sistemi operativi che utilizzano il protocollo TCP/IP, e consente di effettuare delle query ad un server DNS per la risoluzione di indirizzi IP o Hostname. Posso in pratica ottenere da un dominio il relativo indirizzo IP o nome host e viceversa.
La chiamata gethostbyname() riceve una struttura con il nome DNS di un host e ne restituisce una sockaddr. La chiamata gethostbyaddr() fa l'esatto contrario.
L'implementazione di nslookup che propongo legge un argomento (opzione) da linea di comando: se è "-d" chiama gethostbyaddr(), se è "-i" chiama gethostbyname().
Da notare che nslookup non ha bisogno in realtà di queste opzioni, dal momento che se gli do un nome mi restituisce l'ip e viceversa.
[...]
String *nome, *ipaddress; // per comodità non li gestisco come struct ma come string
if(argv[1] == "-d") // per brevità gestisco le stringhe per assegnamento, senza "strcmp()"
{
ipaddress = argv[2]; // memorizzo l'ipaddress passato da linea di comando
// Voglio ora ottenere il nome dall'indirizzo IP passato,
// che andrà poi convertito nel tipo di rete opportuno
if(!(status = gethostbyaddr(nome, ipaddress))
//gli passo sia il nome che l'indirizzo perché durante l'interrogazione al DNS
//devo verificare che esista un'effettiva corrispondenza tra indirizzo e nome
{
//...nel caso non esista corrispondenza,
//il programma esce visualizzando l'errore
perror("Resolver non presente");
exit(-1);
}
else
{
//...se invece esiste corrispondenza, stampa a video il nome
printf("%s", nome);
exit(0);
}
}
else if(argv[1] == "-i")
{
nome = argv[2]; // memorizzo il nome passato da linea di comando
// Voglio ora ottenere l'indirizzo IP dal nome passato,
// che andrà poi convertito nel tipo di rete opportuno
if(!(status = gethostbyname(nome, ipaddress))
//anche in questo caso vale il discorso delle corrispondenze fatto prima
{
perror("Resolver non presente");
exit(-1);
}
else
{
//...se invece esiste corrispondenza, stampa a video l'indirizzo IP
printf("%s", ipaddress);
exit(0);
}
}
Anche se non è richiesto, vediamo come potrebbe essere implementata l'interfaccia IDL.
// interface header IDL per il DNS
[ uiid 12345 ... //calcolato opportunamente
version(1.0)
]
// interface body
interface DNS {
[out] int status gethostbyname ( [in] string nome, [in] ipaddress);
...
}
Analizziamo come è stato descritto il metodo remoto gethostbyname:
- [out], indica che il valore di ritorno del metodo deve essere restituito dal server al client;
- int status, specifica nome e tipo del valore di ritorno del metodo;
- gethostbyname, è il nome del metodo remoto;
- [in], indica che il parametro seguente è passato dal client al server;
- string nome, indica nome e tipo del parametro passato.
A questo punto compilo con rpcgen o IDL-compiler l'IDL appena creata, ottenendo tre file:
- DNS.h (o DNS_h.h), che è l'header file da includere sia nel client che nel server, che contiene le definizioni dei metodi remoti;
- DNS_c.c, ovvero lo stub che gestisce la comunicazione con il server, da linkare al client;
- DNS_s.c, ovvero lo skeleton che gestisce la comunicazione con il client, da linkare al server.
Linkando client e server coi rispettivi stub e skeleton e compilando, ottengo due nuovi eseguibili che chiamerò clientrpc.exe e serverrpc.exe. Non mi resta che:
- registrare l'interfaccia col portmapper del server (porta 111) in modo che il chiamante si connetta alla porta giusta, e lanciare serverrpc.exe .
- lanciare clientrpc.exe al posto del vecchio client.exe .
Scopriamo runtime un interfaccia nota, è una chiamata statica . C'è una identificazione runtime delle porte dell'interfaccia.
Esercizio 3
Scrivete una richiesta HTTP/1.0 per la pagina default.htm sul server www.dti.unimi.it
Scrivete una richiesta sul server www.dti.unimi.it per la URL radice /, ma soltanto i byte da 0 a 300.
SOLUZIONE
prima richiesta:
GET /default.htm HTTP/1.0
Host: www.dti.unimi.it
Connection: close
seconda richiesta:
GET / HTTP/1.1
Host: www.dti.unimi.it
Range: bytes=0-300
Connection: close
Esercizio 4
Un sito Web di grande successo offre la registrazione degli utenti alla URL http://ebusiness.com/register.htm. Poichè la procedura consiste di 3 form da compilare una dopo l’altra, si vuol permettere all’utente di abbandonare la procedura di registrazione in ogni momento per riprendere quando lo desidera. Per evitare di sprecare risorse sul server, si desidera memorizzare lato server le sole registrazioni complete. Illustrate una tecnica a vostra scelta per risolvere il problema.
SOLUZIONE
Una tecnica per risolvere questo problema è quella dei cookie. I cookie sono file testuali contenenti attributi per simulare lo stato di una sessione HTTP. Possono servire per: informazioni generali (pagine visitate, prodotti cercati, preferenze, ecc / autenticazione senza ripetere il login.
Immaginiamo che in fondo alla pagina dopo i 3 form ci sarà un tasto di invio, quindi se l'utente compila tutte e tre le form le informazioni vengono inviate e salvate sul server.
Mentre se l'utente compila solo parzialmente i form sul server non viene salvato nulla, ma vengono utilizzati i cookie che conterranno le informazioni parziali inserite dall'utente. La prossima volta che l'utente accederà alla pagina il client controllerà la presenza dei cookie, se li rileva inserisce le informazioni nei vari campi compilati precedentemente.
Domande:
- Se una pagina Web contiene 10 tag <img ...> quante connessioni TCP verranno aperte con http1.0 quando si accede alla pagina? quante con http 1.1?
- Spiegare la differenza tra i protocolli POP e IMAP
- Cos'è IIOP e che ruolo ha nel protocollo CORBA?
SOLUZIONE
1.
In HTTP/1.0 ogni immagine o altro file è caricato come connessione differente. Quindi avrò 10 connessioni per ogni mmagine più una per la pagina richiesta, quindi 11 totali.
Invece in HTTP/1.1 viene fatto un multiple-get nella stessa connessione. Quindi avrò un unica connessione.
NOTA HTTP: se faccio connessioni web corte ( HTTP/1.0 ) non do il tempo di arrivare ad una dimensione ottimale della finestra, mentre con HTTP/1.1 arrivo prima ad una finestra ottimale TCP di connessione.
Infatti minore è la dimensione delle immagini, minore è la differenza tra HTTP/1.0 e HTTP/1.1. Se sono "grosse" fa differenza perché passo più tempo al livello ottimale della finestra.
2.
Il protocollo IMAP ( Internet Messagge Access Protocol ) è un’alternativa a POP3.
Come POP3 definisce un’astrazione nota come casella postale, posta sul computer server. L’utente attiva un client IMAP che contatta il server per recuperare i messaggi.
La diversità tra i due protocolli sta nel fatto che IMAP permette di creare, cancellare o rinominare dinamicamente le caselle postali. IMAP fornisce inoltre una funzionalità estesa per il recupero e l’elaborazione dei messaggi. L’utente può ottenere informazioni su un messaggio o esaminare i campi dell’intestazione senza recuperare l’intero messaggio; inoltre, può cercare una stringa specificata e riprendere parti specifiche di un messaggio.
(dal libro)
3.
IIOP ( Internet Inter-ORB Protocol ) è un programma basato su CORBA ed è una particolare implementazione del protocollo GIOP che si avvale del protocollo TCP/IP per la realizzazione dello strato di trasporto dei messaggi tra oggetti remoti.
NOTA: Non mi pare che abbia mai fatto questa parte (o magari mentre la faceva stavo giocando a Quake :p). Però a questo link trovate qualche spiegazione interessante sul protocollo GIOP e IIOP.
Torna alla pagina di Sistemi per l'elaborazione delle informazioni