Strumenti Utente

Strumenti Sito


lpr-b-2007-2008:esercizi

Differenze

Queste sono le differenze tra la revisione selezionata e la versione attuale della pagina.

Link a questa pagina di confronto

lpr-b-2007-2008:esercizi [28/11/2007 alle 08:12 (17 anni fa)]
Marco Danelutto
lpr-b-2007-2008:esercizi [19/09/2008 alle 14:08 (16 anni fa)]
Linea 1: Linea 1:
-===== LPRb 2007--2008: Esercizi assegnati a lezione ===== 
  
-[[lpr-b:start|Torna alla pagina principale del corso]] 
- 
-==== Test di ingresso ====  
-    * Si realizzi un programma Java che si comporta come il comando Unix cat (il programma deve copiare quanto letto dallo standard input sullo standard output) //da completare in 10 minuti// 
-    * Si realizzi un programma che stampa i metodi statici di una classe. Il nome della classe è passato come parametro della riga di comando // da completare in 15 minuti // 
-    * Si realizzi un programma nel quale due thread si alternano in esecuzione: il primo thread attende un numero di secondi random, compreso fra 0 e 10, poi si sospende, mandando in esecuzione il secondo thread. Il secondo thread attende un numero di secondi random, sempre compreso fra 0 e 10, quindi si sospende riattivando il primo thread, e così via. Durante il periodo di attesa, i thread stampano un messaggio con il proprio identificatore ogni secondo. // da completare in 20 minuti // 
-=== Soluzione === 
-      * comando [[cat|Cat]] 
-      * [[metodiStatici|metodi statici]] di una classe 
-      * thread che si alternano in esecuzione: [[alternatingProtocol|oggetto condiviso]], [[ilThread|thread]], [[alternating|main]] 
- 
- 
- 
- 
- 
- 
- 
- 
-==== Pool di Thread ==== 
-Si realizzi un programma in grado di eseguire calcoli generici utilizzando un pool di thread. I calcoli da eseguire sono quelli modellati dal metodo **compute** definito dall'interfaccia  
-<code java> 
-public interface Compute<T,S> { 
- public S compute(T x); 
-} 
-</code> 
-i valori di tipo *T* da calcolare si devono prelevare da un **Repository<T>** nel quale vengono inseriti da un **Generatore** e i risultati ottenuti devono essere riposti in una secondo **Repository<S>** dal quale saranno prelevati da uno **Stampatore<S>**. Assumiamo che i dati da calcolare siano **Integer** e che i risultati del calcolo siano **Integer**. Il numero di thread **Calcolatore** da utilizzare per eseguire i calcoli deve essere un parametro della riga di comando.   
- 
-{{  lpr-b:threadpoolfig1n.jpg  |Schema dell'applicazione}} 
- 
-La classe **Repository<T>** deve mettere a disposizione due metodi 
-<code java> 
-... modificatori opportuni ... void insert(T x) // inserisce un oggetto di tipo T nel repository 
-... modificatori opportuni ... T extract()      // preleva il più vecchio elemento del repository 
-</code> 
-Per l'implementazione del repository di //deve// usare una struttura dati fra quelle messe a disposizione dall'ambiente di programmazione Java.  Si considerino per esempio quelle definite in **java.util**. La richiesta di **extract** da un **Repository** vuoto deve provocare il blocco del thread che l'ha invocata fino a quando non sia effettivamente disponibile un dato da estrarre. 
-Per il generatore, si può usare la classe:  
-<code java> 
-public class Generatore extends Thread { 
-  
- Repository<Integer> rep = null;  
- int n = 10; 
- final int DELAY = 1000; 
- final float RANGE = (float) 1024.00; 
-  
- /** 
- * costruttore 
- * @param n numero di oggetti da generare 
- * @param rep repository nel quale depositare gli oggetti generati  
- */ 
- public Generatore(int n, Repository<Integer> rep) { 
- this.rep = rep;  
- this.n = n; 
- } 
-  
- /** 
- * corpo del thread generatore: ciclo finito che genera interi compresi fra 0 e RANGE 
- */ 
- public void run() { 
- for(int i=0; i<n; i++) { 
- try { 
- rep.insert(new Integer((int) (Math.random() * RANGE))); 
- sleep(DELAY); 
- } catch(InterruptedException e) { 
- System.out.println("Interrupted");  
- } 
- } 
- } 
- 
-} 
-</code> 
-Per lo stampatore invece, si consideri il codice 
-<code java> 
-public class Stampatore<T> extends Thread { 
- 
- Repository<T> rep; 
-  
- public Stampatore(Repository<T> rep) { 
- this.rep = rep;  
- } 
-  
- public void run() { 
- while(true) { 
- T i = rep.extract(); 
- System.out.println("Estratto "+i.toString()); 
- } 
- } 
-} 
-</code> 
-Durante l'esecuzione del programma, devono essere attivi ad ogni istante: 1 thread generatore, 1 thread stampatore e N thread calcolatori. Il thread generatore deve terminare quando ha terminato la generazione degli n valori richiesti.  
-Si può realizzare il programma senza necessariamente trattare la terminazione. Una volta realizzato il programma, si rimuova il ritardo nel ciclo di produzione dei valori iniziali da parte del **Generatore** e si inserisca, nel thread **Calcolatore** un ritardo pseudo casuale fra 0 e 5 secondi, utilizzando il codice: 
-<code java> 
-try { 
-  int random =  ((int)(Math.random() * 5000.00)) 
-  System.out.println("Thread "+this.getName()+" inizio calcolo"); 
-  sleep(random); 
-  ... calcolo vero e proprio ...  
-  System.out.println("Thread "+this.getName()+" fine calcolo"); 
-} catch(...) {...} 
-</code> 
-Si controlli quindi che i meccanismi di sincronizzazione fra thread funzionano ancora e che effettivamente si siano più thread che calcolano contemporaneamente. 
- 
-=== Soluzione ===  
-== Senza terminazione == 
-  * Codice per l'interfaccia [[ThreadPoolCompute|Compute]] e per la classe [[ThreadPoolFunzione|FunzioneSemplice]], che implementa l'interfaccia 
-  * Codice per la classe [[ThreadComputeCalcolatore|ThreadComputer]] che implementa il singolo thread del ThreadPool (Calcolatore, nella figura) 
-  * Codice del [[ThreadPoolRepository|Repository]] 
-== Soluzione con terminazione ==  
-  * Codice per il [[RepositoryTermina|repository]]: è stato introdotto un flag che dice se ci saranno altre inserzioni nel repository. Questo flag viene cambiato dal [[GeneratoreTermina|generatore]] prima di terminare. 
-  * Codice del [[ThreadTermina|thread calcolatore]] e codice del [[maintermina|main]]. Il thread calcolatore termina se gli arriva un'eccezione anzichè un valore estratto. Il main lancia i thread calcolatori, poi ne attende la terminazione e a quel punto manda un'interruzione anche al thread stampatore, l'ultimo rimasto attivo, bloccato su una extract dal repository dei risultati, che non sarà mai più utilizzato.  
-  * Codice dello [[StampatoreTermina|stampatore]] che termina. Si rileva l'eccezione InterruptedException conseguente all'interrupt lanciato dal main.  
- 
-== Soluzione che usa java.util.concurrent ==  
-Prima versione: utilizza un thread pool di java.util.concurrent ma usa ancora i Repository come precedentemete definiti: 
-  * Codice con il [[mainTPconcurr|main]] di prova 
-  * Codice per il calcolo del singolo [[taskTPconcurr|task]] 
-  * tutto il resto del codice è uguale a quello delle soluzioni precedenti 
-Seconda versione: utilizza una LinkedBlockingQueue per realizzare i Repository:  
-  * Codice per il [[mainTPconcurDue|main]] 
-  * Codice per lo [[stampatoreTPconcurDue|Stampatore]] (cambia il metodo chiamato per l'estrazione) 
-  * Codice per il [[taskDueC|task]] (cambia il costruttore)  
-  * tutto il resto del codice è identico  
- 
-==== Server di upload file ==== 
-Utilizzando socket TCP/IP, si realizzi un server che mette a disposizione un servizio di file upload. Il client che intende fruire del servizio si connette ed invia:  
-  - il nome del file di cui si vuole fare l'upload 
-  - un ritorno carrello (è la stringa "\n") 
-  - l'intero file 
-Il server, a seguito dell'arrivo di una richiesta di upload, memorizza il file inviato nella directory destinazione. Il nome della directoy destinazione va passato come argomento della riga di comando. Si relizzi anche un Client in grado di inviare un file il cui nome è passato come parametro dalla riga di comando. 
- 
-Dunque sulla macchina fujih3 potremmo lanciare il comando  
- 
-  java serverUpload.Server /tmp 
- 
-(primo ed unico parametro è la directory di upload) mentre sulla macchina fujih5 potremmo lanciare il comando 
- 
-  java serverUpload.Client fujih3 prova.txt 
- 
-(il primo parametro è il nome dell'host che ospita il server di upload, il secondo è il nome del file di cui si richiede l'upload) e come risultato dovremmo avere che fuijh3:/tmp/prova.txt è copia identica del file prova.txt che si trova nella directory corrente per il Client su fujih5. 
- 
-Si realizzi il Server prima come server single thread e successivamente come server multithreaded. In entrambi i casi, il Server deve essere in grado di eseguire l'upload di un numero arbitrario di file.  
- 
-== Soluzione proposta ==  
-  * [[clientUpload|Client]] e [[serverUpload|server]] che fanno uso solo di InputStream e OutputStream per implementare la comunicazione su socket. 
-  * [[serverUploadBuffered|Server]] che utilizza un **BufferedReader** per la comunicazione sul socket (il client è lo stesso). 
- 
- 
- 
- 
-==== Instant messanger ==== 
-Si realizzi un programma che implementa un instant messanger punto a punto. Lanciando il programma su due macchine diverse e passando il nome dell'altra macchina come parametro della riga di comando, si ottiene il "collegamento" fra la due macchine: ciò che viene scritto alla tastiera di una macchina deve comparire sul video dell'altra e viceversa. Per la realizzazione del programma, ognuno usi come numero di porta 1NNNN dove NNNN sono le ultime 4 cifre del proprio numero di matricola.  
- 
-I due programmi lanciati sulle due macchine devono essere identici.  
- 
-Qualora si tenti un contatto su una macchina dove ancora non è stata avviato il programma per l'instant messager sulla porta 1NNNN, ci si deve sospendere per 5 secondi e successivamente riprovare, per 5 volte. Se il contatto non ha successo nemmeno la quarta volta, il programma deve terminare con un messaggio di errore.  
- 
-{{  lpr-b:minitalk.jpg?800  | Esempio di sessione con due terminali sulle macchine u5 e u6}} 
- 
-== Soluzione proposta == 
-  * Codice per il [[messangerMain|Messanger]]  
-  * Codice per il [[copyThreadMessanger|CopyThread]] 
- 
- 
- 
- 
- 
-==== NsLookup con cache ==== 
- 
-Si realizzi un server multithreaded che accetta richieste di risoluzione di nomi di host. La risoluzione dei nomi deve prevedere l'utilizzo di una cache interna al server che mantiene tutti i risultati (<nomeserver,indirizzoIP>) delle query precedenti. Il server deve implementare un pool di thread di dimensione fissata e per il multithreading deve utilizzare le classi della **java.util.concurrent**. Le richieste avvengono utilizzando un **ObjectStream** e sono contenute in un oggetto che ha un campo **String** e un campo **boolean**. Il campo **String** contiene il nome dell'host da risolvere, il campo **boolean** dice (se **true**) che si accetta la risoluzione del nome dell'host effettuata mediante accesso alla cache interna del server o (se **false**) che non si vuole considerare la risposta eventualmente presente nella cache del server. Se non presente in cache, o se il flag nella richiesta vale **false**, il server effettua il lookup del nome del server utilizzando una semplice **InetAddress.getByName**. 
-{{  lpr-b:lookup2.jpg  |Funzionamento del server}} 
-Il client deve stampare un messaggio con il numero di millisecondi necessari ad ottenere la risposta. A tale scopo si utilizzi il metodo statico **currentTimeMillis()** della classe **System** che restituisce il "tempo di sistema" in millisecondi, in modo da ottere il valore del tempo di sistema prima dell'invio della richiesta al server e immediatamente dopo la ricezione della risposta. La differenza fra i due tempi dà il tempo in millisecondi speso dal server per evadere la richiesta.  
- 
- 
-== Soluzione proposta == 
-  * File [[namequery|NameQuery.java]] 
-  * File [[nameserver|NameServer.java]] 
-  * File [[nslookup|NsLookup.java]] 
-  * File [[QueryAnswer|QueryAnswer.java]] 
-  * File [[ServerThread|ServerThread.java]] 
- 
- 
-==== Trasferimento file (TFTP con UDP) ==== 
- 
-Si vuole realizzare una coppia di programmi che permettano di trasferire l’intero contenuto di un file da una macchina all’altra, utilizzando un protocollo tipo TFTP implementatao su datagram socket. Il protocollo TFTP prevede la trasmissione dell’intero file mediante la spedizione di un certo numero di pacchetti, ognuno dei quali contiene una parte del file. La spedizione di un messaggio (tranne che nel caso del primo messaggio) può avvenire solo dopo la ricezione di un ACK da parte del destinatario. In caso non venga ricevuto l’ACK entro un certo timeout (per esempio entro due secondi) il mittente deve provvedere alla ritrasmissione del pacchetto. Analogamente, il destinatario che non veda arrivare un pacchetto entro un certo timeout deve provvedere alla rispedizione dell’ultimo ACK, nell’eventualità che si sia perso. 
- 
-Si dovranno quindi realizzare due programmi: un Sender che prende come parametri il file da trasmettere, l’host destinazione e la porta destinazione e trasmette il file, e un Receiver che prende come parametri il nome del file da scrivere e la porta da utilizzare e riceve il file salvandolo su disco.  Per verificare che i due programmi funzionino correttamente, lanciare una shell remota su una macchina diversa da quella su cui state lavorando, poi lanciate il Sender su una macchina e il Receiver sull’altra macchina, date due nomi di file diversi e alla fine eseguite un comando diff filename1 filename2. Se ritorna il prompt della shell senza messaggi vuol dire che i file sono uguali, altrimenti c’e’ stato un errore. Provate a trasferire sia file di tipo testo che file di tipo binario  (per esempio, file .class)). 
- 
-Si supponga che il pacchetto da trasmettere sia modellato dalla classe [[TFTmessageClass|TFTmessage]]  e che questi oggetti siano serializzati innetro ai DatagramPacket utilizzando la classe [[ODPClass|ODP]] già vista a lezione quando abbiamo introdotto la serializzazione degli oggetti. 
-La classe TFTPmessage prevede sia un numero di sequenza che una marca booleana che dice se devono essere spediti ancora pacchetti del file oppure se la spedizione è terminata. Tali campi vanno utilizzati per gestire le ritrasmissioni in modo corretto, sia dal lato Sender (ritrasmissione di pacchetti con dati relativi al file) sia lato Receiver (ritrasmissione dei pacchetti di tipo ACK). 
-Utilizzando ODP, si può spedire un oggetto in un DatagramPacket creando un ODP col costruttore che prende come parametro l’oggetto da spedire e successivamente invocando sull’oggetto creato un metodo getDatagramPacket. Per ricevere un oggetto serializzato in un DatagramPacket, si usa il metodo statico getODP per ottenere un ODP dal DatagramPacket e successivamnete una getObject sull’oggetto ODP restituito dalla getODP per ottenre l’oggetto deserializzato. 
- 
-== Soluzione proposta == 
-  * File [[ODP|ODP.java]] 
-  * File [[Sender|Sender.java]] 
-  * File [[Receiver|Receiver.java]] 
-  * File [[TFTPmessage|TFTPmessage.java]] 
- 
-=============================== 
- 
- 
- 
- 
- ==== Server NFS ==== 
-  
- Si realizzi un semplice server NFS, utilizzando esclusivamente il protocollo UDP. Il server deve accettare richieste di apertura, lettura, scrittura e chiusura di file. L'operazione richiesta deve avere effetto locale e al client remoto si deve restituire: 
-    * un handle (che il server dovra' cachare) per le successive operazioni (di tipo String, per esempio) in caso di open  
-    * un byte[] contenente l'array di byte letti, nel caso di read 
-    * un errore o un codice di successo nel caso di close e di write. 
- In particolare, suggeriamo di utilizzare messaggi (nel payload dei pacchetti UDP) tipo: 
-    * OPEN \n filename \n  per la richiesta di apertura di un file 
-    * READ \n handle \n nbytedaleggere \n per la richiesta di lettura di un file con handler dato 
-    * CLOSE \n handle \n per la richiesta di chiusura di un file con handler dato 
-    * WRITE \n handle \n seguito da un byte[] opportunamente codificato contenente i byte da scrivere per la richiesta di scrittura di un file con handler dato 
-    * ERROR \n argomenti dell'errore separati da \n e conclusi da \n per gli errori 
-    * OK \n per i messaggi di successo 
-    * HANDLE \n handle \n per le risposte ai messaggi di apertura avvenuta con successo. 
- 
- Vista la complessità del problema, si consiglia di strutturare la soluzione come segue: 
-    * classe **RemoteInputStream**, con metodi **boolean open(String filename)**, **boolean close()** e **byte[] read(int len)** 
-    * classe **RemoteOutputStream**, con metodi **boolean open(String filename)**, **boolean close()** e **boolean write(byte[])** 
-    * classe **UDPServerComm**, con metodo **DatagramPacket call (DatagramPacket dp)** che si prende carico di trasmettere una richiesta e ricevere una risposta dal server, incapsulando tutti i dettagli legati all'utilizzo di socket non affidabili come i DatagramSocket, il setting del timeout e l'eventuale ritrasmissione 
-    * class **NfsServer**, che implementa il server remoto. 
- Le prime due classi dovrebbero permettere di scrivere un cliente tipo: 
- <code java> 
-         public static void main(String[] args) { 
-                 RemoteInputStream ris = new RemoteInputStream("fujih1"); 
-                 ris.open("prova1.txt"); 
-                 byte [] buffer = ris.read(100); 
-                 ris.close(); 
-                 System.out.println("letti "+buffer.length+" bytes:"+new String (buffer)); 
-         } 
-  
- </code> 
- per leggere parte del file che si trova su fujih1 se sul fujih1 sta girando un NfsServer. 
- 
- 
-=== Soluzione === 
-      * classe [[ilserver|NfsServer.java]] 
-      * classe [[remotestream|RemoteStream.java]] 
-      * classe [[remoteinputstream|RemoteInputStream.java]] 
-      * classe [[remoteoutputstream|RemoteOutputStream.java]] 
-      * classe [[streamref|StreamRef.java]] 
-      * classe [[remotepacket|RemotePacket.java]] 
-      * classe [[udpservercomm|UDPServerComm.java]] 
- 
- 
- 
- 
- 
- 
-==== Chat con multicast ==== 
-Si realizzi un client chat che permetta la comunicazione di semplici messaggi di testo fra più utenti. Il programma deve fare uso di multicast UDP.  
-La chat avviene utilizzando un indirizzo di multicast passato come parametro della riga di comando. I messaggi inviati alla chat dal singolo utente vengono semplicemente inviati al gruppo di multicast. Tutti i messaggi diretti all'indirizzo di multicast vengono copiati sullo standard output.  
- 
-Successivamente si modifichi il programma in modo da utilizzare broadcast invece che multicast per l'implementazione delle stesse funzionalità. 
- 
-Si faccia in modo che i messaggi stampati a video comincino sempre con l'indicazione dell'indirizzo della macchina (o meglio il nome) da cui si è ricevuto il messaggio. Per esempio, a terminale dovremmo vedere una cosa tipo:  
- 
-  fujih4:~> java chatMulticast/Chat 225.67.1.91 23232 
-  ciao 
-  From /131.114.11.154: ciao 
-  Come va 
-  From /131.114.11.154: Come va 
-  From /131.114.11.151: hola 
- 
-=== Soluzione proposta === 
- 
-  * [[copyin|thread copiatore (pacchetti -> video)]] 
-  * [[copyout|thread copiatore (tastiera -> pacchetto)]] 
-  * [[mainchat|main]] 
- 
- 
- 
- 
- 
-==== Test Datagram ==== 
-Questo non è un esercizio vero e proprio. E' la realizzazione di una classe che sottoclassa DatagramSocket e implementa un metodo send che "perde" (ovvero non trasmette) una percentuale prefissata di pacchetti.  
-Utilizzando questa classe invece della DatagramSocket standard si può facilmente verificare il funzionamento di un software che utilizza protocolli basati su UDP.  
- 
-  * codice della classe [[datagrampacket|DatagramPacket]] 
-   
- 
-==== ContoCorrente RMI ==== 
-Si realizzi un oggetto ContoCorrente e lo si renda accessibile da remoto utilizzando la tecnologia RMI, che permetta di effettuare operazioni di 
-   
-  long saldo()                      // restitiusce il saldo 
-  long prelievo(long cifra)         // preleva la cifra e restituisce il saldo 
-  long deposito(long cifra)         // deposita la cifra e restituisce il saldo 
-  String ultimiMovimenti(int n)     // restituisce gli ultimi n movimenti, uno per riga 
- 
-Si realizzino: 
-    * un'interfaccia con la signature dell'oggetto remoto 
-    * l'oggetto da pubblicare con RMI 
-    * un main che crea il registry e pubblica l'oggetto 
-    * un cliente che accetta come parametri dalla riga di comando il nome dell'host su cui si trova l'oggetto, il nome dell'oggetto, un codice operazione e eventuali parametri dell' operazione ed effettui l'operazione richiesta.  
- 
-Si faccia in modo che ciascuna delle operazioni dell'oggetto conto corrente prenda un certo tempo (per esempio 20 secondi) e si controlli, lanciando piu' clienti contemporaneamente che le operazioni avvengano effettivamente in mutua esclusione e rispettando la semantica propria di un contocorrente.  
- 
- =============================== 
-[[lpr-b:start|Torna alla pagina principale del corso]] 
lpr-b-2007-2008/esercizi.txt · Ultima modifica: 19/09/2008 alle 14:08 (16 anni fa) (modifica esterna)