Java - Programmazione di Reti
-
Upload
brottysandro -
Category
Documents
-
view
476 -
download
1
Transcript of Java - Programmazione di Reti
ULezione 1: JAVA Tasks e Threads 1Andrea Corradini
Lezione n.1LPR-B-09
JAVA: Tasks e Threads
22/9/2009Andrea Corradini
(basato su materiale di Laura Ricci e Marco Danelutto)
Università degli Studi di Pisa Dipartimento di Informatica
ULezione 1: JAVA Tasks e Threads 2Andrea Corradini
PROGRAMMA DEL CORSO
Threads: Attivazione, Classe Thread,Interfaccia Runnable, Thread Pooling, Threads, Sincronizzazione su strutture dati condivise: metodi synchronized, wait, notify, notifyall
Thread Pooling: Meccanismi di gestione di pools di threads
Streams: Proprietà degli Streams, Tipi di streams, Composizione di streams, ByteArrayInputStream, ByteArrayOutputStream
Indirizzamento IP: Gestione degli Indirizzi IP in JAVA: La classe InetAddress
Meccanismi di Comunicazione in Rete: Sockets Connectionless e Connection Oriented
Connectionless Sockets: La classe Datagram Socket: creazione di sockets, generazione di pacchetti, timeouts, uso degli streams per la generazione di pacchetti di bytes, invio di oggetti su sockets connectionless.
ULezione 1: JAVA Tasks e Threads 3Andrea Corradini
PROGRAMMA DEL CORSO
Multicast: La classe MulticastSocket, Indirizzi di Multicast, Associazione ad un gruppo di multicast. Proposte di reliable multicast (FIFO multicast,causal multicast, atomic multicast).
Connection Oriented Sockets: Le classi ServerSocket e Socket. Invio di oggetti su sockets TCP.
Il Paradigma Client/Server: Caratteristiche del paradigma client/ server, Meccanismi per l'individuazione di un servizio, architettura di un servizio di rete.
Oggetti Distribuiti: Remote Method Invocation, Definizione di Oggetti Remoti, Registrazione di Oggetti, Generazione di Stub e Skeletons.
Meccanismi RMI Avanzati: Il meccanismo delle callback.
ULezione 1: JAVA Tasks e Threads 4Andrea Corradini
MULTITHREADING: DEFINIZIONI
Definizioni:
Thread: flusso sequenziale di esecuzione
Multithreading:
• consente di definire più flussi di esecuzione (threads) all’ interno dello stesso programma
• i threads possono essere eseguiti
• in parallelo (simultaneamente) se il programma viene eseguito su un multiprocessor
• in modo concorrente (interleaving) se il programma viene eseguito su un uniprocessor, ad esempio mediante time-sharing
ULezione 1: JAVA Tasks e Threads 5Andrea Corradini
MULTITHREADING: MOTIVAZIONI
Migliorare le prestazioni di un programma:
• dividere il programma in diverse parti ed assegnare l'esecuzione di ogni parte ad un processore diverso
• su architetture di tipo uniprocessor: può migliorare l'uso della CPU quando il programma si blocca (es: I/O) implementazione di interfacce utente reattive
Web Server:
• Assegna un thread ad ogni richiesta
• Utilizza più CPU in modo da gestire in parallelo le richieste degli utenti
Interfacce reattive:
• gestione asincrona di eventi generati dall'interazione con l'utente
ULezione 1: JAVA Tasks e Threads 6Andrea Corradini
MULTITHREADING: MOTIVAZIONI
Migliorare la progettazione del codice: Non solo una panacea per aumentare le prestazioni, ma uno strumento
per sviluppare software robusto e responsivo
Esempi: progettazione di un browser : mentre viene caricata una pagina, mostra
un'animazione. Inoltre mentre carico la pagina posso premere il bottone di stop ed interrompere il caricamento. Le diverse attività possono essere associati a threads diversi.
Progettazione di applicazioni complesse che richiedono la gestione contemporanea di più attività.
applicazioni interattive distribuite (giochi multiplayers): si devono gestire eventi provenienti dall'interazione con l'utente, dalla rete...
ULezione 1: JAVA Tasks e Threads 7Andrea Corradini
TASKS E THREADS IN JAVA
• L'interfaccia java.lang.Runnable contiene un solo metodo
void run( )
• Un task è un oggetto (di una classe) che implementa l'intefaccia Runnable: la sua esecuzione inizia con l'invocazione di run()
• Un thread è un oggetto della classe java.lang.Thread, che implementa Runnable
• Attivare un thread significa iniziare un nuovo flusso di esecuzione per eseguire un determinato task
• Un thread viene attivato invocando il metodo
void start()
• La JVM (Java Virtual Machine) inizia l'esecuzione del thread invocandone il metodo run()
ULezione 1: JAVA Tasks e Threads 8Andrea Corradini
THREAD IN UN PROGRAMMA JAVA
• IL thread main viene creato dalla JVM per avviare l'esecuzione di un'applicazione JAVA: il task eseguito è il metodo main(String [])della classe invocata, ad esempio MyClass se abbiamo eseguito da linea di comando
java MyClass
• Altri thread sono attivati automaticamente dalla JVM (sono thread daemons: gestore eventi interfaccia, garbage collector, ...)
• Ogni thread durante la sua esecuzione può attivare altri thread
• Un thread è un daemon se e solo se il thread che lo ha creato è un daemon
• Un programma JAVA termina quando sono terminati tutti i suoi thread che non sono daemons.
ULezione 1: JAVA Tasks e Threads 9Andrea Corradini
ATTIVAZIONE DI THREADS
Per definire un task e eseguirlo in un nuovo thread si usano due tecniche: Si estende la classe Thread sovrascrivendo il metodo run(): questo è
possibile solo se non dobbiamo ereditare da un'altra classe. Per attivare il thread basta invocare start() su una istanza della classe.
oppure Si definisce una classe C che implementa Runnable. Quindi si crea una
nuova istanza di Thread, passando una istanza O di C come argomento, e si invoca start() sul thread.
Come funziona? Vediamo il metodo run() di Thread:
public Thread (Runnable target){
this.target = target}
public void run( ) {
if (target != null) { target.run( ); } }
ULezione 1: JAVA Tasks e Threads 10Andrea Corradini
IL TASK DECOLLO
Esempio: Implementare un task Decollo che implementi un 'conto alla rovescia' e che, alla fine del conto, invii un segnale 'Via!'
public class Decollo implements Runnable {
int countDown = 10; // Predefinito
private static int taskCount = 0;
final int id= taskCount ++; // identifica il task
public Decollo( ) { }
public Decollo (int countDown) {this.countDown = countDown; }
public String status ( ) {return "#" + id + "(" +
(countDown > 0 ? countDown: "Via!!!")+"),"; }
ULezione 1: JAVA Tasks e Threads 11Andrea Corradini
IL TASK DECOLLO
public void run( ) {
while (countDown-- > 0){System.out.print(status( ));try{ Thread.sleep(100);}
catch(InterruptedException e){ }}}}
public class MainThread {
public static void main(String[] args){decollo d= new Decollo(); d.run( ); System.out.println ("Aspetto il decollo");}}
OutputGenerato
#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(Via!!!),Aspetto il decollo
ULezione 1: JAVA Tasks e Threads 12Andrea Corradini
UN TASK NON E' UN THREAD!
• NOTA BENE: Nell'esempio precedente non viene creato alcun thread per
l'esecuzione del metodo run( )
• Il metodo run( ) viene eseguito all'interno del thread main, attivato per il
programma principale
• Invocando direttamente il metodo run( ) di un oggetto di tipo Runnable,
non si attiva alcun thread ma si esegue il task definito dal metodo run( )
nel thread associato al flusso di esecuzione del chiamante
• Per associare un nuovo thread di esecuzione ad un Task, occorre creare un
oggetto di tipo Thread e passargli il Task
ULezione 1: JAVA Tasks e Threads 13Andrea Corradini
DECOLLO IN UN THREAD INDIPENDENTE
public class MainThread {public static void main(String [ ] args) { Decollo d = new Decollo();
Thread t = new Thread(d);t.start();System.out.println("Aspetto il Decollo"); }
}
Output generato (con alta probabilità, comunque può dipendere dallo
schedulatore):
Aspetto il decollo
#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(Via!!!),
ULezione 1: JAVA Tasks e Threads 14Andrea Corradini
DECOLLO IN PIU' THREAD
public class MoreThreads {
public static void main(String [ ]args) {for (int i=0; i<5; i++)
new Thread(new Decollo()).start();System.out.println("Aspetto il decollo");
}}
Possibile output generato:
#0(9),#1(9),Aspetto il decollo#2(9),#4(9),#3(9),#1(8),#0(8),#3(8),#4(8),#2(8),#1(7),#0(7),#2(7),#4(7),#3(7),#0(6),#4(6),#2(6),#3(6),#1(6),#2(5),#1(5),#3(5),#4(5),#0(5),#2(4),#3(4),#0(4),#4(4),#1(4),#2(3),#0(3),#3(3),#4(3),#1(3),#3(2),#0(2),#2(2),#4(2),#1(2),#3(1),#2(1),#0(1),#1(1),#4(1),#3(Via!!!),#4(Via!!!),#2(Via!!!),#0(Via!!!),#1(Via!!!),
ULezione 1: JAVA Tasks e Threads 15Andrea Corradini
LA CLASSE java.lang.Thread
La classe java.lang.Thread contiene membri per:
• costruire un thread interagendo con il sistema operativo ospite
• attivare, sospendere, interrompere i thread
• non contiene i metodi per la sincronizzazione tra i thread, che sono definiti in java.lang.Object.
Costruttori:
• Vari: differiscono per parametri utilizzati (esempio: task da eseguire, nome del thread, gruppo cui appartiene il thread: vedere API)
Metodi
• Possono essere utilizzati per interrompere, sospendere un thread, attendere la terminazione di un thread + un insieme di metodi set e get per impostare e reperire le caratteristiche di un thread esempio: assegnare nomi e priorità ai thread
ULezione 1: JAVA Tasks e Threads 16Andrea Corradini
LA CLASSE java.lang.Thread: start()
Il metodo start( )
• segnala allo schedulatore della JVM che il thread può essere attivato
(invoca un metodo nativo). L'ambiente del thread viene inizializzato
• ritorna immediatamente il controllo al chiamante, senza attendere che il
thread attivato inizi la sua esecuzione.
NOTA: la stampa del messaggio “Aspetto il decollo” è nel mezzo di
quelle effettuate dai threads. Questo significa che il controllo è stato
restituito al thread chiamante (il thread associato al main) prima che
sia terminata l'esecuzione dei threads attivati
ULezione 1: JAVA Tasks e Threads 17Andrea Corradini
LA CLASSE java.lang.Thread: run()
• La classe Thread implementa l'interfaccia Runnable e quindi contiene l'implementazione del metodo run( )
public void run( ) {if (target != null) { target.run( ); } }
target = riferimento all'oggetto Runnable passato al momento della creazione
oppure null.
• L'attivazione di un thread mediante la start() causa l'invocazione del metodo run( ) precedente. A sua volta, viene invocato il metodo run( ) sull'oggetto che implementa Runnable (se questo è presente).
• Qualsiasi istruzione eseguita dal thread fa parte di run( ) o di un metodo invocato da run( ). Inoltre il thread termina con l'ultima istruzione di run( ).
• Dopo la terminazione un thread non può essere riattivato
ULezione 1: JAVA Tasks e Threads 18Andrea Corradini
ESTENSIONE DELLA CLASSE THREADS
Creazione ed attivazione di threads: un approccio alternativo
• creare una classe C che estenda la classe Thread
• effettuare un overriding del metodo run( ) definito all'interno della
classe Thread
• Istanziare un oggetto O di tipo C. O è un thread il cui comportamento
è programmato nel metodo run( ) riscritto in C
• Invocare il metodo start( ) su O. Tale metodo attiva il thread ed
invoca il metodo riscritto.
ULezione 1: JAVA Tasks e Threads 19Andrea Corradini
DECOLLO COME SOTTOCLASSE DI THREAD
public class DecolloThread extends Thread {.......
public void run( ) {
while (countDown-- > 0){
System.out.print(status( ));
try{ Thread.sleep(100);
} catch(InterruptedException e){ } }}}
public class MainDecolloThread {
public static void main(String [ ]args) {
DecolloThread d = new DecolloThread( );
d.start();
System.out.println("Aspetto il Decollo"); }}
ULezione 1: JAVA Tasks e Threads 20Andrea Corradini
GERARCHIA DELLE CLASSI
• Thread estende Object e implementa l'interfaccia Runnable
• DecolloThread estende Thread e sovrascrive il metodo run() di Thread
Object
Thread
DecolloThread
Runnable
implements
ULezione 1: JAVA Tasks e Threads 21Andrea Corradini
QUANDO E' NECESSARIO USARE LA RUNNABLE
In JAVA una classe può estendere una solo altra classe (eredità singola)
⇒La classe i cui oggetti devono essere eseguiti come thread non può
estendere altre classi.
Questo può risultare svantaggioso in diverse situazioni.
Esempio: Gestione degli eventi (es: movimento mouse, tastiera…) la classe che gestisce l’evento deve estendere una classe predefinita
JAVA inoltre può essere necessario eseguire il gestore come un thread
separato
ULezione 1: JAVA Tasks e Threads 22Andrea Corradini
GESTIONE DEI THREADS
• public static native Thread currentThread ( ) In un ambiente multithreaded, lo stesso metodo può essere
eseguito in modo concorrente da più di un thread. Questo metodo restituisce un riferimento al thread che sta eseguendo un segmento di codice
• public final void setName(String newName) • public final String getName( )
consentono, rispettivamente, di associare un nome ad un thread e di reperire il nome assegnato
• public static native void sleep (long mills) sospende l'esecuzione del thread che invoca il metodo per mills
millisecondi. Durante questo intervallo di tempo il thread non utilizza la CPU. Non è possibile porre un altro thread in sleep.
ULezione 1: JAVA Tasks e Threads 23Andrea Corradini
ESERCIZIO 1
• Scrivere un programma JAVA che attivi K thread, chiamati “T1”, “T2”, ..., “TK”. Tutti i thread sono caratterizzati dallo stesso comportamento: ogni thread stampa i primi N numeri naturali, senza andare a capo (K e N sono dati in input dall'utente). Accanto ad ogni numero deve essere visualizzato il nome del thread che lo ha generato, ad esempio usando il formato “: n [Tk] :”. Tra la stampa di un numero e quella del numero successivo ogni thread deve sospendersi per un intervallo di tempo la cui durata è scelta in modo casuale tra 0 e 1000 millisecondi.
• Sviluppare due diverse versioni del programma che utilizzino le due tecniche per l'attivazione di threads presentate in questa lezione.
ULezione 1: JAVA Tasks e Threads 24Andrea Corradini
COME INTERROMPERE UN THREAD
• Un thread può essere interrotto, durante il suo ciclo di vita, ad esempio mentre sta 'dormendo' in seguito all'esecuzione di una sleep()
• L'interruzione di un thread causa una InterruptedException
public class SleepInterrupt implements Runnable {public void run ( ){ try{ System.out.println("vado a dormire per 20 secondi");
Thread.sleep(20000);
System.out.println ("svegliato"); }catch ( InterruptedException x )
{ System.out.println ("interrotto"); return;};System.out.println("esco normalmente");}}
ULezione 1: JAVA Tasks e Threads 25Andrea Corradini
COME INTERROMPERE UN THREAD
public class SleepMain {
public static void main (String args [ ]) {SleepInterrupt si = new SleepInterrupt();Thread t = new Thread (si);t.start ( );try {
Thread.sleep(2000);} catch (InterruptedException x) { };System.out.println("Interrompo l'altro thread");t.interrupt( );
System.out.println ("sto terminando...");
} }
ULezione 1: JAVA Tasks e Threads 26Andrea Corradini
COME INTERROMPERE UN THREAD
• Il metodo interrupt( ): interrompe il thread causando una InterruptedException se era sospeso
(con wait(), sleep(), join(), I/O) altrimenti imposta a true un flag nel descrittore del thread
E' possibile testare il valore del flag mediante: public static boolean interrupted ( ) STATIC !!!
restituisce il valore del flag (relativo al thread in esecuzione); riporta il valore del flag a false
public boolean isInterrupted ( )restituisce il valore del flag, relativo al thread su cui è invocato
Nota: se esiste un interrupt pendente al momento dell'esecuzione della
sleep( ), viene sollevata immediatamenete una InterruptedException.
ULezione 1: JAVA Tasks e Threads 27Andrea Corradini
ESERCIZIO 2: INTERROMPERE UN THREAD
Scrivere un programma che avvia un thread che va in sleep per 10 secondi. Il programma principale interrompe il thread dopo 5 secodni. Il thread deve catturare l'eccezione e stampare il tempo trascorso in sleep.
Per ottenere l'ora corrente usare il metodo System.currentTimeMillis(), consultandone la documentazione on line.
ULezione 1: JAVA Tasks e Threads 28Andrea Corradini
Esercizio 3: CALCOLO DI π
Scrivere un programma che attiva un thread T che effettua il calcolo approssimato di π. Il programma principale riceve in input da linea di comando due argomenti:
un parametro che indica il grado di accuratezza (accuracy) per il calcolo di π
il tempo massimo di attesa dopo cui il programma principale interrompe il thread T.
Il thread T effettua un ciclo infinito per il calcolo di π usando la serie di Gregory-Leibniz ( π = 4/1 – 4/3 + 4/5 - 4/7 + 4/9 - 4/11 ...). Il thread esce dal ciclo quando una delle due condizioni seguenti risulta verificata:
1) il thread è stato interrotto, oppure
2) la differenza tra il valore stimato di π ed il valore Math.PI (della
libreria Java) è minore di accuracy
ULezione 1: JAVA Tasks e Threads 29Andrea Corradini
PRIORITA' DEI THREADS
• Ogni thread ha una priorità che può essere cambiata durante l'esecuzione. La priorità rappresenta un suggerimento allo schedulatore sull'ordine con cui i threads possono essere inviati in esecuzione
• La priorità viene ereditata dal thread padre
• Metodi per gestire la priorità public final void setPriority (int newPriority)
Può essere invocato prima dell'attivazione del thread o durante la sua esecuzione (da un thread che ne ha il diritto)
public final int getPriority ( ) Thread.MAX_PRIORITY (= 10) Thread.MIN_PRIORITY (= 1) Thread.NORM_PRIORITY (= 5)
ULezione 1: JAVA Tasks e Threads 30Andrea Corradini
THREAD CON DIVERSE PRIORITA'
Scriviamo un programma dove il thread main ha priorità 5 (come da default), e attiva i thread
Thread A con priorità 8 e poi 3 Thread B con priorità 2 Thread D con priorità 7, che crea
Thread C con la stessa priorità, 7.
ULezione 1: JAVA Tasks e Threads 31Andrea Corradini
THREAD PRIORITY: Definizione dei task
public class Task1 implements Runnable{
public void run( ) {for (int i = 0; i < 4; i++){
Thread t = Thread.currentThread ( );System.out.println(t.getName() +
" ha priorita' " + t.getPriority());try {Thread.sleep (2000);} catch(InterruptedException x) {}} } }
public class Task2 implements Runnable{
public void run( ) {Thread tC = new Thread(new Task1(), "thread C");tC.start(); new Task1().run(); } }
ULezione 1: JAVA Tasks e Threads 32Andrea Corradini
THREAD PRIORITY: il main
public class MainPriority{
public static void main (String[ ] args) {Thread tA = new Thread(new Task1(),"thread A");Thread tB = new Thread(new Task1(),"thread B");Thread tD = new Thread(new Task2(),"thread D");tA.setPriority(8); tB.setPriority(2); tD.setPriority(7);tA.start(); tB.start(); tD.start();try{Thread.sleep(3000);}catch(InterruptedException x) { }tA.setPriority(3);System.out.println("main ha priorita' " +
Thread.currentThread().getPriority()); } }
ULezione 1: JAVA Tasks e Threads 33Andrea Corradini
THREAD PRIORITY: Un possibile output
thread B ha priorita' 2thread D ha priorita' 7thread C ha priorita' 7thread A ha priorita' 8thread B ha priorita' 2thread D ha priorita' 7thread A ha priorita' 8thread C ha priorita' 7main ha priorita' 5thread B ha priorita' 2thread D ha priorita' 7thread A ha priorita' 3thread C ha priorita' 7thread B ha priorita' 2thread D ha priorita' 7thread A ha priorita' 3thread C ha priorita' 7
ULezione 2: JAVA pooling 1Andrea Corradini
Lezione n.2LPR-B-09
Thread Pooling e Callable
29/9-6/10/2009Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
ULezione 2: JAVA pooling 2Andrea Corradini
ATTENDERE LA TERMINAZIONE DI UN THREAD: METODO join()
Un thread J può invocare il motodo join( ) su un oggetto T di tipo thread
J rimane sospeso sulla join( ) fino alla terminazione di T.
Quando T termina, J riprende l'esecuzione con l'istruzione successiva alla
join( ).
Un thread sospeso su una join( ) può essere interrotto da un altro thread
che invoca su di esso il metodo interrupt( ).
Il metodo può essere utilizzato nel main per attendere la terminazione di
tutti i threads attivati.
ULezione 2: JAVA pooling 3Andrea Corradini
JOINING A THREAD
public class Sleeper extends Thread {
private int period;
public Sleeper (String name, int sleepPeriod){super(name);period = sleepPeriod;start( ); }
public void run( ){try{
sleep (period); }
catch (InterruptedException e){System.out.println(getName( )+" e' stato interrotto"); return;}
System.out.println(getName()+" e' stato svegliato normalmente");}}
ULezione 2: JAVA pooling 4Andrea Corradini
JOINING A THREAD
public class Joiner extends Thread {
private Sleeper sleeper;
public Joiner(String name, Sleeper sleeper){super(name);this.sleeper = sleeper;start( ); }
public void run( ){try{
sleeper.join( );}catch(InterruptedException e) {
System.out.println("Interrotto"); return; }
System.out.println(getName( )+" join completed"); }}
ULezione 2: JAVA pooling 5Andrea Corradini
JOINING A THREAD
public class Joining {
public static void main(String[ ] args){Sleeper assonnato = new Sleeper("Assonnato", 1500);Sleeper stanco = new Sleeper("Stanco", 1500);
new Joiner("WaitforAssonnato", assonnato);new Joiner("WaitforStanco", stanco);stanco.interrupt(); } }
Output:
Stanco è stato interrotto
WaitforStanco join completed
Assonnato è stato svegliato normalmente
WaitforAssonnato join completed
ULezione 2: JAVA pooling 6Andrea Corradini
THREAD POOLING: CONCETTI FONDAMENTALI
• L'utente struttura l'applicazione mediante un insieme di tasks.
• Task = segmento di codice che può essere eseguito da un “esecutore”. Può
essere definito come un oggetto di tipo Runnable
• Thread = esecutore in grado di eseguire tasks.
• Uno stesso thread può essere utilizzato per eseguire diversi tasks, durante la
sua vita.
• Thread Pool = Struttura dati (normalmente con dimensione massima
prefissata), che contiene riferimenti ad un insieme di threads
• I thread del pool vengono utilizzati per eseguire i tasks sottomessi
dall'utente.
ULezione 2: JAVA pooling 7Andrea Corradini
THREAD POOLING: MOTIVAZIONI
• Tempo stimato per la creazione di un thread: qualche centinaio di microsecondi.
• La creazione di un alto numero di threads può non essere tollerabile per certe applicazioni.
• Thread Pooling– Diminuisce l'overhead dovuto alla creazione di un gran numero di
thread: lo stesso thread può essere riutilizzato per l'esecuzione di più di un tasks
– Permette una semplificazione e una migliore strutturazione del codice dell'applicazione: tutta la gestione dei threads può essere delegata al gestore del pool (che va può essere riutilizzato in altre applicazioni...)
ULezione 2: JAVA pooling 8Andrea Corradini
THREAD POOLING: USO
L'utente
• Definisce i tasks dell'applicazione
• Crea un pool di thread e stabilisce una politica per la gestione dei threads all'interno del pool. La politica stabilisce:
– quando i threads del pool vengono attivati: (al momento della creazione del pool, on demand, in corrispondenza dell'arrivo di un nuovo task,....)
– se e quando è opportuno terminare l'esecuzione di un thread (ad esempio se non c'è un numero sufficiente di tasks da eseguire)
• Sottomette i tasks per l'esecuzione al pool di thread.
ULezione 2: JAVA pooling 9Andrea Corradini
THREAD POOLING: IL GESTORE
• L'applicazione sottomette un task T al gestore del pool di thread
• Il gestore sceglie un thread dal pool per l'esecuzione di T.
Le scelte possibili sono:– utilizzare un thread attivato in precedenza, ma inattivo al momento
dell'arrivo del nuovo task– creare un nuovo thread, purchè non venga superata la dimensione
massima del pool– memorizzare il task in una struttura dati, in attesa di eseguirlo– respingere la richiesta di esecuzione del task
• Il numero di threads attivi nel pool può variare dinamicamente
ULezione 2: JAVA pooling 10Andrea Corradini
LIBRERIA java.util.concurrent
• L'implementazione del thread pooling:
– Fino a J2SE 1.4 doveva essere realizzata a livello applicazione
– J2SE 5.0 introduce la libreria java.util.concurrent che contiene
metodi per
• Creare un pool di thread e il gestore associato
• Definire la struttura dati utilizzata per la memorizzazione dei
tasks in attesa
• Decidere specifiche politiche per la gestione del pool
ULezione 2: JAVA pooling 11Andrea Corradini
CREARE UN THREADPOOL EXECUTOR
Il package java.util.concurrent definisce:
• Alcune interfacce che definiscono servizi generici di esecuzione...public interface Executor {
public void execute (Runnable task); }
public interface ExecutorService extends Executor{... }● diverse classi che implementano ExecutorService (ThreadPoolExecutor,
ScheduledThreadPoolExecutor, ...) ● la classe Executors che opera come una Factory in grado di generare
oggetti di tipo ExecutorService con comportamenti predefiniti.
I tasks devono essere incapsulati in oggetti di tipo Runnable e passati a
questi esecutori, mediante invocazione del metodo execute( )
ULezione 2: JAVA pooling 12Andrea Corradini
ESEMPI: IL TASK
public class TakeOff implements Runnable{
int countDown = 3; // Predefinito
public String status( ){return "#" + Thread.currentThread() +
"(" + (countDown > 0 ? countDown: "Via!!!") + "),";
}
public void run( ) {while (countDown-- > 0){
System.out.println(status());try{ Thread.sleep(100);} catch(InterruptedException e){ }
}
}}
ULezione 2: JAVA pooling 13Andrea Corradini
import java.util.concurrent.*;
public class Esecutori1 {
public static void main(String[ ] args) {ExecutorService exec = Executors.newCachedThreadPool();for (int i=0; i<3; i++) {
exec.execute(new TakeOff( )); } } }
newCachedThreadPool ( ) crea un pool in cui quando viene sottomesso un task
• viene creato un nuovo thread se tutti i thread del pool sono occupati nell'esecuzione di altri tasks.
• viene riutilizzato un thread che ha terminato l'esecuzione di un task precedente, se disponibile
Se un thread rimane inutilizzato per 60 secondi, la sua esecuzione termina e
viene rimosso dalla cache.
THREAD POOLING: ESEMPIO 1
ULezione 2: JAVA pooling 14Andrea Corradini
ESEMPIO1: OUTPUT
Output del programma:
#Thread[pool-1-thread-2,5,main](2)
#Thread[pool-1-thread-1,5,main](2)
#Thread[pool-1-thread-3,5,main](2)
#Thread[pool-1-thread-3,5,main](1),
#Thread[pool-1-thread-2,5,main](1),
#Thread[pool-1-thread-1,5,main](1)
#Thread[pool-1-thread-2,5,main](Via!!!),
#Thread[pool-1-thread-1,5,main](Via!!!)
#Thread[pool-1-thread-3,5,main](Via!!!),
ULezione 2: JAVA pooling 15Andrea Corradini
THREAD POOLING: ESEMPIO 2
import java.util.concurrent.*;
public class Esecutori2 {
public static void main(String[]args){ExecutorService exec = Executors.newCachedThreadPool();for (int i=0; i<3; i++){
exec.execute(new TakeOff( ));try {Thread.sleep (4000);}
catch(InterruptedException e) { } } } }
La sottomissione di tasks al pool viene distanziata di 4 secondi. In questo modo
l'esecuzione precedente è terminata ed è possibile riutilizzare un thread
attivato precedentemente
ULezione 2: JAVA pooling 16Andrea Corradini
ESEMPIO 2: OUTPUT
#Thread[pool-1-thread-1,5,main](2)
#Thread[pool-1-thread-1,5,main](1),
#Thread[pool-1-thread-1,5,main](Via!!!),
#Thread[pool-1-thread-1,5,main](2)
#Thread[pool-1-thread-1,5,main](1)
#Thread[pool-1-thread-1,5,main](Via!!!)
#Thread[pool-1-thread-1,5,main](2)
#Thread[pool-1-thread-1,5,main](1)
#Thread[pool-1-thread-1,5,main](Via!!!),
ULezione 2: JAVA pooling 17Andrea Corradini
THREAD POOLING: ESEMPIO 3
import java.util.concurrent.*;
public class Esecutori3 {
public static void main(String[]args){
ExecutorService exec = Executors.newFixedThreadPool(2);
for (int i=0; i<3; i++){
exec.execute(new TakeOff());} } }
newFixedThreadPool (int i) crea un pool in cui, quando viene sottomesso un task
• Viene riutilizzato un thread del pool, se inattivo
• Se tutti i thread sono occupati nell'esecuzione di altri tasks, il task viene inserito in una coda, gestita dall'ExecutorService e condivisa da tutti i thread.
ULezione 2: JAVA pooling 18Andrea Corradini
ESEMPIO 3: OUTPUT
#Thread[pool-1-thread-1,5,main](2),
#Thread[pool-1-thread-2,5,main](2),
#Thread[pool-1-thread-2,5,main](1),
#Thread[pool-1-thread-1,5,main](1),
#Thread[pool-1-thread-1,5,main](Via!!!),
#Thread[pool-1-thread-2,5,main](Via!!!),
#Thread[pool-1-thread-1,5,main](2),
#Thread[pool-1-thread-1,5,main](1),
#Thread[pool-1-thread-1,5,main](Via!!!),
ULezione 2: JAVA pooling 19Andrea Corradini
THREAD POOL EXECUTOR
package java.util.concurrent;
public class ThreadPoolExecutor implements ExecutorService{
public ThreadPoolExecutor ( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue <Runnable> workqueue);
... }
• crea un oggetto di tipo ExecutorService
• consente di definire gestori di thread pool con una politica di gestione personalizzata
ULezione 2: JAVA pooling 20Andrea Corradini
THREAD POOL EXECUTOR
public ThreadPoolExecutor ( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue <Runnable> workqueue);
... }
• corePoolSize, maximumPoolSize, e keepAliveTime controllano la
gestione dei threads del pool
• workqueue è una struttura dati usata per memorizzare gli eventuali
tasks in attesa di esecuzione
ULezione 2: JAVA pooling 21Andrea Corradini
THREAD POOL: GESTIONE DINAMICA
• corePoolSize: dimensione minima del pool
– È possibile allocare corePoolSize thread al momento della creazione
del pool mediante il metodo prestartAllCoreThreads( ). I thread
creati rimangono inattivi in attesa di tasks da eseguire.
– Oppure i thread possono essere creati “on demand”. Quando viene
sottomesso un nuovo task, viene creato un nuovo thread, anche se
alcuni dei threads già creati sono inattivi. L'obiettivo è di riempire il
“core” del pool prima possibile.
• maximumPoolSize:dimensione massima del pool
ULezione 2: JAVA pooling 22Andrea Corradini
THREAD POOL: GESTIONE DINAMICA
• Se sono in esecuzione tutti i core thread, un nuovo task sottomesso viene inserito in una coda Q.– Q deve essere una istanza di BlockingQueue<Runnable>– Q viene passata al momento della costruzione del threadPoolExecutor
(ultimo parametro del costruttore)– E' possibile scegliere diversi tipi di coda (sottoclassi di
BlockingQueue). Il tipo di coda scelto influisce sullo scheduling.
• I task vengono poi prelevati da Q e inviati ai threads che si rendono disponibili
• Solo quando Q risulta piena si crea un nuovo thread attivando così kthreads, corePoolSize ≤ k ≤ maxPoolSize
ULezione 2: JAVA pooling 23Andrea Corradini
THREAD POOL: GESTIONE DINAMICA
Da questo punto in poi, quando viene sottomesso un nuovo task T
• se esiste un thread th inattivo, T viene assegnato a th
• se non esistono threads inattivi, si preferisce sempre accodare un
task piuttosto che creare un nuovo thread
• solo se la coda è piena, si attivano nuovi threads
• Se la coda è piena e sono attivi MaxPoolSize threads, il task viene
respinto e viene sollevata un'eccezione
ULezione 2: JAVA pooling 24Andrea Corradini
THREAD POOL: GESTIONE DINAMICA
Supponiamo che un thread th termini l'esecuzione di un task, e che il pool
contenga k threads
• Se k <= core: il thread si mette in attesa di nuovi tasks da eseguire. L'attesa ė indefinita.
• Se k > core, si considera il timeout definito al momento della costruzione del thread pool– se nessun task viene sottomesso entro il timeout, th termina la sua
esecuzione, riducendo così il numero di threads del pool
• Il timeout è determinato dai parametri long keepAliveTime e TimeUnit unit del costruttore, quindi consiste di: – un valore (es: 50000L) e – l'unità di misura utilizzata (es: TimeUnit.MILLISECONDS)
ULezione 2: JAVA pooling 25Andrea Corradini
THREAD POOL: TIPI DI CODA
• SynchronousQueue: dimensione uguale a 0. Ogni nuovo task T
– viene eseguito immediatamente oppure respinto.
– T viene eseguito immediatamente se esiste un thread inattivo oppure
se è possibile creare un nuovo thread (numero di threads ≤
maxPoolSize)
• LinkedBlockingQueue: dimensione illimitata
– E' sempre possibile accodare un nuovo task, nel caso in cui tutti i tasks
attivi nell'esecuzione di altri tasks
– La dimensione del pool di non supererà mai core
• ArrayBlockingQueue: dimensione limitata, stabilita dal programmatore
ULezione 2: JAVA pooling 26Andrea Corradini
THREAD POOLING: UN ESEMPIO
• Dato un intero K, si vuole calcolare, per ogni valore n < K il valore dell'n-
esimo numero di Fibonacci
• Si definisce un task T che effettua il calcolo del numero di Fibonacci di n
(valore passato come parametro)
• Si attiva un ThreadPoolExecutor, definendo la politica di gestione del pool
di thread mediante i parametri passati al costruttore
• Si passa all'esecutore una istanza di T per ogni n < K, invocando il metodo
execute()
ULezione 2: JAVA pooling 27Andrea Corradini
FIBONACCI TASK (I)
public class FibTask implements Runnable{int n; String id;public FibTask(int n, String id){
this.n = n; this.id = id;} private int fib(int n){
if (n == 0 || n == 1) return n;if (n==1) return 1;return fib(n - 1) + fib(n - 2);
}
ULezione 2: JAVA pooling 28Andrea Corradini
FIBONACCI TASK (II)
public void run( ){try{
Thread t = Thread.currentThread ( );System.out.println("Starting task " + id + " su " + n + " eseguito dal thread " + t.getName( ));System.out.println("Risultato " + fib(n) + " da task " + id +
" eseguito dal thread " + t.getName( ));} catch (Exception e){
e.printStackTrace();}
}
}
ULezione 2: JAVA pooling 29Andrea Corradini
FIBONACCI THREAD POOL
import java.util.concurrent.*;
public class ThreadPoolTest {
public static void main (String [] args){
int nTasks = Integer.parseInt(args[0]); // # di tasks da eseguire// dimensione del core pool
int corePoolSize = Integer.parseInt(args[1]);
// massima dimensione del pool
int maxPoolSize = Integer.parseInt(args[2]);
ThreadPoolExecutor tpe = new ThreadPoolExecutor (corePoolSize,maxPoolSize,50000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>( ) );
ULezione 2: JAVA pooling 30Andrea Corradini
FIBONACCI THREAD POOL
FibTask [ ] tasks = new FibTask[nTasks];
for (int i=0; i< nTasks; i++){
tasks[i] = new FibTask(i, "Task" + i);
tpe.execute(tasks[i]);
System.out.println("dimensione del pool " + tpe.getPoolSize());
}
tpe.shutdown();
}
}
ULezione 2: JAVA pooling 31Andrea Corradini
GESTIONE DINAMICA: ESEMPI
Parametri: tasks= 8, core = 3, MaxPoolSize= 4, SynchronousQueue, timeout=50000msecdimensione del pool 1Starting task Task0 eseguito da pool-1-thread-1Risultato 0 da task Task0 eseguito dapool-1-thread-1dimensione del pool 2dimensione del pool 3dimensione del pool 3Starting task Task3 eseguito da pool-1-thread-1Starting task Task1 eseguito da pool-1-thread-2Starting task Task2 eseguito da pool-1-thread-3dimensione del pool 4Risultato 1 da task Task1 eseguito dapool-1-thread-2Starting task Task4 eseguito da pool-1-thread-4RisultatO 2 da task Task3 eseguito dapool-1-thread-1Risultato 1 da task Task2 eseguito dapool-1-thread-3java.util.concurrent.RejectedExecutionExceptionRisultato 3 da task Task4 eseguito dapool-1-thread-4
ULezione 2: JAVA pooling 32Andrea Corradini
GESTIONE DINAMICA: ESEMPI
Tutti I threads attivati inizialmente mediante tpe.prestartAllCoreThreads( );Parametri: tasks= 8, core = 3, MaxPoolSize= 4, SynchronousQueuedimensione del pool 3Starting task Task0 eseguito da pool-1-thread-3dimensione del pool3Risultato 0 da task Task0 eseguito da pool-1-thread-3dimensione del pool 3Starting task Task2 eseguito da pool-1-thread-1Starting task Task1 eseguito da pool-1-thread-2Risultato 1 da task Task2 eseguito dapool-1-thread-1Risultato 1 da task Task1 eseguito dapool-1-thread-2Starting task Task3 eseguito da pool-1-thread-3dimensione del pool 3Risultato 2 da task Task3 eseguito dapool-1-thread-3dimensione del pool 3
ULezione 2: JAVA pooling 33Andrea Corradini
GESTIONE DINAMICA: ESEMPI
(CONTINUA)
Starting task Task4 eseguito da pool-1-thread-2dimensione del pool 3Starting task Task5 eseguito da pool-1-thread-3Risultato 3 da task Task4 eseguito dapool-1-thread-2dimensione del pool 3Starting task Task6 eseguito da pool-1-thread-1Risultato 5 da task Task5 eseguito dapool-1-thread-3dimensione del pool 3Starting task Task7 eseguito da pool-1-thread-2Risultato 8 da task Task6 eseguito dapool-1-thread-1Risultato 13 da task Task7 eseguito dapool-1-thread-2
ULezione 2: JAVA pooling 34Andrea Corradini
GESTIONE DINAMICA: ESEMPI
Parametri: tasks= 10, core = 3, MaxPoolSize= 4, SynchronousQueue,timeout=0msecdimensione del pool 1Starting task Task0 eseguito da pool-1-thread-1Risultato 0 da task Task0 eseguito dapool-1-thread-1dimensione del pool 2Starting task Task1 eseguito da pool-1-thread-2Risultato 1 da task Task1 eseguito dapool-1-thread-2dimensione del pool 3dimensione del pool 3Starting task Task2 eseguito da pool-1-thread-3Starting task Task3 eseguito da pool-1-thread-2dimensione del pool 3Starting task Task4 eseguito da pool-1-thread-1Risultato 1 da task Task2 eseguito dapool-1-thread-3Risultato 2 da task Task3 eseguito dapool-1-thread-2dimensione del pool 4
ULezione 2: JAVA pooling 35Andrea Corradini
GESTIONE DINAMICA:ESEMPI
(CONTINUA)
Risultato 3 da task Task4 eseguito dapool-1-thread-1Starting task Task5 eseguito da pool-1-thread-4dimensione del pool 3Starting task Task6 eseguito da pool-1-thread-2Risultato 5 da task Task5 eseguito dapool-1-thread-4Starting task Task7 eseguito da pool-1-thread-1dimensione del pool 3Risultato 8 da task Task6 eseguito dapool-1-thread-2dimensione del pool 3Starting task Task8 eseguito da pool-1-thread-4dimensione del pool 3Starting task Task9 eseguito da pool-1-thread-2Risultato 13 da task Task7 eseguito dapool-1-thread-1Risultato 21 da task Task8 eseguito dapool-1-thread-4Risultato 34 da task Task9 eseguito dapool-1-thread-2
ULezione 2: JAVA pooling 36Andrea Corradini
GESTIONE DINAMICA: ESEMPI
Parametri: tasks= 10, core = 3, MaxPoolSize= 4,LinkedBlockingQueuedimensione del pool 1Starting task Task0 eseguito da pool-1-thread-1Risultato 0 da task Task0 eseguito dapool-1-thread-1dimensione del pool 2dimensione del pool 3Starting task Task1 eseguito da pool-1-thread-2Risultato 1 da task Task1 eseguito dapool-1-thread-2Starting task Task3 eseguito da pool-1-thread-2dimensione del pool 3Risultato 2 da task Task3 eseguito dapool-1-thread-2dimensione del pool 3Starting task Task2 eseguito da pool-1-thread-3Starting task Task4 eseguito da pool-1-thread-1Starting task Task5 eseguito da pool-1-thread-2dimensione del pool 3
ULezione 2: JAVA pooling 37Andrea Corradini
GESTIONE DINAMICA:ESEMPI
(CONTINUA)
Risultato 1 da task Task2 eseguito dapool-1-thread-3Risultato 3 da task Task4 eseguito dapool-1-thread-1Risultato 5 da task Task5 eseguito dapool-1-thread-2dimensione del pool 3Starting task Task6 eseguito da pool-1-thread-3dimensione del pool 3Starting task Task7 eseguito da pool-1-thread-1Risultato 8 da task Task6 eseguito dapool-1-thread-3dimensione del pool 3Starting task Task8 eseguito da pool-1-thread-2Risultato 13 da task Task7 eseguito dapool-1-thread-1dimensione del pool 3Starting task Task9 eseguito da pool-1-thread-3Risultato 21 da task Task8 eseguito dapool-1-thread-2Risultato 34 da task Task9 eseguito dapool-1-thread-3
ULezione 2: JAVA pooling 38Andrea Corradini
TERMINAZIONE DI THREADS
• La JVM termina la sua esecuzione quando tutti i thread (non demoni) terminano la loro esecuzione
• Poiché un ExecutorService esegue i tasks in modo asincrono rispetto alla loro sottomissione, è necessario ridefinire il concetto di terminazione, nel caso si utilizzi un ExecutorService
• Un ExecutorService mette a disposizione del programmatore diversi metodi per effettuare lo 'shutdown' dei thrad del pool
• La terminazione può avvenire
in modo graduale. Si termina l'esecuzione dei tasks già sottomessi, ma non si inizia l'esecuzione di nuovi tasks
in modo istantaneo. Terminazione immediata
ULezione 2: JAVA pooling 39Andrea Corradini
TERMINAZIONE DI EXECUTORS
Alcuni metodi definiti dalla interfaccia ExecutorService
• void shutdown( )
• List<Runnable> shutdownNow( )
• boolean isShutdown( )
• boolean isTerminated( )
• boolean awaitTermination(long timeout, TimeUnit unit)
ULezione 2: JAVA pooling 40Andrea Corradini
TERMINAZIONE DI EXECUTORS
• void shutdown( ): graceful termination.
non accetta ulteriori task
i tasks sottomessi in precedenza vengono eseguiti, compresi quelli la cui esecuzione non è ancora iniziata (quelli accodati).
tutti i threads del pool terminano la loro esecuzione
• List<Runnable> shutdowNow( ): immediate termination
non accetta ulteriori tasks,
elimina dalla coda i tasks la cui esecuzione non è ancora iniziata, e li restituisce in una lista
tenta di terminare l'esecuzione dei thread che stanno eseguendo i tasks (tipicamente con interrupt()): quindi non può garantire la terminazione dei thread.
ULezione 2: JAVA pooling 41Andrea Corradini
CALLABLE E FUTURE
• Un oggetto di tipo Runnable incapsula un'attività che viene eseguita in modo asincrono
• Una Runnable si può considerare un metodo asincrono, senza parametri e che non restituisce un valore di ritorno
• Per definire un task che restituisca un valore di ritorno occorre utilizzare le seguenti interfacce:– Callable: per definire un task che può restituire un risultato e sollevare
eccezioni– Future: per rappresentare il risultato di una computazione asincrona.
Definisce metodi per controllare se la computazione è terminata, per attendere la terminazione di una computazione (eventualmente per un tempo limitato),per cancellare una computazione, .....
• La classe FutureTask fornisce una implementazione della interfaccia Future.
ULezione 2: JAVA pooling 42Andrea Corradini
L'INTERFACCIA CALLABLE
public interface Callable<V>{ V call() throws Exception; }
L'interfaccia generica Callable<V>
• contiene il solo metodo call(), analogo al metodo run( ) della interfaccia Runnable, ma che può restituire un valore e sollevare eccezioni controllate
• per definire il codice deltask, occorre implementare il metodo call()
• il parametro di tipo <V> indica il tipo del valore restituito
• Esempio: Callable<Integer> rappresenta una elaborazione asincrona che restituisce un valore di tipo Integer
ULezione 2: JAVA pooling 43Andrea Corradini
CALLABLE: UN ESEMPIO
Definire un task T che calcoli una approssimazione di π, mediante la serie di Gregory-Leibniz (vedi lezione precedente). T restituisce il valore calcolato quando la differenza tra l'approssimazione ottenuta ed il valore di Math.PI risulta inferiore ad una soglia precision. T deve essere eseguito in un thread indipendente.
import java.util.concurrent.*;
public class Pigreco implements Callable<Double>{
private Double precision;
public Pigreco (Double precision) {this.precision = precision;};
public Double call ( ){ Double result = <calcolo dell'approssimazione di π>
return result; }}}
ULezione 2: JAVA pooling 44Andrea Corradini
L'INTERFACCIA FUTURE
• Per poter accedere al valore restituito dalla Callable, occorre costruire un
oggetto di tipo Future<V>, che rappresenta il risultato della computazione
• Per costruire un oggetto di tipo Future se si gestiscono esplicitamente i
threads:
– si costruisce un oggetto della classe FutureTask (che implementa Future
e Runnable) passando un oggetto di tipo Callable al costruttore
– si passa l'oggetto FutureTask al costruttore del thread
• Se si usano i thread pools, si sottomette direttamente l'oggetto di tipo
Callable al pool (con submit()) e si ottiene un oggetto di tipo Future
ULezione 2: JAVA pooling 45Andrea Corradini
L'INTERFACCIA FUTURE
public interface Future <V>{
V get( ) throws ...;
V get (long timeout, TimeUnit) throws ...;
void cancel (boolean mayInterrupt);
boolean isCancelled( );
boolean isDone( ); }
• get( ) si blocca fino alla terminazione del task e restituisce il valore calcolato
• È possibile definire un tempo massimo di attesa della terminazione del task, dopo cui viene sollevata una TimeoutException
• E' possibile cancellare il task e verificare se la computazione è terminata oppure è stata cancellata
ULezione 2: JAVA pooling 46Andrea Corradini
CALLABLE E FUTURE: UN ESEMPIO
import java.util.*;
import java.util.concurrent.*;
public class FutureCallable {
public static void main(String args[])
double precision = ........;
Pigreco pg = new Pigreco(precision);
FutureTask <Double> task= new FutureTask <Double>(pg);
Thread t = new Thread(task);
t.start();
ULezione 2: JAVA pooling 47Andrea Corradini
CALLABLE E FUTURE: UN ESEMPIO
try{
double ris = task.get(1000L, TimeUnit.MILLISECONDS);
System.out.println("valore di isdone" + task.isDone());
System.out.println(ris + "valore di pigreco");
}
catch(ExecutionException e) { e.printStackTrace();}
catch(TimeoutException e)
{ e.printStackTrace();
System.out.println("tempo scaduto");
System.out.println("valore di isdone" + task.isDone());}
catch(InterruptedException e){ } } }
ULezione 2: JAVA pooling 48Andrea Corradini
THREAD POOLING CON CALLABLE
• E' possibile sottomettere un oggetto di tipo Callable<V> ad un thread
pool mediante il metodo submit( )
• Il metodo restituisce direttamente un oggetto O di tipo Future<V>, per
cui non è necessario costruire oggetti di tipo FutureTask
• E' possibile applicare all'oggetto O tutti i metodi visti nei lucidi
precedenti
ULezione 2: JAVA pooling 49Andrea Corradini
THREAD POOLING CON CALLABLE
import java.util.*;
import java.util.concurrent.*;
public class futurepools {
public static void main(String args[])
ExecutorService pool = Executors.newCachedThreadPool ( );double precision = .........;
pigreco pg = new pigreco(precision);
Future <Double> result = pool.submit(pg);
try{ double ris = result.get(1000L, TimeUnit.MILLISECONDS);
System.out.println(ris+"valore di pigreco");}
catch(...........){ }..............}}
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 1Andrea Corradini
Lezione n.3LPR-B-09
Threads: Sincronizzazione e Mutua Esclusione
6/10/2009Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 2Andrea Corradini
SULLA TERMINAZIONE: THREAD DEMONI
• Thread Demone (Daemon): fornisce un servizio, generalmente inbackground, fintanto che il programma è in esecuzione, ma non è considerato parte fondamentale di un programma
• Esempio: thread temporizzatori che scandiscono il tempo per conto di altri threads
• Quando tutti i thread non demoni hanno completato la lori esecuzione, il programma termina, anche se ci sono thread demoni in esecuzione
• Se ci sono thread non demoni in esecuzione, il programma non termina
Esempio: i thread attivati nel thread pool rimangono attivi anche se non esistono task da eseguire
• Si dichiara un thread demone invocando il metodo setdaemon(true), prima di avviare il thread
• Se un thread è un demone, allora anche tutti i threads da lui creati lo sono
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 3Andrea Corradini
TERMINAZIONE: THREAD DEMONIpublic class SimpleDaemon extends Thread {
public SimpleDaemon ( ) {
setDaemon(true);
start( ); }
public void run( ) {
while(true) {
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);}
System.out.println("mi sono svegliato"+this); } }
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 4Andrea Corradini
TERMINAZIONE: THREAD DEMONI
public static void main(String[ ] args) {for(int i = 0; i < 5; i++)
new SimpleDaemon( ); Thread.sleep(300);
} }
• il main crea 5 threads demoni
• ogni thread 'si addormenta' e si risveglia per un certo numero di volte, poi quando il main termina (non daemon thread), anche i threads terminano)
• Se pongo setDaemon(false) (il default), il programma non termina, i thread continuano ad 'addormentarsi' e 'risvegliarsi'.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 5Andrea Corradini
GRUPPI DI THREAD• Alcuni programmi contengono un gran numero di thread. Può essere utile
organizzare i thread in una struttura gerarchica in base alle loro funzionalità.
• Esempio: un browser attiva molti thread il cui compito è scaricare le immagini contenute in una pagina web. Se l'utente preme il pulsante stop(), è comodo utilizzare un metodo per interrompere tutti i threads simultaneamente.
• Gruppi di thread: sono insiemi di thread e di (sotto)gruppi di thread, raggruppati per esempio in base alle loro funzionalità, in modo che si possa lavorare su tutti threads di un gruppo simultaneamente.
ThreadGroup g = new ThreadGroup("GruppoPadre");ThreadGroup f = new ThreadGroup(g, "GruppoFiglio");Thread t1 = new Thread(g, aTask, "T1");Thread t2 = new Thread(g, anotherTask, "T2");............g.interrupt( ); // interrompe tutti i thread del gruppo
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 6Andrea Corradini
CONDIVISIONE RISORSE TRA THREADS
• Più thread attivati da uno stesso programma possono condividere un insieme di oggetti. Schema tipico: gli oggetti vengono passati al costruttore del thread:
public class OggettoCondiviso { ........ }
public class Condivisione extends Thread {OggettoCondiviso oc;public Condivisione (OggettoCondiviso oc) {this.oc = oc;};public void run (){ ...... };
public static void main(String args[ ]){ OggettoCondiviso oc = new OggettoCondiviso();
new Condivisione(oc).start();
new Condivisione(oc).start(); }
}
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 7Andrea Corradini
ACCESSO A RISORSE CONDIVISE
• L'interazione incontrollata dei threads sull'oggetto condiviso può produrre risultati non corretti
• Consideriamo il seguente esempio:
Definiamo una classe EvenValue che implementa un generatore di numeri pari.
Ogni oggetto istanza della classe ha un valore uguale ad un numero pari
Se il valore del numero è x, il metodo next( ), definito in EvenValueassegna il valore x+2
Si attivano un insieme di threads che condividono un oggetto di tipo EvenValue e che invocano concorrentemente il metodo next( )
Non si possono fare ipotesi sulla strategia di schedulazione dei threads
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 8Andrea Corradini
ACCESSO A RISORSE CONDIVISE
public interface ValueGenerator {public int next( ); }
public class EvenValue implements ValueGenerator {
private int currentEvenValue = 0;
public int next( ) {++currentEvenValue;Thread.yield ( );++currentEvenValue;return currentEvenValue;}; }
Thread.yield ( ): “suggerisce” allo schedulatore di sospendere l'esecuzione del thread che ha invocato la yield( ) e di cedere la CPU ad altri threads
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 9Andrea Corradini
ACCESSO A RISORSE CONDIVISEimport java.util.concurrent.*;
public class ThreadTester implements Runnable {
private ValueGenerator gen;
public ThreadTester(ValueGenerator gen) {this.gen = gen;}
public void run( ) {for (int i = 0; i < 5; i++){
int val= gen.next();if (val % 2 !=0) System.out.println(Thread.currentThread( )+"errore"+val);
else System.out.println(Thread.currentThread( )+"ok"+val); }}
public static void test(ValueGenerator gen, int count){ExecutorService exec= Executors.newCachedThreadPool();for (int i=0; i<count; i++){
exec.execute(new ThreadTester(gen));};
exec.shutdown( ); }}
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 10Andrea Corradini
ACCESSO A RISORSE CONDIVISE
public class AccessTest {
public static void main(String args[ ]){EvenValue gen = new EvenValue();ThreadTester.test(gen, 2);} }
OUTPUT: Thread[pool-1-thread-1,5,main]ok2Thread[pool-1-thread-2,5,main]ok4Thread[pool-1-thread-1,5,main]errore7Thread[pool-1-thread-2,5,main]errore9Thread[pool-1-thread-1,5,main]ok10Thread[pool-1-thread-2,5,main]errore13Thread[pool-1-thread-1,5,main]ok14Thread[pool-1-thread-1,5,main]errore17Thread[pool-1-thread-2,5,main]ok18Thread[pool-1-thread-2,5,main]ok20
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 11Andrea Corradini
RACE CONDITION
• Perchè si è verificato l'errore?
• Supponiamo che il valore corrente di currentEvenValue sia 0.
• Il primo thread esegue il primo assegnamento++currentEvenValue
e viene quindi deschedulato, in seguito alla yield( ): currentEvenValue assume valore 1
• A questo punto si attiva il secondo thread, che esegue lo stesso assegnamento e viene a sua volta deschedulato, currentEvenValue assume valore 2
• Viene riattivato il primo thread, che esegue il secondo incremento, il valore assume valore 3
• ERRORE! : Il valore restituito dal metodo next( ) è 3.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 12Andrea Corradini
RACE CONDITION e THREAD SAFETY
• Nel nostro caso la race condition (corsa critica) è dovuta alla possibilità che un thread invochi il metodo next( ) e venga deschedulato prima di avere completato l'esecuzione del metodo
• In questo modo la risorsa viene lasciata in uno stato inconsistente
(un solo incremento per currentEvenValue )
• Classi Thread Safe: l'esecuzione concorrente dei metodi definiti nella classe non provoca comportamenti scorretti
• EvenValue non è una classe thread safe
• Per renderla thread safe occorre garantire che le istruzioni contenute all'interno del metodo next( ) vengano eseguite in modo atomico o indivisibile o in mutua esclusione
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 13Andrea Corradini
RACE CONDITION IN SINGOLA ISTRUZIONE• Race Condition: si può verificare anche nella esecuzione di una singola
istruzione di assegnamento
• Consideriamo un'istruzione che incrementa una variabile intera:count = count + 1; o count++;
• L'istruzione può essere elaborata come segue
1) il valore di count viene caricato in un registro
2) si somma 1
3) si memorizza il risultato in count
• Un thread T potrebbe eseguire i passi 1), 2) e poi venire deschedulato,
• Viene quindi schedulato un secondo thread Q che esegue tutta l'istruzione
• T esegue il passo 3) assegnando a count il valore che già contiene: un aggiornamento si è perso.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 14Andrea Corradini
Esercizio 1Si scriva un programma Java che dimostri che si possono verificare delle race conditions anche con una singola istruzione di incremento di una variabile.
Scrivere una classe Counter che offre un metodo next() che incrementa una variabile locale, e un metodo getCount() che ne restituisce il valore.
Scrivere un task TaskCounter che implementa Callable e che riceve nel costruttore un Counter e un intero n. Il task invoca la next() del Counter un numero casuale di volte compreso tra n/2 e n, e restituisce il numero casuale calcolato.
Il main crea un Counter e un pool di threads, in cui esegue M copie di TaskCounter passando a ognuna di esse il Counter e un valore N; quindi stampa la somma dei valori restituiti dagli M threads, e il valore finale del contatore ottenuto con getCount(): se questi due valori sono diversi c'è stata una race condition. M e N devono essere forniti dall'utente.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 15Andrea Corradini
RACE CONDITION: LAZY INITIALIZATION
Un altro esempio di una classe non thread safe
public class LazyInitRace {private ExpensiveObject instance = null;public ExpensiveObject getInstance( ){ if (instance == null)
instance = new ExpensiveObject(); return instance: }}
• Lazy Initialization =
alloca un oggetto solo se non esiste già un'istanza di quell'oggetto
deve assicurare che l'oggetto venga inizializzato una sola volta
getInstance( ) deve restituire sempre la stessa istanza
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 16Andrea Corradini
RACE CONDITIONS: ESEMPIpublic class LazyInitRace {
private ExpensiveObject instance = null;public ExpensiveObject getInstance( ){ if (instance == null)
instance = new ExpensiveObject(); return instance: }}
• Il programma non è corretto perchè contiene una race condition
• Il thread A esegue getInstance(), trova (instance == null), poi viene deschedulato. Il thread B esegue getInstance() e trova a sua volta (instance == null). I due thread restituiscono due diverse istanze di ExpensiveObject
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 17Andrea Corradini
JAVA: MECCANISMI DI LOCK
• Occorre disporre di meccanismi per garantire che un metodo (es: next())
venga eseguito in mutua esclusione quando invocato su di un oggetto
• JAVA offre un meccanismo implicito di locking (intrinsic locks) che
consente di assicurare la atomicità di porzioni di codice eseguite in modo
concorrente sullo stesso oggetto
• L'uso del lock garantisce che se un thread T esegue un metodo di istanza
di un oggetto, nessun altro thread che richiede il lock può eseguire un
metodo sullo stesso oggetto fino a che T non ha terminato l'esecuzione
del metodo
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 18Andrea Corradini
JAVA: IL COMANDO synchronized
• Sincronizzazione di un blocco:
synchronized (obj)
{ // blocco di codice che accede o modifica l'oggetto
}
• L'oggetto obj può essere quello su cui è stato invocato il metodo che contiene il codice (this) oppure un altro oggetto
• Il thread che esegue il blocco sincronizzato deve acquisire il locksull'oggetto obj
• Il lock viene rilasciato nel momento in cui il thread termina l'esecuzione del blocco (es: return, throw, esecuzione dell'ultima istruzione del blocco)
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 19Andrea Corradini
JAVA: RENDERE ATOMICO IL METODO NEXT
public class EvenGenerator implements ValueGenerator{
private int currentEvenValue = 0;
public int next( ){synchronized(this){
++currentEvenValue;Thread.yield();++currentEvenValue;return currentEvenValue;} } }
• Questa modifica consente di evitare le race conditions
• ATTENZIONE: in generale non è consigliabile l'inserimento di una istruzione che blocca il thread che la invoca (sleep( ), yield( ),....) all'interno di un blocco sincronizzato
• Infatti il thread che si blocca non rilascia il lock ed impedisce ad altri threads di invocare il metodo next( ) sullo stesso oggetto
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 20Andrea Corradini
JAVA 1.5: LOCK ESPLICITI
• A partire da JAVA 1.5 è possibile definire ed utilizzare oggetti di tipo lock
import java.util.concurrent.locks.*;
class X { private final ReentrantLock mylock = new ReentrantLock( );// ..public void m( ) {
mylock.lock( ); // block until condition holds
try { // ... method body
} finally {lock.unlock( ) } } }
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 21Andrea Corradini
JAVA 1.5: LOCK ESPLICITI• Vediamo un'altra versione del nostro esempio con lock esplicito:
import java.util.concurrent.locks.*;
public class LockingEvenGenerator implements ValueGenerator {
private int currentEvenValue = 0;
ReentrantLock evenlock=new ReentrantLock( );
public int next( ) {try {
evenlock.lock( );++currentEvenValue;Thread.yield ( );++currentEvenValue;return currentEvenValue;
} finally {evenlock.unlock( );}}}
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 22Andrea Corradini
JAVA: MECCANISMO DEI LOCK
• Lock espliciti: si definisce un oggetto di tipo ReentrantLock( ) Quando un thread invoca il metodo lock( ) su un oggettodi tipo
ReentrantLock( ), il thread rimane bloccato se qualche altro thread ha già acquisito il lock
Quando un thread invoca unlock( ) uno dei thread eventualmente bloccati su quella lock viene risvegliato
• Lock impliciti su metodi
Sono definiti associando al metodo la parola chiave synchronized
Equivale a sincronizzare tutto il blocco di codice che corrisponde al corpo del metodo
L'oggetto su cui si acquisisce il lock è quello su cui viene invocato il metodo
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 23Andrea Corradini
I METODI SYNCHRONIZED
• La parola chiave synchronized nella intestazione di un metodo ha l'effetto di serializzare gli accessi al metodo
public synchronized int next ( )
• Se un thread sta eseguendo il metodo next( ), nessun altro thread potrà
eseguire lo stesso codice sullo stesso oggetto finchè il primo thread non
termina l'esecuzione del metodo
• Implementazione:
Supponiamo che il metodo m synchronized appartenga alla classe C
ad ogni oggetto O istanza di C viene associata un lock, L(O)
quando un thread T invoca m su O, T tenta di acquisire L(O), prima di iniziare l'esecuzione di M. Se T non acquisisce L(O), si sospende
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 24Andrea Corradini
I METODI SYNCHRONIZED
• Se rendiamo synchronized il metodo next(), l'output che otteniamo sarà
Thread[pool-1-thread-1,5,main]ok2
Thread[pool-1-thread-2,5,main]ok4
Thread[pool-1-thread-1,5,main]ok6
Thread[pool-1-thread-2,5,main]ok8
Thread[pool-1-thread-1,5,main]ok10
Thread[pool-1-thread-2,5,main]ok12
Thread[pool-1-thread-1,5,main]ok14
Thread[pool-1-thread-2,5,main]ok16
Thread[pool-1-thread-1,5,main]ok18
Thread[pool-1-thread-2,5,main]ok20
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 25Andrea Corradini
I METODI SYNCHRONIZED
• Importante: il lock è associato all'istanza di un oggetto, non al metodo o alla classe (a meno di metodi statici che vedremo in seguito)
• Diversi metodi sincronizzati invocati sull'istanza dello stesso oggetto competono per lo stesso lock, quindi risultano mutuamente esclusivi
• Metodi sincronizzati che operano su istanze diverse dello stesso oggetto possono essere eseguiti in modo concorrente
• All'interno della stessa classe possono comparire contemporaneamente metodi sincronizzati e non (anche se raramente)
I metodi non sincronizzati possono essere eseguiti in modo concorrente
In ogni istante, su un certo oggetto, possono essere eseguiti concorrentemente più metodi non sincronizzati e solo uno dei metodi sincronizzati della classe
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 26Andrea Corradini
I METODI SYNCHRONIZEDL'esempio seguente crea due istanze dell'oggetto EvenValue1( ) e le passa a due thread distinti. Si considera la versione non sincronizzata del metodo next( )
public class EvenValue1 implements ValueGenerator{
private int currentEvenValue = 0;
public int next( ){ ++currentEvenValue; ++currentEvenValue;return currentEvenValue; } }
public class SynchroTest {public static void main(String args[ ]){ValueGenerator eg1 = new EvenValue1();ValueGenerator eg2 = new EvenValue1();ThreadTester1.test(eg1,eg2);}}
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 27Andrea Corradini
I METODI SYNCHRONIZEDimport java.util.concurrent.*;
import java.util.Random;
public class ThreadTester1 implements Runnable{private ValueGenerator g;public ThreadTester1 (ValueGenerator g) {this.g = g;}public void run( ){
for (int i=0; i<5; i++){int val= g.next();if (val % 2 !=0)System.out.println(Thread.currentThread() + "errore" + val);else System.out.println(Thread.currentThread() + "ok" + val);try {Thread.sleep((int) Math.random() * 1000);} catch (Exception e) { }; }}
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 28Andrea Corradini
I METODI SYNCHRONIZED
public static void test(ValueGenerator g1, ValueGenerator g2){ExecutorService exec= Executors.newCachedThreadPool();exec.execute(new tester(g1));exec.execute(new tester(g2)); exec.shutdown() } }
OUTPUT: il risultato è corretto anche se next() non è sincronizzatoThread[pool-1-thread-1,5,main]ok2
Thread[pool-1-thread-2,5,main]ok2
Thread[pool-1-thread-2,5,main]ok4
Thread[pool-1-thread-2,5,main]ok6
Thread[pool-1-thread-2,5,main]ok8
Thread[pool-1-thread-2,5,main]ok10
Thread[pool-1-thread-1,5,main]ok4
Thread[pool-1-thread-1,5,main]ok6
Thread[pool-1-thread-1,5,main]ok8
Thread[pool-1-thread-1,5,main]ok10
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 29Andrea Corradini
Esercizio 2
• Si consideri il metodo next() della classe Counter dell'Esercizio 1. Modificarlo in modo da renderne l'esecuzione non interrompibile, e rieseguire il programma verificando che non si verificano più race conditions. Fare questo nei tre modi visti: usando un comando synchronized usando un lock esplicito dichiarando synchronized il metodo next()
•
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 30Andrea Corradini
LOCK RIENTRANTI
• I lock intrinsechi di JAVA sono rientranti, ovvero il lock( ) su un oggetto O viene associato al thread che accede ad O.
• se un thread tenta di acquisire un lock che già possiede, la sua richiesta ha successo
• Ovvero... un thread può invocare un metodo sincronizzato m su un oggetto O e all'interno di m vi può essere l'invocazione ad un altro metodo sincronizzato su O e così via
• Il meccanismo dei lock rientranti favorisce la prevenzione di situazioni di deadlock
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 31Andrea Corradini
LOCK RIENTRANTI
• Implementazione delle lock rientranti
Ad ogni lock viene associato un contatore ed un identificatore di thread
Quando un thread T acquisisce un lock, la JVM alloca una struttura che contiene l'identificatore T e un contatore, inizializzato a 0
Ad ogni successiva richiesta dello stesso lock, il contatore viene incrementato mentre viene decrementato quando il metodo termina
• Il lock( ) viene rilasciato quando il valore del contatore diventa 0
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 32Andrea Corradini
REENTRANT LOCK E EREDITARIETA'
public class ReentrantExample {
public synchronized void doSomething( ) {
.............} }
public class ReentrantExtended extends ReentrantExample{
public synchronized void doSomething( ){System.out.println(toString( ) + ": chiamata a doSomething");super.doSomething();} }
• La chiamata super.doSomething( ) si bloccherebbe se il lock non fosse rientrante, ed il programma risulterebbe bloccato (deadlock)
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 33Andrea Corradini
MUTUA ESCLUSIONE: RIASSUNTO
• Interazione implicita tra diversi threads: i thread accedono a risorse condivise.
• Per mantenere consistente l’oggetto condiviso occorre garantire la mutua esclusione su di esso.
• La mutua esclusione viene garantita associando un lock ad ogni oggetto
• I metodi synchronized garantiscono che un thread per volta possa eseguire un metodo sull'istanza di un oggetto e quindi garantiscono lamutua esclusione sull’oggetto
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 34Andrea Corradini
ESERCIZIO 3: ANCORA RACE CONDITIONSSimulare il comportamento di una banca che gestisce un certo numero di conti
correnti. In particolare interessa simulare lo spostamento di denaro tra due
conti correnti.
Ad ogni conto è associato un thread T che implementa un metodo che consente
di trasferire una quantità casuale di denaro tra il conto servito da T ed un
altro conto il cui identificatore è generato casualmente.
• sviluppare una versione non thread safe del programma in modo da evidenziare un comportamento scorretto del programma
• definire 3 versioni thread safe del programma che utilizzino, rispettivamente
Lock esplicite
Blocchi sincronizzati
Metodi sincronizzati
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 35Andrea Corradini
THREADS COOPERANTI: I MONITOR
• L'interazione esplicita tra threads avviene in un linguaggio ad oggetti come JAVA mediante l'utilizzo di oggetti condivisi
• Esempio: produttore/consumatore il produttore P produce un nuovo valore e lo comunica ad un thread consumatore C
• Il valore prodotto viene incapsulato in un oggetto condiviso da P e da C, ad esempio una coda che memorizza i messaggi scambiati tra P e C
• La mutua esclusione sull'oggetto condiviso è garantita dall'uso di metodi synchronized, ma...non è sufficiente garantire sincronizzazioni esplicite
• E' necessario introdurre costrutti per sospendere un thread T quando una condizione C non è verificata e per riattivare T quando diventa vera
• Esempio: il produttore si sospende se il buffer è pieno, si riattiva quando
c'e' una posizione libera
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 36Andrea Corradini
• Monitor = Classe di oggetti utilizzabili da un insieme di threads
• Come ogni classe, il monitor ha un insieme di campi ed un insieme di metodi
• La mutua esclusione può essere garantita dalla definizione di metodi synchronized. Un solo thread per volta si “all'interno del monitor”
• E' necessario inoltre
definire un insieme di condizioni sullo stato dell'oggetto condiviso
implementare meccanismi di sospensione/riattivazione dei threads sulla base del valore di queste condizioni
Implementazioni possibili: • definizione di variabili di condizione• metodi per la sospensione su queste variabili• definizione di code associate alle variabili in cui memorizzare i
threads sospesi
THREADS COOPERANTI: I MONITOR
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 37Andrea Corradini
JAVA ● non supporta variabili di condizione● assegna al programmatore il compito di gestire le condizioni
mediante variabili del programma● definisce meccanismi che consentono ad un thread
● di sospendersi wait( ) in attesa che sia verificata una condizione
● di segnalare con notify( ), notifyall ( ) ad un altro/ad altri threads sospesi che una certa condizione è verificata
● Per ogni oggetto implementa due code:● una coda per i thread in attesa di acquisire il lock● una coda in cui vengono memorizzati tutti i thread sospesi con
la wait( ) (in attesa del verificarsi di una condizione).
THREADS COOPERANTI: I MONITOR
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 38Andrea Corradini
CAMPI DELL'OGGETTO
Metodo sincronizzato
Metodo non sincronizzato..
.
.Coda dei threads in
attesa del verificarsi di una condizione (con wait())
MONITOR
Coda dei threads inattesa della lock
.
.
.
.
THREADS COOPERANTI: I MONITOR
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 39Andrea Corradini
ESEMPIO: PRODUTTORE/CONSUMATORE
• Produttore/Consumatore: due thread si scambiano dati attraverso un oggetto condiviso buffer
• Ogni thread deve acquisire il lock sull'oggetto buffer, prima di inserire/prelevare elementi
• Una volta acquisito il lock
il consumatore controlla se c'è almeno un elemento nel buffer: • in caso positivo, preleva un elemento dal buffer e risveglia
l'eventuale produttore in attesa;• se il buffer è vuoto si sospende,
il produttore controlla se c'è almeno una posizione libera nel buffer:• in caso positivo, inserisce un elemento nel buffer e risveglia
l'eventuale consumatore in attesa,• se il buffer è pieno si sospende.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 40Andrea Corradini
I METODI WAIT/NOTIFY
Metodi d'istanza invocati sull'oggetto condiviso (se non compare il riferimento
all'oggetto, ovviamente l'oggetto implicito riferito è this)
• void wait( ) sospende il thread sull'oggetto
• void wait(long timeout) sospende per al massimo timeout millisecondi
• void notify( ) risveglia un thread in attesa sull'oggetto
• void notifyall( ) risveglia tutti i threads in attesa sull'oggetto
Tutti questi metodi
sono definiti nella classe Object (tutti le classi ereditano da Object,....)
per invocare questi metodi occorre aver acquisito il lock sull'oggetto, altrimenti viene lanciata una IllegalMonitorStateException. Quindi vanno invocati all'interno di un metodo o di un blocco sincronizzato, o dopo aver acquisito un lock eslpicito.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 41Andrea Corradini
WAIT, NOTIFY E IL LOCK
wait( )
rilascia il lock sull'oggetto prima di sospendere il thread corrente
quando risvegliato da una notify( ), il thread compete per riacquisire il lock
notify( )
risveglia uno dei thread (non si sa quale...) nella coda di attesa dell'oggetto; non rilascia immediatamente il lock
notifyAll( )
risveglia tutti i threads in attesa sull'oggetto; non rilascia il lock
Tutti i thread risvegliati competono per l'acquisizione del lock sull'oggetto, e verranno eseguiti uno alla volta, quando riusciranno a riacquisire il lock.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 42Andrea Corradini
WAIT E NOTIFY
• Il metodo wait() permette di attendere il cambiamento di una condizione sullo stato dell'oggetto “fuori dal monitor”, in modo passivo
• Evita il controllo ripetuto di una condizione (polling)
• A differenza di sleep() e di yield() rilascia il lock sull'oggetto
• Quando ci si sospende su di una condizione, occorre controllarla quando si viene risvegliati:
synchronized(obj){while (<condizione non soddisfatta>)
obj.wait();... // esegui l'azione per cui richiedevi la condizione
}
• L'invocazione di un metodo wait(), notify(), notifyall() fuori da un metodo synchronized solleva l'eccezione IllegalMonitorException( ): prima di invocare questi metodi occorre aver acquisito il lock su un oggetto condiviso
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 43Andrea Corradini
PRODUTTORE/CONSUMATORE: IL BUFFER
public class Buffer {
int[] buffer; int size = 0; // Array Parzialmente Riempito
public Buffer(int capacity){ buffer = new int[capacity]; }
public synchronized int get() throws InterruptedException{while (size == 0) wait();int result = buffer[--size]; // restituisce l'ultimo notify(); // elemento inseritoreturn result; }
public synchronized void put(int n) throws InterruptedException{while (size == buffer.length) wait();buffer[size++] = n;notify(); } }
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 44Andrea Corradini
IL PRODUTTOREpublic class Producer implements Runnable {
private Buffer buf;
public Producer(Buffer buf){ this.buf = buf; }
public void run() {try{
while(true){int next = (int)(Math.random() * 10000);buf.put(next);System.out.println("[Producer] Put " + next);Thread.sleep((int)(Math.random() * 500)); }
} catch (InterruptedException e){System.out.println("[Producer] Interrupted");
} } }
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 45Andrea Corradini
IL CONSUMATORE
public class Consumer implements Runnable {
private Buffer buf;
public Consumer(Buffer buf){ this.buf = buf; }
public void run() {try{
while(true){System.out.println("[Consumer] Got " + buf.get());Thread.sleep((int)(Math.random() * 500)); }
}catch (InterruptedException e){System.out.println("[Consumer] Interrupted");
} } }
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 46Andrea Corradini
PRODUTTORE/CONSUMATORE: IL MAIN
import java.util.concurrent.*;
public class TestProducerConsumer {
public static void main(String[] args) {
Buffer buf = new Buffer(5);
Consumer cons = new Consumer(buf);
Producer prod = new Producer(buf);
ExecutorService exec = Executors.newFixedThreadPool(2);
exec.execute(cons);
exec.execute(prod);
try { Thread.sleep(10000); } catch (InterruptedException e) { }
exec.shutdownNow();
} }
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 47Andrea Corradini
PRODUCER/CONSUMER: OUTPUT
Esempio di output del programma:[Consumer] Got 7493[Producer] Put 7493[Producer] Put 4495[Consumer] Got 4495[Producer] Put 1515[Producer] Put 8502[Consumer] Got 8502[Producer] Put 4162[Producer] Put 954[Producer] Put 880[Consumer] Got 880[Producer] Put 8503[Producer] Put 4980[Consumer] Got 4980[Consumer] Got 8503[Producer] Put 3013[Consumer] Got 3013[Consumer] Interrupted[Producer] Interrupted
• Si noti che poiché il buffer ha una politica Last In First Out (LIFO), l'ordine non viene preservato. Per esempio, il numero 1515 non viene mai estratto dal buffer.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 48Andrea Corradini
ESERCIZIO 4
• La classe Buffer ha una politica Last In First Out (LIFO), quindi non preserva l'ordine. Scrivere la classe CircularBuffer che estende Buffer e realizza una politica FIFO, gestendo l'array in modo circolare.
• Definire le interfacce generiche Producer<E>, Consumer<E> e Buffer<E>, che definiscono un sistema produttore/consumatore per un generico tipo di dati E.
• Implementare le interfacce in modo che il produttore produca una sequenza di stringhe, leggendole da un file passato come parametro al task, e il consumatore scriva le stringhe che prende dal buffer in un altro file.
• Nel main, creare e attivare un produttore e due o più consumatori. Verificare che la concatenazione dei file generati dai consumatori sia uguale, a meno dell'ordine delle righe, al file letto dal produttore.
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 49Andrea Corradini
ESERCIZIO 5 (a)
Il laboratorio di Informatica del Polo Marzotto è utilizzato da tre tipi di utenti, studenti, tesisti e professori ed ogni utente deve fare una richiesta al tutor per accedere al laboratorio. I computers del laboratorio sono numerati da 1 a 20. Le richieste di accesso sono diverse a seconda del tipo dell'utente:
a) i professori accedono in modo esclusivo a tutto il laboratorio, poichè hanno necessità di utilizzare tutti i computers per effettuare prove in rete.
b) i tesisti richiedono l'uso esclusivo di un solo computer, identificato dall'indice i, poichè su quel computer è istallato un particolare software necessario per lo sviluppo della tesi.
c) gli studenti richiedono l'uso esclusivo di un qualsiasi computer.
I professori hanno priorità su tutti nell'accesso al laboratorio, i tesisti hanno priorità sugli studenti. (prosegue nella pagina successiva)
ULezione 3: Threads: Sincronizzazione e Mutua Esclusione 50Andrea Corradini
ESERCIZIO 5(b)
Scrivere un programma JAVA che simuli il comportamento degli utenti e del tutor. Il programma riceve in ingresso il numero di studenti, tesisti e professori che utilizzano il laboratorio ed attiva un thread per ogni utente. Ogni utente accede k volte al laboratorio, con k generato casualmente. Simulare l'intervallo di tempo che intercorre tra un accesso ed il successivo e l'intervallo di permanenza in laboratorio mediante il metodo sleep. Il tutor deve coordinare gli accessi al laboratorio. Il programma deve terminare quando tutti gli utenti hanno completato i loro accessi al laboratorio.
ULezione 4: Indirizzi IP e URL 1Vincenzo Gervasi
Lezione n.4LPR-A-09
Indirizzi IP e URL
13/10/2009Vincenzo Gervasi
Università degli Studi di Pisa Dipartimento di Informatica
ULezione 4: Indirizzi IP e URL 2Vincenzo Gervasi
PROGRAMMAZIONE DI RETE: INTRODUZIONEProgrammazione di rete:
sviluppare applicazioni definite mediante due o più processi in esecuzione
su hosts diversi, distribuiti sulla rete. I processi cooperano per realizzare
una certa funzionalità Cooperazione: richiede lo scambio di informazioni (comunicazione) tra i
processi Comunicazione = Utilizza protocolli (cioè insieme di regole che i partners
della comunicazione devono seguire per poter comunicare) Alcuni protocolli utilizzati in INTERNET:
– IP (Internet Protocol)– TCP (Transmission Control Protocol) un protocollo connection-oriented– UDP (User Datagram Protocol) protocollo connectionless
ULezione 4: Indirizzi IP e URL 3Vincenzo Gervasi
PROGRAMMAZIONE DI RETE: INTRODUZIONE
Per identificare un processo con cui si vuole comunicare occorre conoscere:
• la rete in cui si trova l’host su cui e’ in esecuzione il processo• l’host all’interno della rete• il processo in esecuzione sull’host
– Identificazione della rete e dell'host• definita dal protocollo IP (Internet Protocol)
– Identificazione del processo• utilizza il concetto di porta
– Porta• Intero da 0 a 65535
ULezione 4: Indirizzi IP e URL 4Vincenzo Gervasi
IL PROTOCOLLO IP
Il Protocollo IP (Internet Protocol) definisce
• un sistema di indirizzamento per gli hosts
• la definizione della struttura del pacchetto IP
• un insieme di regole per la spedizione/ricezione dei pacchetti
Due versioni del protocollo IP sono attualmente utilizzate in Internet:
• IPV4 (IP Versione 4)
• IPV6 (IP versione 6)
– IPV6 introduce uno spazio di indirizzi di dimensione maggiore rispetto a IPV4 (i cui indirizzi cominciano a scarseggiare...)
– Di uso ancora limitato
ULezione 4: Indirizzi IP e URL 5Vincenzo Gervasi
INDIRIZZAMENTO DEGLI HOSTS
Materiale di riferimento: Pitt, capitolo 2 Ogni host di una rete IPV4 o IPV6 è connesso alla rete mediante una o
più interfacce Ogni interfaccia è caratterizzata da un indirizzo IP Indirizzo IP
− IPV4: numero rappresentato su 32 bits (4 bytes)− IPV6:numero rappresentato su 128 bits (16 bytes)
Multi-homed host: un host che possiede un insieme di interfacce verso la rete, e quindi un insime di indirizzi IP (uno per ogni interfaccia)– gateway tra sottoreti IP− routers
ULezione 4: Indirizzi IP e URL 6Vincenzo Gervasi
INDIRIZZI IP
Un indirizzo IPV4
32 bits Ognuno dei 4 bytes, viene interpretato come un numero decimale
senza segno Rappresentato come sequenza di 4 valori da 0 a 255 separati da “.”
Un indirizzo IPV6 128 bits sequenza di 8 gruppi di 4 cifre esadecimali, separati da “:”, es.
2000:fdb8:0000:0000:0001:00ab:853c:39a12000:fdb8::1:00ab:853c:39a1
10010001 00001010 00100010 00000011
145. 10. 34. 3
ULezione 4: Indirizzi IP e URL 7Vincenzo Gervasi
INDIRIZZI IP E NOMI DI DOMINIO
• Gli indirizzi IP sono indispensabili per la funzionalità di istradamento dei pacchetti effettuata dai routers, ma sono poco leggibili per gli utenti della rete
• Soluzione: assegnare un nome simbolico (unico?) a ogni host si utilizza uno spazio di nomi gerarchico, per esempio:
fujih1.cli.di.unipi.it (host fujih1 nel dominio cli.di.unipi.it ) livelli della gerarchia separati dal punto. nomi interpretati da destra a sinistra
• Risoluzione di indirizzi IP: corrispondenza tra nomi ed indirizzi IP
• La risoluzione viene gestita da un servizio di nomi (“servizio DNS”) DNS = Domain Name System
ULezione 4: Indirizzi IP e URL 8Vincenzo Gervasi
INDIRIZZAMENTO A LIVELLO DI PROCESSI
• Su ogni host possono essere attivi contemporaneamente più servizi (es: e-mail, ftp, http,…)
• Ogni servizio viene incapsulato in un diverso processo• L’indirizzamento di un processo avviene mediante una porta• Porta = intero compreso tra 0 e 65535. Non è un dispositivo fisico, ma
un'astrazione per individuare i singoli servizi (processi).• Porte comprese tra 1 e 1023 riservati per particolari servizi. • In Linux: solo i programmi in esecuzione con diritti di root possono
ricevere dati da queste porte. Chiunque può inviare dati a queste porte.Esempi: porta 7 echo porta 21 ftp
porta 22 ssh porta 32 telnet porta 80 HTTP
• In LINUX: controllare il file /etc/services
ULezione 4: Indirizzi IP e URL 9Vincenzo Gervasi
SOCKETS
• Socket: astrae il concetto di 'communication endpoint'
• Individuato da un indirizzo IP e da un numero di porta
• Socket in JAVA: istanza di una di queste classi (che vedremo in futuro)
– Socket
– ServerSocket
– DatagramSocket
– MulticastSocket
ULezione 4: Indirizzi IP e URL 10Vincenzo Gervasi
JAVA: LA CLASSE INETADDRESSpublic static InetAddress [ ] getAllByName (String hostname)
throws UnKnownHostException
utilizzata nel caso di hosts che posseggano piu indirizzi (es: web servers)
public static InetAddress getLocalHost () throws UnKnownHostException
per reperire nome simbolico ed indirizzo IP del computer locale
Getter Methods = Per reperire i campi di un oggetto di tipo InetAddress
public String getHostName ( ) // può fare una reverse resolution
r
public byte [ ] getAddress ( )
public String getHostAddress ( )
ULezione 4: Indirizzi IP e URL 11Vincenzo Gervasi
JAVA: LA CLASSE INETADDRESS
public static InetAddress getByName (String hostname) throws UnKnownHostException
se il valore di hostname è l’indirizzo IP (una stringa che codifica la
dotted form dell'indirizzo IP)
la getByName non contatta il DNS il nome dell’host non viene impostato nell'oggetto InetAddress il DNS viene contattato solo quando viene richiesto esplicitamente il
nome dell’host tramite il metodo getter getHostName( ) la getHostName( ) non solleva eccezione, se non riesce a risolvere
l’indirizzo IP.
ULezione 4: Indirizzi IP e URL 12Vincenzo Gervasi
JAVA: LA CLASSE INETADDRESS
• accesso al DNS: operazione potenzialmente molto costosa
• i metodi descritti effettuano caching dei nomi/indirizzi risolti.
• nella cache vengono memorizzati anche i tentativi di risoluzione non andati a buon fine (di default: per un certo numero di secondi)
• se creo un InetAddress per lo stesso host, il nome viene risolto con i dati nella cache (di default: per sempre)
• permanenza dati nella cache: per default alcuni secondi se la risoluzione non ha avuto successo, illimitato altrimenti. Problemi: indirizzi dinamici..
• java.security.Security.setProperty consente di impostare il numero di secondi in cui una entry nella cache rimane valida, tramite le proprietà
networkaddress.cache.ttl e networkaddress.cache.negative.ttl
esempio:
java.security.Security.setProperty("networkaddress.cache.ttl" , "0");
ULezione 4: Indirizzi IP e URL 13Vincenzo Gervasi
LA CLASSE INETADDRESS:ESEMPIO DI UTILIZZO
• implementazione in JAVA della utility UNIX nslookup
• nslookup
consente di tradurre nomi di hosts in indirizzi IP e viceversa
i valori da tradurre possono essere forniti in modo interattivo oppure da linea di comando
si entra in modalità interattiva se non si forniscono parametri da linea di comando
consente anche funzioni più complesse (vedere LINUX)
ULezione 4: Indirizzi IP e URL 14Vincenzo Gervasi
LA CLASSE INETADDRESS:ESEMPIO DI UTILIZZO
import java.net.*;import java.io.*;
public class HostLookUp {public static void main (String [ ] args) {if (args.length > 0) { for (int i=0; i<args.length; i++) {
System.out.println (lookup(args[i])) ;}
}else {/* modalita’ interattiva*/ }
ULezione 4: Indirizzi IP e URL 15Vincenzo Gervasi
LA CLASSE INETADDRESS:ESEMPIO DI UTILIZZO
private static boolean isHostName (String host){
char[ ] ca = host.toCharArray(); for (int i = 0; i < ca.length; i++) {
if(!Character.isDigit(ca[i])) {if (ca[i] != '.')
return true; }
}return false;
}
ULezione 4: Indirizzi IP e URL 16Vincenzo Gervasi
LA CLASSE INETADDRESS:ESEMPIO DI UTILIZZO
private static String lookup(String host) {InetAddress node;try {
node = InetAddress.getByName(host); System.out.println(node); if (isHostName(host))
return node.getHostAddress( );else
return node.getHostName ( ); } catch (UnknownHostException e)
return "non ho trovato l’host"; }
ULezione 4: Indirizzi IP e URL 17Vincenzo Gervasi
Esercizio 1
• Scrivere un programma Java Resolve che traduca una sequenza di nomi simbolici di host nei corrispondenti indirizzi IP.
• Resolve legge i nomi simbolici da un file, il cui nome è passato da linea di comando oppure richiesto all'utente.
• Si deve definire un task che estenda l’interfaccia Callable, e che, ricevuto come parametro un nome simbolico, provvede a tradurre il nome ritornando un InetAddress.
• Per ottimizzare la ricerca, si deve attivare un pool di thread che esegua i task in modo concorrente. Ogni volta che si sottomette al pool di thread un task, si ottiene un oggetto Future<InetAddress>, che deve essere aggiunto ad un ArrayList.
• Infine, si scorre l’ArrayList, stampando a video gli InetAddress.
ULezione 4: Indirizzi IP e URL 18Vincenzo Gervasi
• Scrivere un programma che enumeri e stampi a video tutte le interfacce di rete del computer, usando i metodi della classe java.net.NetworkInterface.
• Usare il metodo statico getNetworkInterfaces() per ottenere una Enumeration di NetworkInterface.
• Per ogni NetworkInterface, stampare gli indirizzi IP associati ad essa (IPv4 e IPv6) e il nome dell’interfaccia.
Esercizio 2
ULezione 4: Indirizzi IP e URL 19Vincenzo Gervasi
• Scrivere un programma che ricerca una parola chiave (key) nei file contenuti in una directory (fornita dall'utente) e nelle sue sottodirectory. Per ogni file che contiene key, si deve visualizzare il nome dei file e il contenuto della prima riga trovata che contiene key.
• Creare una classe FindKeyword che implementa Callable, alla quale si può passare una directory come parametro del costruttore, e che ritorna un array di stringhe del formato
<nome file> : <contenuto riga che contiene key>
• Il metodo search della classe FindKeyword implementa la ricerca di key all’interno di un singolo file, e ritorna una stringa formattata come sopra oppure null se key non compare.
• Creare un pool di thread a cui vengono sottomessi un task FindKeyword per ogni directory/sottodirectory, e usare gli oggetti Future restituiti per stampare a video i risultati ottenuti.
Esercizio 3
ULezione 4: Indirizzi IP e URL 20Vincenzo Gervasi
Uniform Resource Locator
• URL è un acronimo per Uniform Resource Locator• Un riferimento (un indirizzo) per una risorsa su Internet.
Di solito un URL è il nome di un file su un host. Ma può anche puntare ad altre risorse:
una query per un database;l’output di un comando.
Es: http://java.sun.comhttp: identificativo del protocollo.java.sun.com: nome della risorsa.
ULezione 4: Indirizzi IP e URL 21Vincenzo Gervasi
Nomi di risorsa
• Il nome di una risorsa è composto da: Host Name: il nome dell’host su cui si trova la risorsa. Filename: il pathname del file sull’host. Port Number: il numero della porta su cui connettersi
(opzionale). Reference: un riferimento ad una specifica locazione
all’interno del file (opzionale).• Nel caso del protocollo http, se il Filename è omesso (o
finisce per /), il web server è configurato per restituire un file di default all’interno del path (ad es. index.html, index.php, index.asp).
ULezione 4: Indirizzi IP e URL 22Vincenzo Gervasi
URL e URI
• Un URI (Uniform Resource Identifier) è un costrutto sintattico che specifica, tramite le varie parti che lo compongono, una risorsa su Internet: [schema:]ParteSpecificaSchema[#frammento] dove ParteSpecificaSchema ha la struttura
[//autorita’][percorso][?query]
• Un URL è un tipo particolare di URI: contiene sufficienti informazioni per individuare e ottenere una risorsa.
• Altre URI, ad es: URN:ISBN:0-395-36341-1 non specificano come individuare la risorsa. In questo caso, le URI sono dette URN (Uniform Resource
Name).
ULezione 4: Indirizzi IP e URL 23Vincenzo Gervasi
URL in Java
• In JAVA per creare un oggetto URL: URL cli = new URL(“http://www.cli.di.unipi.it/”); è un esempio di un URL assoluto.
• È anche possibile creare un URL relativo, che ha la forma URL(URL baseURL, String relativeURL) Esempi:.
URL cli = new URL(“http://www.cli.di.unipi.it/”);URL faq = new URL(cli, “faq”);
che risulterà puntare a http://www.cli.di.unipi.it/faq• I protocolli gestiti da Java con gli URL sono http, https, ftp, file e jar.• I costruttori possono lanciare una MalformedURLException.
ULezione 4: Indirizzi IP e URL 24Vincenzo Gervasi
Parsare un URL
• La classe URL offre metodi per accedere ai componenti di una URLimport java.net.*;import java.io.*;
public class URLReader {public static void main(String[] args) throws Exception {
String url = “http://www.cli.di.unipi.it:80/faq”;URL cli = new URL(url);System.out.println(“protocol = ” + cli.getProtocol());System.out.println(“authority = ” + cli.getAuthority());System.out.println(“host = ” + cli.getHost());System.out.println(“port = ” + cli.getPort());System.out.println(“path = ” + cli.getPath());System.out.println(“query = ” + cli.getQuery());System.out.println(“filename = ” + cli.getFile());System.out.println(“ref = ” + cli.getRef());
}}
ULezione 4: Indirizzi IP e URL 25Vincenzo Gervasi
Parsare un URL
• Eseguendo l'esempio precedente si ottiene:
protocol = httpauthority = www.cli.di.unipi.it:80host = www.cli.di.unipi.itport = 80path = /faqquery = nullfilename = /faqref = null
ULezione 4: Indirizzi IP e URL 26Vincenzo Gervasi
Leggere da un URL
• Una volta creato un oggetto URL si può invocare il metodo openStream() per ottenere uno stream da cui poter leggere il contenuto dell’URL.
• Il metodo openStream() ritorna un oggetto java.io.InputStream leggere da un URL è analogo a leggere da uno stream di input.
import java.net.*;import java.io.*;public class URLReader {
public static void main(String[] args) throws Exception {URL cli = new URL(“http://www.cli.di.unipi.it/”);BufferedReader in = new BufferedReader(
new InputStreamReader(cli.openStream()));String inputLine;while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);in.close();
}}
ULezione 4: Indirizzi IP e URL 27Vincenzo Gervasi
Leggere da un URL
• Una volta creato un oggetto URL si può invocare il metodo openStream() per ottenere uno stream da cui poter leggere il contenuto dell’URL.
• Il metodo openStream() ritorna un oggetto java.io.InputStream leggere da un URL è analogo a leggere da uno stream di input.
import java.net.*;import java.io.*;public class URLReader {
public static void main(String[] args) throws Exception {URL cli = new URL(“http://www.cli.di.unipi.it/”);BufferedReader in = new BufferedReader(
new InputStreamReader(cli.openStream()));String inputLine;while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);in.close();
}}
args[0]
ULezione 4: Indirizzi IP e URL 28Vincenzo Gervasi
Leggere da un URL
• Eseguendo l'esempio precedente, si ottiene:
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”“http://www.w3.org/TR/html4/loose.dtd”><html lang=”it”><head><meta http-equiv=”Content-Type” content=”text/html;charset=iso-8859-1”><link rel=”stylesheet” href=”/cdc.css” type=”text/css”><link rel=”alternate” type=”application/rss+xml”title=”Ultime notizie” href=”/feed.php”><title>Home CdC </title></head><body bgcolor=”#ced8e0”>....• Può essere necessario impostare il proxy su Java:java -Dhttp.proxyHost=proxyhost [-Dhttp.proxyPort=portNumber] URLReader
ULezione 4: Indirizzi IP e URL 29Vincenzo Gervasi
Connettersi a un URL• Nell’esempio precedente, la connessione all’URL veniva effettuata solo dopo
aver invocato openStream().
• In alternativa, è possibile invocare il metodo openConnection() per ottenere un oggetto URLConnection. Utile nel caso in cui si vogliano settare alcuni parametri o proprietà della
richiesta prima di connettersi. es: cliConn.setRequestProperty(“User-Agent”, “Mozilla/5.0”);
• Successivamente, si invoca URLConnection.connect().
URL cli = new URL(“http://www.cli.di.unipi.it/”);URLConnection cliConn = cli.openConnection();cliConn.connect();BufferedReader in = new BufferedReader(
new InputStreamReader(cliConn.getInputStream()));
ULezione 4: Indirizzi IP e URL 30Vincenzo Gervasi
URL e HTTPS• Tutto quanto detto vale anche per le connessioni sicure via HTTPS
import java.net.*;import java.io.*;
public class SecureClientUrl {public static void main(String[] args) {
try {URL url = new URL(“https://www.verisign.com”);URLConnection conn = url.openConnection();BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));String inputLine;while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);in.close();
} catch (Exception e){e.printStackTrace();}}
}
ULezione 6: Il Protocollo UDP, sockets e datagrams 1Andrea Corradini
Lezione n.6LPR-B-09
Il protocollo UDP:Socket e Datagram
27/10/2009Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
ULezione 6: Il Protocollo UDP, sockets e datagrams 2Andrea Corradini
MECCANISMI DI COMUNICAZIONE TRA PROCESSI
Meccanismi di comunicazione tra processi (IPC)
Processo 2(Destinatario)
HOST 2HOST 1
Processo 1(Mittente)
HOST 1
Processo 1(Mittente)
HOST 1
Processo 1(Destinatario)
HOST n
Processo n(Destinatario)
dato
.
.
.
dato
dato
ULezione 6: Il Protocollo UDP, sockets e datagrams 3Andrea Corradini
COMUNICAZIONECONNECTION ORIENTED VS. CONNECTIONLESS
Comunicazione Connection Oriented (come una chiamata telefonica)• creazione di una connessione (canale di comunicazione dedicato)
tra mittente e destinatario• invio dei dati sulla connessione • chiusura della connessione
Comunicazione Connectionless (come l'invio di una lettera)● non si stabilisce un canale di comunicazione dedicato ● mittente e destinatario comunicano mediante lo scambio di pacchetti
ULezione 6: Il Protocollo UDP, sockets e datagrams 4Andrea Corradini
Indirizzamento: Connection Oriented: l’indirizzo del destinatario è specificato
al momento della connessione Connectionless: l’indirizzo del destinatario viene specificato in
ogni pacchetto (per ogni send)
• Ordinamento dei dati scambiati: Connection Oriented: ordinamento dei messaggi garantito Connectionless: nessuna garanzia sull’ordinamento dei messaggi
• Utilizzo: Connection Oriented: grossi streams di dati Connectionless : invio di un numero limitato di dati
COMUNICAZIONECONNECTION ORIENTED VS. CONNECTIONLESS
ULezione 6: Il Protocollo UDP, sockets e datagrams 5Andrea Corradini
COMUNICAZIONE:CONNECTION ORIENTED VS. CONNECTIONLESS
Protocollo UDP (User Datagram Protocol) =
connectionless, trasmette pacchetti di dati (Datagrams)
• ogni datagram deve contenere l’indirizzo del destinatario
• datagrams spediti dallo stesso processo possono seguire percorsi diversi ed arrivare al destinatario in ordine diverso rispetto all’ordine di spedizione
Protocollo TCP (Transmission Control Protocol) =
trasmissione connection-oriented o stream oriented• viene stabilita una connessione tra mittente e destinatario • su questa connesione si spedisce una sequenza di dati = stream di dati • per modellare questo tipo di comunicazione in JAVA si possono sfruttare i
diversi tipi di stream definiti dal linguaggio.
ULezione 6: Il Protocollo UDP, sockets e datagrams 6Andrea Corradini
IPC: MECCANISMI BASE
Una API per la comunicazione tra processi (IPC= Inter Process
Communication) deve garantire almeno le seguenti funzionalità
• Send per trasmettere un dato al processo destinatario
• Receive per ricevere un dato dal processo mittente
• Connect (solo per comunicazione connection oriented) per stabilire una connessione logica tra mittente e destinatario
• Disconnect per eliminare una connessione logica
Possono esistere diversi tipi di send/receive (sincrona/asincrona,
simmetrica/asimmetrica)
ULezione 6: Il Protocollo UDP, sockets e datagrams 7Andrea Corradini
IPC: MECCANISMI BASE
Un esempio: HTTP (1.0)
• il processo che esegue il Web browser esegue una connect per stabilire una connessione con il processo che esegue il Web Server
• il Web browser esegue una send, per trasmettere una richiesta al Web Server (operazione GET)
• il Web server esegue una receive per ricevere la richiesta dal Web Browser, quindi a sua volta esegue una send per inviare la risposta
• i due processi eseguono una disconnect per terminare la connessione
HTTP 1.1: Più richieste su una connessione (più send e receive).
ULezione 6: Il Protocollo UDP, sockets e datagrams 8Andrea Corradini
IPC: MECCANISMI BASE
Comunicazione sincrona (o bloccante): il processo che esegue la send o lareceive si sospende fino al momento in cui la comunicazione è completata.
send sincrona = completata quando i dati spediti sono stati ricevuti dal destinatario (è stato ricevuto un ack da parte del destinatario)
receive sincrona = completata quando i dati richiesti sono stati ricevuti
send asincrona (non bloccante) = il destinatario invia i dati e prosegue la sua esecuzione senza attendere un ack dal destinatario
receive asincrona = il destinatario non si blocca se i dati non sono arrivati. Possibile diverse implementazioni
ULezione 6: Il Protocollo UDP, sockets e datagrams 9Andrea Corradini
IPC: MECCANISMI BASE
•Receive Non Bloccante.
• se il dato richiesto è arrivato, viene reso disponibile al processo che ha eseguito la receive
• se il dato richiesto non è arrivato:
il destinatario esegue nuovamente la receive, dopo un certo intervallo di tempo (polling)
il supporto a tempo di esecuzione notifica al destinatario l’arrivo del dato (richiesta l’attivazione di un event listener)
ULezione 6: Il Protocollo UDP, sockets e datagrams 10Andrea Corradini
IPC: MECCANISMI BASE
• Comunicazione non bloccante: per non bloccarsi indefinitamente
Timeout – meccanismo che consente di bloccarsi per un intervallo di tempo prestabilito, poi di proseguire comunque l’esecuzione
Threads – l’operazione sincrona può essere effettuate in un thread. Se il thread si blocca su una send/receive sincrona, l’applicazione può eseguire altri thread.
Nel caso di receive sincrona, gli altri threads ovviamente non devono richiedere per l’esecuzione il valore restituito dalla receive
ULezione 6: Il Protocollo UDP, sockets e datagrams 11Andrea Corradini
INVIARE OGGETTI
Invio di strutture dati ed, in generale, di oggetti richiede :
• il mittente deve effettuare la serializzazione delle strutture dati (eliminazione dei puntatori)
• il destinatario deve ricostruire la struttura dati nella sua memoria
Da Wikipedia: La serializzazione è un processo per salvare un oggetto in un supporto di memorizzazione lineare (ad esempio, un file o un'area di memoria), o per trasmetterlo su una connessione di rete. La serializzazione può essere in forma binaria o può utilizzare codifiche testuali (ad esempio il formato XML)... Lo scopo della serializzazione è di trasmettere l'intero stato dell'oggetto in modo che esso possa essere successivamente ricreato nello stesso identico stato dal processo inverso, chiamato deserializzazione.
Il processo di serializzare un oggetto viene anche indicato come marshalling
ULezione 6: Il Protocollo UDP, sockets e datagrams 12Andrea Corradini
JAVA IPC: I SOCKETS
Socket = presa di corrente•Termine utilizzato in tempi remoti in telefonia. La connessione tra due utenti veniva stabilita tramite un operatore che inseriva fisicamente i due estremi di un cavo in due ricettacoli (sockets), ognuno dei quali era assegnato ai due utenti.
Socket è una astrazione che indica una “presa ” a cui un processo si può collegare per spedire dati sulla rete. Al momento della creazione un socket viene collegato ad una porta.
Socket
ULezione 6: Il Protocollo UDP, sockets e datagrams 13Andrea Corradini
Socket Application Program Interface = Definisce un insieme di meccanismiche supportano la comunicazione di processi in ambiente distribuito.
• JAVA socket API: definisce classi diverse per UDP e TCP
Protocollo UDP = DatagramSocket Protocollo TCP = ServerSocket e Socket
JAVA IPC: I SOCKETS
ULezione 6: Il Protocollo UDP, sockets e datagrams 14Andrea Corradini
FORMATO DEL PACCHETTO IP
IP VersionLungh.Header TOS Lungh. Datagram
Identific. Flag Offsetframmentazione
TTL Protocollo Checksum
Indirizzo Mittente
Indirizzo Destinatario
Opzioni
Dati
0
32
64
96
128
160
160/192+
ULezione 6: Il Protocollo UDP, sockets e datagrams 15Andrea Corradini
LIVELLO IP: FORMATO DEL PACCHETTOIP Version: IPV4 / IPV6
TOS (Type of Service) Consente un trattamento differenziato dei pacchetti.
Esempio: un particolare valore di TOS indica che il pacchetto ha una priorità
maggiore rispetto agli altri, Utile per distinguere tipi diversi di traffico
( traffico real time, messaggi per la gestione della rete,..)
TTL – Time to Live
Consente di limitare la diffusione del pacchetto sulla rete
• valore iniziale impostato dal mittente
• quando il pacchetto attraversa un router, il valore viene decrementato
• quando il valore diventa 0, il pacchetto viene scartato
Introdotto per evitare percorsi circolari infiniti del pacchetto. Utilizzato
anche per limitare la diffusione del pacchetto nel multicast
ULezione 6: Il Protocollo UDP, sockets e datagrams 16Andrea Corradini
LIVELLO IP: FORMATO DEL PACCHETTO
• Protocol: Il valore di questo campo indica il protocollo a livello
trasporto utilizzato (es: TCP 6, UDP 17, IPv6 41). Consente di
interpretare correttamente l'informazione contenuta nel datagram e
costituisce l'interfaccia tra livello IP e livello di trasporto
• Frammentazione: Campi utilizzati per gestire la frammentazione e la
successiva ricostruzione dei pacchetti
• Checksum: per controllare la correttezza del pacchetto
• Indirizzo mittente/destinatario
ULezione 6: Il Protocollo UDP, sockets e datagrams 17Andrea Corradini
L'HEADER UDP
• Datagram UDP = unità di trasmissione definita dal protocollo UDP
• Ogni datagram UDP
viene incapsulato in un singolo pacchetto IP
definisce un header che viene aggiunto all'header IP
Porta sorgente (0-65535) Porta Destinazione(0-65535)
Lunghezza Dati Checksum
DATI
0
32
64
ULezione 6: Il Protocollo UDP, sockets e datagrams 18Andrea Corradini
L'HEADER UDP
• L'header UDP viene inserito in testa al pacchetto IP
• contiene 4 campi, ognuno di 2 bytes
• i numeri di porta (0-65536) mittente/destinazione consentono un
servizio di multiplexing/demultiplexing
• Demultiplexing: l'host che riceve il pacchetto UDP decide in base al
numero di porta il servizio (processo) a cui devono essere consegnare i
dati
• Checksum: si riferisce alla verifica di correttezza delle 4 parole di 16
bits dell'header
• Lunghezza: lunghezza del datagram
ULezione 6: Il Protocollo UDP, sockets e datagrams 19Andrea Corradini
DATAGRAM UDP: LUNGHEZZE AMMISSIBILI
• IPV4 limita la lunghezza del datagram a 64K (65507 bytes + header)
• In pratica, la lunghezza del pacchetto UDP è limitata alla dimensione dei buffer associati al socket in ingresso/uscita dimensione del buffer = 8K nella maggior parte dei sistemi operativi. in certi sistemi si può incrementare la dimensione di questo buffer
• I routers IP possono frammentare i pacchetti IP che superano una certa dimensione se un pacchetto IP che contiene un datagram UDP viene frammentato,
il pacchetto non viene ricostruito e viene di fatto scartato per evitare problemi legati alla frammentazione, è meglio restringere
la lunghezza del pacchetto a 512 bytes
• E' possibile utilizzare dimension maggiori per pacchetti spediti su LAN
• IPV6 datagrams = 232 -1 bytes (jumbograms!)
ULezione 6: Il Protocollo UDP, sockets e datagrams 20Andrea Corradini
TRASMISSIONE PACCHETTI UDPPer scambiare un pacchetto UDP:
• mittente e destinatario devono creare due sockets attraverso i quali avviene la comunicazione.
• il mittente collega il suo socket ad una porta PM, il destinatario collega il suo socket ad una porta PD
Per spedire un pacchetto UDP, il mittente
• crea un socket SM collegato a PM
• crea un pacchetto DP (datagram).
• invia il pacchetto DP sul socket SMOgni pacchetto UDP spedito dal mittente deve contenere:
• indirizzo IP dell'host su cui è in esecuzione il destinatario + porta PD
• riferimento ad un vettore di bytes che contiene il valore del messaggio.
ULezione 6: Il Protocollo UDP, sockets e datagrams 21Andrea Corradini
TRASMISSIONE PACCHETTI UDP
Il destinatario, per ricevere un pacchetto UDP
• crea un socket SD collegato a PD
• crea una struttura adatta a memorizzare il pacchetto ricevuto
• riceve un pacchetto dal socket SD e lo memorizza in una struttura locale
i dati inviati mediante UDP devono essere rappresentati come vettori di bytes
JAVA offre diversi tipi di filtri per generare streams di bytes a partire da dati strutturati/ad alto livello
ULezione 6: Il Protocollo UDP, sockets e datagrams 22Andrea Corradini
TRASMISSIONE PACCHETTI UDP
Caratteristiche dei sockets UDP
• il destinatario deve “pubblicare” la porta a cui è collegato il socket di ricezione, affinchè il mittente possa spedire pacchetti su quella porta
• non è in genere necessario pubblicare la porta a cui è collegato il socket del mittente
• un processo può utilizzare lo stesso socket per spedire pacchetti verso destinatari diversi
• processi diversi possono spedire pacchetti sullo stesso socketallocato da un processo destinatario
socket
ULezione 6: Il Protocollo UDP, sockets e datagrams 23Andrea Corradini
JAVA : SOCKETS UDP
public class DatagramSocketCostruttori:public DatagramSocket( ) throws SocketException
• crea un socket e lo collega ad una porta anonima (o effimera), il sistema sceglie una porta non utilizzata e la assegna al socket. Per reperire la porta allocata utilizzare il metodo getLocalPort().
• utilizzato generalmente da un client UDP.
• Esempio: un client si connette ad un server mediante un socket collegato ad una porta anonima. Il server invia la risposta sullo stesso socket, prelevando l’indirizzo del mittente (IP+porta) dal pacchetto ricevuto. Quando il client termina, la porta viene utilizzata per altre connessioni.
ULezione 6: Il Protocollo UDP, sockets e datagrams 24Andrea Corradini
JAVA : SOCKETS UDP
Altro costruttore:
public DatagramSocket (int p ) throws SocketException
• crea un socket sulla porta specificata (p).
• viene sollevata un’eccezione (BindException / SocketException) se la porta è già utilizzata, oppure se si tenta di connettere il socket ad una porta su cui non si hanno diritti (SecurityException)
• utilizzato da un server UDP.
• Esempio: il server crea un socket collegato ad una porta resa nota ai clients. Di solito la porta viene allocata permanentemente a quel servizio (porta non effimera)
ULezione 6: Il Protocollo UDP, sockets e datagrams 25Andrea Corradini
INDIVIDUAZIONE DELLE PORTE LIBERE
Un programma per individuare le porte libere su un host:
import java.net.*;
public class ScannerPorte {
public static void main(String args[ ]){
for (int i = 1; i < 1024; i++){try {
new DatagramSocket(i);
System.out.println ("Porta libera"+i);
}
catch (BindException e) {System.out.println ("porta già in uso") ;}
catch (Exception e) {System.out.println (e);}
} }
ULezione 6: Il Protocollo UDP, sockets e datagrams 26Andrea Corradini
I DATAGRAMPACKET
• Un oggetto di tipo DatagramPacket può essere utilizzato per
memorizzare un datagram che deve essere spedito sulla rete
contenere i dati copiati da un datagram ricevuto dalla rete
• Struttura di un DatagramPacket
Buffer: riferimento ad un array di byte per la memorizzazione dei dati spediti/ricevuti
Metadati: • Lunghezza: quantità di dati presenti nel buffer• Offset: localizza il primo byte significativo nel buffer
InetAddress e porta del mittente o del destinatario
• I campi assumono diverso significato a seconda che il DatagramPacket
sia utilizzato per spedire o per ricevere dati
ULezione 6: Il Protocollo UDP, sockets e datagrams 27Andrea Corradini
LA CLASSE DATAGRAMPACKET
public final class DatagramPacket
public DatagramPacket (byte[ ] data, [int offset,] int length, InetAddress destination, int port)
• per la costruzione di un DatagramPacket da inviare sul socket
• length indica il numero di bytes che devono essere copiati dal vettore data[a partire dalla posizione offset] nel pacchetto UDP/IP.
• destination + port individuano il destinatario
• il messaggio deve essere trasformato in una sequenza di bytes e memorizzato nell'array data (strumenti necessari per la traduzione, es: metodo getBytes ( ), la classe java.io.ByteArrayOutputStream)
ULezione 6: Il Protocollo UDP, sockets e datagrams 28Andrea Corradini
LA CLASSE DATAGRAMPACKET
public DatagramPacket (byte[ ] data, [int offset,] int length)
• definisce la struttura utilizzata per memorizzare il pacchetto ricevuto..
• il buffer viene passato vuoto alla receive che lo riempie al momento della• ricezione di un pacchetto.
• il payload del pacchetto (la parte che contiene i dati) viene copiato nel buffer [a partire dalla posizione offset] al momento della ricezione.
• la copia del payload termina quando l'intero pacchetto è stato copiato oppure, se la lunghezza del pacchetto è maggiore di length, quando length bytes sono stati copiati
• il parametro length
prima della copia, indica il numero massimo di bytes che possono essere copiati nel buffer
dopo la copia, indica il numero di bytes effettivamente copiati.
ULezione 6: Il Protocollo UDP, sockets e datagrams 29Andrea Corradini
GENERAZIONE DEI PACCHETTI
Metodi per la conversione stringhe/vettori di bytes
• byte[ ] getBytes( ) applicato ad un oggetto String, restituisce una
sequenza di bytes che codifica i caratteri della stringa usando la
codifica di default dell'host
• String (byte[ ] bytes, int offset, int length) costruisce un nuovo
oggetto di tipo String decodificando la sottosequenza di length bytes
dall'array bytes, a partire dalla posizione offset
0 1 2 3 4 5 6 7 8 9 10 ...
offset 4 length 6
ULezione 6: Il Protocollo UDP, sockets e datagrams 30Andrea Corradini
INVIARE E RICEVERE PACCHETTI
Invio di pacchetti
• sock.send (dp)
dove: sock è il DatagramSocket attraverso il quale voglio spedire il pacchetto (DatagramPacket) dp
Ricezione di pacchetti• sock.receive(buffer)
dove sock è il DatagramSocket attraverso il quale ricevo il pacchetto e buffer è il DatagramPacket in cui memorizzo il pacchetto ricevuto
ULezione 6: Il Protocollo UDP, sockets e datagrams 31Andrea Corradini
COMUNICAZIONE TRAMITE SOCKETS: CARATTERISTICHE
• send non bloccante = il processo che esegue la send prosegue la sua esecuzione, senza attendere che il destinatario abbia ricevuto il pacchetto
• receive bloccante = il processo che esegue la receive si blocca fino al momento in cui viene ricevuto un pacchetto.per evitare attese indefinite è possibile associare al socket un timeout. Quando il timeout scade, viene sollevata una InterruptedIOException
ClientServer
receiverichiesta
risposta
send
receive
send
in esecuzione
bloccato
ULezione 6: Il Protocollo UDP, sockets e datagrams 32Andrea Corradini
RECEIVE CON TIMEOUT
• SO_TIMEOUT: proprietà associata al socket, indica l'intervallo di tempo, in
millisecondi, di attesa di ogni receive eseguita su quel socket
• Nel caso in cui l'intervallo di tempo scada, prima che venga ricevuto un
pacchetto dal socket, viene sollevata una eccezione di tipo
InterruptedIOException
• Metodi per la gestione di time out
public synchronized void setSoTimeout( int timeout) throws
SocketException
Esempio: se ds è un datagram socket, ds.setSoTimeout(30000)
associa un timeout di 30 secondi al socket ds.
ULezione 6: Il Protocollo UDP, sockets e datagrams 33Andrea Corradini
SEND/RECEIVE BUFFERS
• Ad ogni socket sono associati due buffers: uno per la ricezione ed uno per la spedizione
• Questi buffers sono gestiti dal sistema operativo, non dalla JVM. La loro dimensione dipende dalla piattaforma su cui il programma è in esecuzione
import java.net.*;
public class UDPBufferSize {public static void main (String args[]) throws Exception{
DatagramSocket dgs = new DatagramSocket();int r = dgs.getReceiveBufferSize(); int s = dgs.getSendBufferSize();System.out.println("receive buffer " + r);System.out.println("send buffer " + s); } }
• Stampa prodotta : receive buffer 8192 send buffer 8192 (8 Kbyte)
ULezione 6: Il Protocollo UDP, sockets e datagrams 34Andrea Corradini
SEND/RECEIVE BUFFERS • La dimensione del receive buffer deve essere almeno uguale a quella del
Datagram più grande che può essere ricevuto tramite quel buffer
• Receive Buffer: consente la bufferizzazione di un insieme di Datagram, nel caso in cui la frequenza con cui essi vengono ricevuti sia maggiore di quella con cui l'applicazione esegue la receive e quindi preleva i dati dal buffer
• La dimensione del send buffer viene utilizzata per stabilire la massima dimensione del Datagram
• Send Buffer: consente la bufferizzare di un insieme di Datagram, nel caso in cui la frequenza con cui essi vengono generati sia molto alta, rispetto alla frequenza con cui il supporto li preleva e spedisce sulla rete
• Per modificicare la dimensione del send/receive buffer
void setSendBufferSize(int size)
void setReceiveBufferSize(int size)
sono da considerare 'suggerimenti' al supporto sottostanta
ULezione 6: Il Protocollo UDP, sockets e datagrams 35Andrea Corradini
JAVA: IL CONCETTO DI STREAM
• Streams: introdotti per modellare l’interazione del programma con i dispositivi di I/O (console, files, connessioni di rete,…)
• JAVA Stream I/O: basato sul concetto di stream: si può immaginare uno stream come una condotta tra una sorgente ed una destinazione (dal programma ad un dispositivo o viceversa), da un estremo entrano dati, dall'altro escono
…..….. ReadWrite
• L’applicazione può inserire dati ad un capo dello stream
• I dati fluiscono verso la destinazione e possono essere estratti dall’altro capo dello stream Esempio: l'applicazione scrive su un FileOutputStream. Il dispositivo legge i dati e li memorizza sul file
ULezione 6: Il Protocollo UDP, sockets e datagrams 36Andrea Corradini
JAVA: IL CONCETTO DI STREAM
Caratteristiche principali degli streams:
• mantengono l’ordinamento FIFO
• read only o write only
• accesso sequenziale
• bloccanti: quando un’applicazione legge un dato dallo stream (o lo scrive) si blocca finchè l’operazione non è completata (ma le ultime versioni di JAVA introducono l’ I/O non bloccante: java.nio)
• non è richiesta una corrispondenza stretta tra letture/scritture
Esempio: una unica scrittura inietta 100 bytes sullo stream, che vengono letti con due read successive, la prima legge 20 bytes, la seconda 80 bytes
ULezione 6: Il Protocollo UDP, sockets e datagrams 37Andrea Corradini
STREAMS DI BASE: OUTPUTSTREAM
Streams di bytes: public abstract class OutputStream
Metodi di base:
public abstract void write (int b) throws IOException;
public void write(byte [ ] data) throws IOException;
public void write(byte [ ] data, int offset, int length) throws IOException;
La classe OutputStream ed il metodo write sono dichiarati astratti.
• write (int b) scrive su un OutputStream il byte meno significativo dell’intero passato (gli ultimi 8 bit dei 32)
• L’implementazione del metodo write può richiedere codice nativo (es: scrittura su un file…).
• Le sottoclassi descrivono stream legati a particolari dispositivi di I/O (file, console,…) .
ULezione 6: Il Protocollo UDP, sockets e datagrams 38Andrea Corradini
STREAMS DI BASE: INPUTSTREAM
Stream di bytes: public abstract class InputStream
Metodi di base:
public abstract int read () throws IOException;
public int read(byte [ ] b) throws IOException;
public int read(byte [ ] b, int offset, int length) throws IOException;
La classe InputStream ed il metodo read sono dichiarati astratti.
• read () restituisce un byte (un int nel rage 0-255) oppure -1 se ha raggiunto la fine dello stream.
• L’implementazione del metodo read può richiedere codice nativo (es: lettura da file…).
• Le sottoclassi descrivono input stream legati a particolari dispositivi di I/O (file, console,…) .
ULezione 6: Il Protocollo UDP, sockets e datagrams 39Andrea Corradini
STREAMS DI BASE E WRAPPERS
• Stream di base: classi utilizzate
*Stream utilizzate per la trasmissione di bytes
*Reader o *Writer: utilizzate per la trasmissione di caratteri
• Per non lavorare direttamente a livello di bytes o di carattere
Si definisce una serie di wrappers che consentono di avvolgere / incapsulare uno stream intorno all'altro ( come un tubo composto da più guaine...)
l'oggetto più interno è uno stream di base che 'avvolge' la sorgente dei dati (ad esempio il file, la connessione di rete,....).
i wrappers sono utilizzati per il trasferimento di oggetti complessi sullo stream, per la compressione di dati, per la definizione di strategie di buffering (per rendere più veloce la trasmissione)
ULezione 6: Il Protocollo UDP, sockets e datagrams 40Andrea Corradini
JAVA STREAMS: FILTRI
DataOutputStream consente di trasformare dati di un tipo primitivo JAVA in una sequenza di bytes da iniettare su uno stream.Alcuni metodi utili:
public final void writeBoolean(boolean b) throws IOException;
public final void writeInt (int i) throws IOException;
public final void writeDouble (double d) throws IOException;……
Il filtro produce una sequenza di bytes che rappresentano il valore del dato.Rappresentazioni utilizzate: • interi 32 bit complemento a due, big-endian • float 32 bit IEEE754 floating points Formati utilizzati dalla maggior parte dei protocolli di reteNessun problema se i dati vengono scambiati tra programmi JAVA.
ULezione 6: Il Protocollo UDP, sockets e datagrams 41Andrea Corradini
STREAMS DI BASE E WRAPPERS
InputStream, OuputStream consentono di manipolare dati a livello molto basso,per cui lavorare direttamente su questi streams risulta parecchio complesso.Per estendere le funzionalità degli streams di base: classi wrapper
applicazioneinteri
bytes
buffered bytes
file (data.txt)
DataOutputStream
BufferedOutputStream
FileOutputStream
DataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(“data.txt”)))
WRAPPER
ULezione 6: Il Protocollo UDP, sockets e datagrams 42Andrea Corradini
BYTEARRAY OUTPUT STREAMS
public ByteArrayOutputStream ( )public ByteArrayOutputStream (int size)• gli oggetti di questa classe rappresentano stream di bytes tali che ogni dato
scritto sullo stream viene riportato in un buffer di memoria (array di byte)a dimensione variabile (dimensione di default = 32 bytes).
• quando il buffer si riempie la sua dimensione viene raddoppiata • quindi consente di accedere ad un array di byte come se fosse uno stream;
l'array può essere estratto con il metodo toByteArray()
BUFFERByteArrayOutputStream
puntatore all’ultimo elemento inserito
ULezione 6: Il Protocollo UDP, sockets e datagrams 43Andrea Corradini
JAVA: USO DEGLI STREAM PER LA PROGRAMMAZIONE DI RETE
Come utilizzeremo gli streams in questo corso:
• Trasmissione connectionless:
ByteArrayOutputStream, consentono la conversione di uno stream di bytes in un array di bytes da spedire con i pacchetti UDP
ByteArrayInputStream, converte un array di bytes in uno stream di byte. Consente di manipolare più agevolmente i bytes
• Trasmissione connection oriented:
Una connessione viene modellata con uno stream.
invio di dati = scrittura sullo stream
ricezione di dati = lettura dallo stream
ULezione 6: Il Protocollo UDP, sockets e datagrams 44Andrea Corradini
BYTEARRAY OUTPUT STREAMS NELLA COSTRUZIONE DI PACCHETTI UDP
Ad un ByteArrayOutputStream può essere collegato un altro wrapper
ByteArrayOutputStream baos = new ByteArrayOutputStream( );
DataOutputStream dos = new DataOutputStream (baos)
Posso scrivere un dato di qualsiasi tipo sul DataOutputStream( )I dati presenti nel buffer B associato ad un ByteArrayOuputStream baospossono essere copiati in un array di bytes, di dimensione uguale alla dimensione attuale di B
byte [ ] barr = baos. toByteArray( )
datirete
DataOutputStream ByteArrayOutputStream
Buffer
ByteArray Pacchetto
ULezione 6: Il Protocollo UDP, sockets e datagrams 45Andrea Corradini
BYTEARRAY INPUT STREAMS
public ByteArrayInputStream ( byte [ ] buf )public ByteArrayInputStream ( byte [ ] buf, int offset, int length )
• creano stream di byte a partire dai dati contenuti nell'array di byte buf.
• il secondo costruttore copia length bytes iniziando alla posizione offset.
• E’ possibile incapsularlo in un DataInputStream
Ricezione di un pacchetto UDP dalla rete:
pacchetto Byte array
ByteArrayInputStream DataInputStream
Dati
ULezione 6: Il Protocollo UDP, sockets e datagrams 46Andrea Corradini
BYTE ARRAY INPUT/OUTPUT STREAMS
• Le classi ByteArrayInput/OutputStream facilitano l’invio dei dati di qualsiasi tipo (anche oggetti) sulla rete. La trasformazione in sequenza di bytes è automatica.
• uno stesso ByteArrayOuput/InputStream può essere usato per produrre streams di bytes a partire da dati di tipo diverso
• il buffer interno associato ad un ByteArrayOutputStream baos viene svuotato (puntatore all’ultimo elemento inserito = 0) con
• baos.reset ( )
• il metodo toByteArray non svuota il buffer!
ULezione 6: Il Protocollo UDP, sockets e datagrams 47Andrea Corradini
INVIO DI UDP DATAGRAMS
Ipotesi semplificativa: non consideriamo perdita/riordinamento di pacchetti
import java.io.*;
import java.net.*;
public class MultiDataStreamSender{
public static void main(String args[ ]) throws Exception{ // inizializzazione
InetAddress ia = InetAddress.getByName("localhost");
int port = 13350;
DatagramSocket ds = new DatagramSocket ( );
byte [ ] data = new byte [20];
DatagramPacket dp = new DatagramPacket(data, data.length, ia , port);
ByteArrayOutputStream bout= new ByteArrayOutputStream( );
DataOutputStream dout = new DataOutputStream (bout);
ULezione 6: Il Protocollo UDP, sockets e datagrams 48Andrea Corradini
for (int i=0; i< 10; i++){dout.writeInt(i); // scrivo 4 bytes nello streamdata = bout.toByteArray(); // estraggo l'array di bytedp.setData(data,0,data.length); // lo inserisco nel DatagramPacketdp.setLength(data.length); // definisco la lunghezza del bufferds.send(dp); // invio il DatagramPacket sul socketbout.reset( ); // svuoto lo streamdout.writeUTF("***"); // scrivo una stringa nello streamdata = bout.toByteArray( ); // ...dp.setData (data,0,data.length);
dp.setLength (data.length); ds.send (dp); bout.reset( ); } } }
INVIO DI UDP DATAGRAMS
ULezione 6: Il Protocollo UDP, sockets e datagrams 49Andrea Corradini
Ipotesi semplificativa: non consideriamo perdita/riordinamento di pacchettiimport java.io.*;import java.net.*;public class MultiDataStreamReceiver{
public static void main(String args[ ]) throws Exception{// fase di inizializzazioneFileOutputStream fw = new FileOutputStream("text.txt");DataOutputStream dr = new DataOutputStream(fw);int port = 13350;DatagramSocket ds = new DatagramSocket (port);byte [ ] buffer = new byte [200];DatagramPacket dp = new DatagramPacket (buffer, buffer.length);
RICEZIONE DI UDP DATAGRAMS
ULezione 6: Il Protocollo UDP, sockets e datagrams 50Andrea Corradini
for (int i = 0; i < 10; i++){
ds.receive(dp); // ricevo il DatagramPacket ByteArrayInputStream bin= // getLength() è il numero di byte letti
new ByteArrayInputStream(dp.getData(), 0, dp.getLength()); DataInputStream ddis= new DataInputStream(bin); int x = ddis.readInt(); // leggo un intero attraverso lo stream dr.writeInt(x); // lo scrivosul file System.out.println(x); // lo stampo ds.receive(dp); // ricevo altro DatagramPacket bin = new ByteArrayInputStream(dp.getData(), 0 ,dp.getLength()); ddis = new DataInputStream(bin); String y = ddis.readUTF( ); // leggo una stringa System.out.println(y); } } } // la stampo
RICEZIONE DI UDP DATAGRAMS
ULezione 6: Il Protocollo UDP, sockets e datagrams 51Andrea Corradini
Protocollo UDP: problemi
• Nel programma precedente, la corrispondenza tra la scrittura nel mittente e la lettura nel destinatario potrebbe non essere più corretta
• Esempio:
il mittente alterna la spedizione di pacchetti contenenti valori interi con pacchetti contenenti stringhe
il destinatario alterna la lettura di interi e di stringhe se un pacchetto viene perso ⇒ scritture/letture possono non
corrispondere
• Realizzazione di UDP affidabile: utilizzo di ack per confermare la ricezione + identificatori unici
ULezione 6: Il Protocollo UDP, sockets e datagrams 52Andrea Corradini
PER ESEGUIRE GLI ESERCIZI SU UN UNICO HOST
• Attivare il client ed il server in due diverse shell
• Se l’host è connesso in rete: utilizzare come indirizzo IP del mittente/destinatario l’indirizzo dell’host su cui sono in esecuzione i due processi (reperibile con InetAddress.getLocalHost( ) )
• Se l’host non è connesso in rete utilizzare l’indirizzo di loopback (“localhost” o 127.0.0.1)
• Tenere presente che mittente e destinatario sono in esecuzione sulla stessa macchina ⇒ devono utilizzare porte diverse
• Mandare in esecuzione per primo il server, poi il client
ULezione 6: Il Protocollo UDP, sockets e datagrams 53Andrea Corradini
ESERCIZIO 1: INVIO DI DATAGRAM UDP
Esercizio:
Scrivere un'applicazione composta da un processo Sender ed un processo Receiver. Il Receiver riceve da linea di comando la porta su cui deve porsi in attesa. Il Sender riceve da linea di comando una stringa e l’indirizzo del Receiver (indirizzo IP + porta), e invia al Receiver la stringa. Il Receiver riceve la stringa e stampa, nell'ordine, la stringa ricevuta, l'indirizzo IP e la porta del mittente.
Considerare poi i seguenti punti:
• cosa cambia se mando in esecuzione prima il Sender, poi il Receiver rispetto al caso in cui mando in esecuzione prima il Receiver?
• nel processo Receiver, aggiungere un time-out sulla receive, in modo che la receive non si bocchi per più di 5 secondi. Cosa accade se attivo il receiver, ma non il sender?
ULezione 6: Il Protocollo UDP, sockets e datagrams 54Andrea Corradini
ESERCIZIO 1: INVIO DI DATAGRAM UDP• Modificare il codice del Sender in modo che usi lo stesso socket per
inviare lo stesso messaggio a due diversi receivers. Mandare in esecuzione prima i due Receivers, poi il Sender. Controllare l'output dei Receiver.
• Modificare il codice del Sender in modo che esso usi due sockets diversi per inviare lo stesso messaggio a due diversi receivers. Mandare in esecuzione prima i due Receivers, poi il Sender.
• Modificare il codice ottenuto al passo precedente in modo che il Sender invii una sequenza di messaggi ai due Receivers. Ogni messaggio contiene il valore della sua posizione nella sequenza. Il Sender si sospende per 3 secondi tra un invio ed il successivo. Ogni receiver deve essere modificato in modo che esso esegua la receive in un ciclo infinito.
• Modificare il codice ottenuto al passo precedente in modo che il Sender non si sospenda tra un invio e l’altro. Cosa accade?
• Modificare il codice iniziale in modo che il Receiver invii al Sender un ack quando riceve il messaggio. Il Sender visualizza l’ack ricevuto.
ULezione 6: Il Protocollo UDP, sockets e datagrams 55Andrea Corradini
ESERCIZIO 2: COUNT DOWN SERVER
Si richiede di programmare un server CountDownServer che fornisceun semplice servizio: ricevuto da un client un valore intero n, il serverspedisce al client i valori n-1,n-2,n-3,….,1, in sequenza.La interazione tra i clients e CountDownServer è di tipo connectionless.Si richiede di implementare due versioni di CountDownServer realizzare CountDownServer come un server iterativo. L’applicazione riceve
la richiesta di un client, gli fornisce il servizio e solo quando ha terminato va a servire altre richieste
realizzare CountDownServer come un server concorrente. Si deve definire un thread che ascolta le richieste dei clients dalla porta UDP a cui è associato il servizio ed attiva un thread diverso per ogni richiesta ricevuta. Ogni thread si occupa di servire un client.
Opzionale: Il client calcola il numero di pacchetti persi e quello di quelli ricevuti fuori ordine e lo visualizza alla fine della sessione.Utilizzare le classi ByteArrayOutput/InputStream per la generazione/ricezione dei pacchetti.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 1Andrea Corradini
Lezione n.7LPR-B-08
UDP: Costruzione di pacchetti, e esercizi avanzati
10/11/2009Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 2Andrea Corradini
Invio di dati tramite UDP• codifica di dati primitivi come array di byte• invio di oggetti tramite serializzazione
Discussione di esercizi avanzati• MiniTalk (Instant Messanger)
– handshaking per stabilire una “connessione”– invio bidirezionale di stringhe
• TFTP (Trivial File Transfer Protocol)– protocollo per trasferimento di file in
lettura/scrittura tra client e server– simulazione di trasmissione basata su stream
SOMMARIO
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 3Andrea Corradini
ABBIAMO VISTO CHE IN JAVA...
• Il protocollo UDP prevede la trasmissione di una sequenza (array) di bytes incapsulato in un DatagramPacket.
• Possiamo trasformare dati primitivi in sequenze di bytes da inserire all'interno del pacchetto usando
la classe DataOutputStream e i metodi writeInt(), writeDouble(), ...
la classe ByteArrayOutputStream e il metodo toByteArray()
• I dati possono essere ricostruiti dal mittente mediante
la classe ByteArrayInputStream
la classe DataInputStream e i metodi readInt(), readDouble(), ...
• Possiamo inviare più dati primitivi nello stesso pacchetto UDP, rileggendoli nello stesso ordine
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 4Andrea Corradini
SENDER: INVIA PIU' DATI IN UN PACCHETTO
byte byteVal = 101;
short shortVal = 10001;
int intVal = 100000001;
long longVal = 100000000001L;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(buf);
out.writeByte(byteVal);
out.writeShort(shortVal);
out.writeInt(intVal);
out.writeLong(longVal);
byte[ ] msg = buf.toByteArray( ); // Va inserito nel DatagramPacket // e spedito sul DatagramSocket
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 5Andrea Corradini
RECEIVER: ESTRAE PIU' DATI DAL PACCHETTO...
ds.receive(dp); // si riceve il DatagramPacket
byte byteValIn;
short shortValIn;
int intValIn;
long longValIn;
ByteArrayInputStream bufin = new ByteArrayInputStream(dp.getData(), 0, dp.getLength());
DataInputStream in = new DataInputStream(bufin);
byteValIn = in.readByte();
shortValIn = in.readShort();
intValIn = in.readInt();
longValIn = in.readLong();
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 6Andrea Corradini
CODIFICARE DATI PRIMITIVI COME BYTE[]• Il protocollo UDP (e come vedremo anche TCP) consente di inviare
unicamente sequenze di bytes. Un byte viene interpretato come un intero nell'intervallo [0..255].
• La codifica dei tipi di dato primitivi di un linguaggio in sequenze di bytes può essere realizzata
dal supporto del linguaggio (come visto nei lucidi precedenti)
più a basso livello, esplicitamente dal programmatore
• In ogni caso, il mittente ed il destinatario devono accordarsi sulla codifica stabilita. Ad esempio, per un dato di tipo intero si deve stabilire:
la dimensione in bytes per ogni tipo di dato
l'ordine dei bytes trasmessi,
l'interpretazione di ogni byte (con segno/senza segno)
• Il problema è semplificato se mittente e destinatario sono codificati mediante lo stesso linguaggio (ad esempio entrambi in JAVA)
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 7Andrea Corradini
CODIFICARE VALORI DI TIPO PRIMITIVO
Per scambiare valori di tipo intero, occorre concordare:
dimensione dei tipi di dati scambiati: long: 8 bytes, int: 4 bytes, short: 2 bytes
ordine dei bytes trasmessi
Little-endian: il primo byte trasmesso è il meno significativo
Big-endian: il primo byte trasmesso è il più significativo
Interpretazione dei valori: con/senza segno
Nel caso di valori di tipo stringa, occorre concordare
codifica adottata per i caratteri contenuti nella stringa
UTF-8,
UTF-16
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 8Andrea Corradini
CODIFICARE VALORI DI TIPO INTERO
• Supponiamo di dover costruire un pacchetto UDP contenente quattro valori interi: un byte, uno short, un intero ed un long
byte short int long
• Non si vogliono utilizzare le classi filtro DataOutputStrem e ByteArrayOutputStream
• Soluzione alternativa: si utilizzano operazioni che operano direttamente sulla rappresentazione degli interi
si definisce message, un vettore di bytes
si selezionano i bytes della rappresentazione mediante shifts a livello di bits
si inserisce ogni byte selezionato in una posizione del vettore message
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 9Andrea Corradini
CODIFICARE VALORI DI TIPO INTERO
public static int encodePacket(byte[ ] dst, int offset, long val, int size){for (int i = size; i > 0; i--){
dst[offset++] = (byte) (val >> ((i - 1) * 8));}return offset;}
• val valore intero
• size dimensione, in bytes, del valore val
• i bytes che rappresentano val devono essere inseriti nel vettore dst, a partire dalla posizione offset
• si utilizza lo shift destro per selezionare i bytes, a partire dal più significativo
• il cast a byte del valore shiftato V restituisce gli 8 bits meno significativi di V, eliminando gli altri bits
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 10Andrea Corradini
CODIFICARE VALORI DI TIPO INTEROpublic static void main(String args[ ])
{ byte byteVal=101, short shortVal = 8, int intVal = 53,
long longVal = 234567L;
final int BSIZE = 1;
final int SSIZE= Short.SIZE / Byte.SIZE;
final int ISIZE = Integer.SIZE/ Byte.SIZE;
final int LSIZE = Long.SIZE / Byte.SIZE;
byte [ ] message = new byte[BSIZE+SSIZE+ISIZE+LSIZE];
int offset = encodePacket(message,0, byteVal, 1);
offset = encodePacket(message,offset, shortVal, SSIZE);
offset = encodePacket(message,offset,intVal, ISIZE);
offset = encodePacket(message,offset, longVal, LSIZE);
............................
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 11Andrea Corradini
DECODIFICARE VALORI DI TIPO INTEROpublic static long decodePacket(byte[ ] val, int offset, int size){
long rtn =0; int BYTEMASK = 0xFF;
for (int i= 0; i < size; i++){
rtn = (rtn << 8) | ((long) val[offset +i] & BYTEMASK); }
return rtn; }
• decodePacket: decodifica il valore di un dato reppresentato dai bytes contenuti nel vettore val, a partire dalla posizione offset. La dimensione (in byte) del valore da decodificare è size.
• Se si vuole 'riassemblare' il valore di tipo short dell'esempio precedente:short value = (short) decodePacket (message, BSIZE, SSIZE);
• “& BYTEMASK” è necessario per mettere a zero i bit più significativi (posizioni 0-55), che potrebbero essere ad 1 per il cast. Si provi il comando:
System.out.println((short) (byte) 128); // Stampa -128 ! Perché ?
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 12Andrea Corradini
CODIFICA DI STRINGHE
• I caratteri vengono codificati in JAVA mediante Unicode che mappa i caratteri ad interi nell'intervallo [0..65535]
• Unicode è compatibile all'indietro con la codifica ASCII
• Il metodo getBytes( ) applicato ad una stringa restituisce un array di bytes contenente la rappresentazione della stringa, ottenuta secondo la codifica utilizzata di default dalla piattaforma su cui il programma viene eseguito
• E' possibile indicare esplicitamente la codifica desiderata, come argomento della getBytes()
• Esempio.
“Test!”.getBytes( )
“Test!”.getBytes(“UTF-16BE”)
• In generale mittente e destinatario devono accordarsi sulla codifica utilizzata per i valori di tipo stringa
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 13Andrea Corradini
SERIALIZZAZIONE DI OGGETTI
• Le classi ObjectInputStream e ObjectOutputStream definiscono streams (basati su streams di byte) su cui si possono leggere e scrivere oggetti.
• La scrittura e la lettura di oggetti va sotto il nome di serializzazione, poiché si basa sulla possibilità di scrivere lo stato di un oggetto in una forma sequenziale, sufficiente per ricostruire l'oggetto quando viene riletto. La serializzazione di oggetti viene usata in diversi contesti:
Per inviare oggetti sulla rete, sia che si utilizzino i protocolli UDP o TCP, sia che si utilizzi RMI.
Per fornire un meccanismo di persistenza ai programmi, consentendo l'archiviazione di un oggetto per esempio in un file. Si pensi ad esempio ad un programma che realizza una rubrica telefonica o un'agenda.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 14Andrea Corradini
SERIALIZZAZIONE DI OGGETTI
Consente di convertire un oggetto E TUTTI QUELLO DA ESSO RAGGIUNGIBILI in una sequenza di bytes.
• Tale sequenza può essere utilizzata per ricostruire l’oggetto e il contesto.
• L’oggetto e tutti quelli raggiungibili devono essere definiti in classi che implementi l’interfaccia Serializable, che non ha metodi! Altrimenti viene lanciata una NotSerializableException.
• Tutte le classi involucro per i tipi di dati primitivi (es: Integer, Double, ...) e anche String implementano Serializable: quindi JAVA garantisce una serializzazione di default per tutti i dati primitivi.
• Per serializzare uno o più oggetti si utilizzano stream di tipo ObjectOutputStream e ObjectInputStream, e i rispettivi metodi writeObject() e readObject().
• Il meccanismo di serializzazione può essere personalizzato, ma non lo vediamo...
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 15Andrea Corradini
LA CLASSE ObjectOutputStream
public ObjectOutputStream (OutputStream out) throws Exception
Quando si costruisce un oggetto di tipo ObjectOutputStream, viene automaticamente registrato in testa allo stream un header, costituito da due short, 4 bytes
(costanti MAGIC NUMBER + NUMERO DI VERSIONE)
• Magic Number = identifica univocamente un object stream
• I Magic Number vengono utilizzati in diversi contesti. Ad esempio, ogni struttura contenente la definizione di una classe Java deve iniziare con un numero particolare (magic number), codificato mediante una sequenza di 4 bytes, che identificano che quella struttura contiene effettivamente una classe JAVA [3405691582, 0xCAFEBABE]
• Se l’header viene cancellato lo stream risulta corrotto e l'oggetto non può essere ricostruito. Infatti al momento della ricostruzione dell'oggetto si controlla innanzi tutto che l'header non sia corrotto
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 16Andrea Corradini
LA CLASSE ObjectInputStream
public ObjectInputStream (InputStream in) throws Exception
• L'header inserito dal costruttore di ObjectOutputStream viene letto e decodificato dal costruttore ObjectInputStream
• Se il costruttore ObjectInputStream( ) rileva qualche problema nel leggere l'header (ad esempio l'header è stato modificato o cancellato) viene segnalato che lo stream risulta corrotto
• L'eccezione sollevata è StreamCorruptedException
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 17Andrea Corradini
TRASMISSIONE DI OGGETTI,SCHEMATICAMENTE
{oggetti}
ObjectOutputStream(ByteArrayOutputStream)
byte[ ]
DatagramPacket
writeObject( )
toByteArray()
DatagramPacket byte[ ]ObjectInputStream(ByteArrayInputStream)
readObject( )
{oggetti}
DatagramSocket
DatagramSocket
send( )
receive( )
UDP/IP on Internet
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 18Andrea Corradini
UN ESEMPIO DI TRASMISSIONE DI OGGETTI
import java.io.*; import java.net.*; import java.util.Vector;public class UDP_SendObject {
public static void main(String[] args) throws IOException {// build interesting objectVector<Object> vett = new Vector<Object>();vett.add(new Integer(74));vett.add("Questa e' una stringa");vett.add(new java.awt.Rectangle(3,4,10,20));// prepare data to be sent using object streamByteArrayOutputStream bout = new ByteArrayOutputStream();ObjectOutputStream oout = new ObjectOutputStream(bout);oout.writeObject(vett);byte[] arr = bout.toByteArray();
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 19Andrea Corradini
UN ESEMPIO DI TRASMISSIONE DI OGGETTI
// continua la classe UDP_SendObject // prepare Datagram socket and packetDatagramSocket ds = new DatagramSocket();InetAddress ia = InetAddress.getByName("localhost");int port = 24309;DatagramPacket dp = new DatagramPacket(arr,arr.length,ia,port);// sendds.send(dp);
}}
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 20Andrea Corradini
UN ESEMPIO DI TRASMISSIONE DI OGGETTI
import java.io.*; import java.net.*; import java.util.Vector;public class UDP_ReceiveObject {
public static void main(String[] args) throws IOException,SocketException,ClassNotFoundException{DatagramSocket ds = new DatagramSocket(24309);DatagramPacket dp = new DatagramPacket(new byte[512],512);ds.receive(dp); // receive packetByteArrayInputStream bais =
new ByteArrayInputStream(dp.getData(),dp.getOffset(),dp.getLength());ObjectInputStream ois = new ObjectInputStream(bais);Vector<Object> vett = (Vector<Object>) ois.readObject();for (Object o : vett){ System.out.println(o.getClass() + ": " + o); } } }
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 21Andrea Corradini
UN ESEMPIO DI TRASMISSIONE DI OGGETTI
L'output del programma mostra che il vettore e il suo contenuto sono arrivati correttamente al receiver:
class java.lang.Integer: 74
class java.lang.String: Questa e' una stringa
class java.awt.Rectangle: java.awt.Rectangle[x=3,y=4,width=10,height=20]
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 22Andrea Corradini
ATTENZIONE: UN ObjectOutputStream PER PACCHETTO!
public class Test {
public static void main(String args[ ]) throws Exception{ByteArrayOutputStream bout = new ByteArrayOutputStream ( );System.out.println (bout.size( )); // Stampa 0ObjectOutputStream out= new ObjectOutputStream(bout);System.out.println (bout.size( )); // Stampa 4: l'header è creatoout.writeObject("prova");System.out.println (bout.size( )); //Stampa 12// Spedisco il contenutobout.reset ( ); //Necessario per eliminare bytes già speditiout.writeObject("prato");System.out.println (bout.size( )); //Stampa 8 = 12-4. }}
• la reset() ha distrutto l’header dello stream!!!
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 23Andrea Corradini
ESERCIZIO 1: UNA CLASSE AUSILIARIA PERTRASMETTERE OGGETTI
Scrivere la classe Obj2DP che fornisce due metodi statici: public static Object dp2obj(DatagramPacket dp)
che restituisce l'oggetto contenuto nel pacchetto passato per argomento, deserializzandolo
public static DatagramPacket obj2dp(Object obj)
che restituisce un pacchetto contenente l'oggetto passato per argomento, serializzato, nel payload.
• Semplificare le classi UDP_SendObject e UDP_ReceiveObject viste prima usando i metodi della classe Obj2DP, senza più usare le classi ObjectOutput/InputStream e ByteOutput/InputStream.
• Usare la classe Obj2DP per i prossimi esercizi, trasmettendo oggetti serializzati con UDP invece di dati di tipi primitivi.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 24Andrea Corradini
ESERCIZIO 2: MiniTalk Server e Client con UDP (1)
Si realizzino un Server e un Client UDP che realizzano un semplice Instant Messanger: ogni linea scritta nella shell del Server viene copiata nella shell del Client e viceversa. La trasmissione delle linee inizia dopo una semplice fase di handshaking, descritta di seguito.
Il Server viene lanciato da shell, fornendo il numero di porta su cui ricevere i pacchetti UDP.
Il Client viene lanciato da un'altra shell (eventualmente su un altro computer), fornendo host e porta del Server e porta locale su cui ricevere pacchetti UDP.
Il Client manda una richiesta di connessione al Server, indicando nel messaggio la propria porta. Se non riceve un messaggio di ack entro 3 secondi, riprova a mandare la richiesta altre 5 volte, poi termina. Se invece riceve l'ack, inizia la trasmissione delle linee scritte nella shell locale e la ricezione e stampa delle linee scritte nella shell del Server.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 25Andrea Corradini
ESERCIZIO 2: MiniTalk Server e Client con UDP (2)
Quando il Server riceve una richiesta di connessione, recupera indirizzo e porta del Client dalla richiesta e manda un messaggio di ack al Client, quindi comincia la trasmissione e ricezione delle stringhe come descritto per il Client.
Il Client decide quando disconnettersi in base a una condizione a vostra scelta (per esempio, allo scadere di un timeout, oppure se la linea da mandare è vuota, oppure se la stringa è “CLOSE”,...).
Per disconnettersi, il Client manda una richiesta di disconnessione al Server e aspetta un ack, quindi termina l'esecuzione.
Quando il Server riceve una richiesta di disconnessione interrome la trasmissione delle linee, manda un messaggio di ack, e si rimette in attesa di una richiesta di connessione.
Il Client e il Server devono scambiarsi unicamente oggetti della classe TalkMsg, usando i metodi della classe per crearne istanze e per ispezionare i messaggi arrivati.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 26Andrea Corradini
ESERCIZIO 3: MiniTalk Messanger con UDP
Riusando il più possibile il codice sviluppato per l'esercizio precedente, realizzare un programma Messanger che offre le stesse funzionalità, ma in cui non si distinguono un Server e un Client.
Due istanze di Messanger devono essere lanciate in due shell diverse, fornendo ad ognuna tre dati: la porta locale, e l'host e la porta dell'altro Messanger. Ideare un opportuno protocollo di handshaking, che permetta di stabilire una connessione (concettuale) tra le due istanze di Messanger.
I messaggi scambiati devono essere tutti oggetti di una stessa classe. Usare la classe TalkMsg, oppure estenderla o definirne una analoga se necessario.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 27Andrea Corradini
ESERCIZIO 4: TFTP con UDP(Trivial File Transfer Protocol) [1]
Questa è la specifica di TFTP da WIKIPEDIA:
• L'host A invia un pacchetto RRQ (read request) o WRQ (write request) all'host B, contenente il nome del file e la modalità di trasferimento.
• B risponde con un ACK (acknowledgement) packet, che serve anche a dire ad A quale porta sull'host B dovrà usare per i restanti pacchetti.
• L'host di origine invia dei pacchetti DATA numerati all'host di destinazione, tutti tranne l'ultimo contenenti un blocco di dati completo. L'host di destinazione risponde con un pacchetto ACK numerato per ogni pacchetto DATA.
• Il pacchetto DATA finale deve contenere un blocco di dati non pieno ad indicare che si tratta dell'ultimo. Se la dimensione del file trasferito è un multiplo esatto della dimensione dei blocchi, la sorgente invia un ultimo pacchetto di dati contente 0 byte di dati.
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi 28Andrea Corradini
ESERCIZIO 4: TFTP con UDP(Trivial File Transfer Protocol) [2]
Realizzare un Server TFTP che implementa il comportamento dell'host B e un Client TFTP che implementa l'host A. In particolare:
• Client e Server devono scambiarsi solo oggetti di una classe, TFTPmsg, usati sia per messaggi di servizio (RRQ, WRQ, ACK) che per i pacchetti DATA: definire opportunamente la classe TFTPmsg.
• Per il trasferimento dei file, considerarli come file binari, usando quindi opportuni Output/InputStreams (e non Writer/Reader).
• Inviare le porzioni di file in array di byte all'interno di un'istanza di TFTPmsg.
Per testare il programma:
• Confrontare il file originale spedito dal mittente con quello ricevuto dal destinatario e scritto nel file system.
• Usare la classe UnreliableDatagramSocket per controllare che i pacchetti persi vengano reinviati correttamente.
Lezione 6: TCP: Stream Sockets 1Andrea Corradini
Lezione n.7LPR-B-09
TCP: Stream Sockets 17/11/2009
Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
Lezione 6: TCP: Stream Sockets 2Andrea Corradini
DATAGRAM SOCKET API:RIASSUNTO DELLE PUNTATE PRECEDENTI
• lo stesso Datagram Socket può essere utilizzato per spedire messaggi verso destinatari diversi
• processi diversi possono inviare datagrams sullo stesso socket di un processo destinatario
• send non bloccante:se il destinatario non è in esecuzione quando il mittente esegue la send, il messaggio può venir scartato
• receive bloccante:uso di timeouts associati al socket per non bloccarsi indefinitamente sulla receive
• i messaggi ricevuti vengono troncati se la dimensione del buffer del destinatario è inferiore a quella del messaggio spedito (provatelo!)
Lezione 6: TCP: Stream Sockets 3Andrea Corradini
DATAGRAM SOCKET API:RIASSUNTO DELLE PUNTATE PRECEDENTI
• protocollo UDP (User Datagram Protocol) non implementa controllo del flusso: se la frequenza con cui il mittente invia i messaggi è sensibilmente maggiore di quella con cui il destinatario li riceve (li preleva dal buffer di ricezione) è possibile che alcuni messaggi sovrascrivano messaggi inviati in precedenza
• Esempio: CountDown Server (vedi prima esercitazione su UDP). Il client invia al server un valore di n “grande” (provare valori>1000). Allora: il server deve inviare al client un numero molto alto di pacchetti il tempo che intercorre tra l’invio di un pacchetto e quello del pacchetto
successivo è basso dopo un certo numero di invii il buffer del client si riempie ⇒ perdita di
pacchetti
Lezione 6: TCP: Stream Sockets 4Andrea Corradini
COUNT DOWN SERVER UDP
n
.
n,n-1,n-2,...
• Si può utilizzare lo stesso socket per inviare n e per ricevere i risultati
• Quando il CountDownClient invia il valore n il CountDownServer deve aver allocato il socket, altrimenti il pacchetto viene perduto
CountDownClient CountDownServer
Lezione 6: TCP: Stream Sockets 5Andrea Corradini
COUNT DOWN SERVER UDP
n
.n,n-1,n-2,...
● Posso utilizzare sockets diversi per la spedizione/ricezione ● In questo caso può accadere che sock2 non sia ancora stato creato
quando CountDownServer inizia ad iniziare la sequenza di numeri ● E' possibile che il CountDownClient si blocchi sulla receive poiché i dati
inviati dal CountDownServer sono stati inviati prima della creazione del socket e quindi sono andati persi
CountDownClient CountDownServer
sock1
sock2
Lezione 6: TCP: Stream Sockets 6Andrea Corradini
IL PROTOCOLLO TCP: STREAM SOCKETS
• Il protocollo TCP (Transmission Control Protocol) supporta un modello computazionale di tipo client/server, in cui il server riceve
dai clients richieste di connessione, le schedula e crea connessioni diverse per ogni richiesta ricevuta
ogni connessione supporta comunicazioni bidirezionali, affidabili
• La comunicazione connection-oriented prevede due fasi: il client richiede una connessione al server quando il server accetta la connessione, client e server iniziano a
scambiarsi i dati
• In JAVA, ogni connessione viene modellata come uno stream di bytes i dati non vengono incapsulati in messaggi (pacchetti) stream sockets: al socket sono associati stream di input/output usa il modello di I/O basato su streams definito in UNIX e JAVA
Lezione 6: TCP: Stream Sockets 7Andrea Corradini
IL PROTOCOLLO TCP: STREAM SOCKETS
• Esistono due tipi di socket TCP: Listening (o passive) sockets: utilizzati dal server per accettare le
richieste di connessione Active Sockets: supportano lo streaming di byte tra client e server
• Il server utilizza un listening socket per accettare le richieste di connessione dei clients
• Il client crea un active socket per richiedere la connessione
• Quando il server accetta una richiesta di connessione, crea a sua volta un proprio active socket che rappresenta il punto
terminale della sua connessione con il client la comunicazione vera e propria avviene mediante la coppia di active
sockets presenti nel client e nel server
Lezione 6: TCP: Stream Sockets 8Andrea Corradini
IL PROTOCOLLO TCP: STREAM SOCKETS
Listening Socket
SIP = Indirizzo IP del ServerClient1
Client2
PL=Porta Locale
PL=Porta Locale
PR = Porta Remota
Active Socket
Active Socket
Richiesta di apertura di connessione. Il client • crea un active socket S e lo associa alla sua porta locale PL • collega S al listening socket presente sul server pubblicato all'indirizzo (SIP, PR)
Lezione 6: TCP: Stream Sockets 9Andrea Corradini
IL PROTOCOLLO TCP: STREAM SOCKETS
Listening Socket
Client1
Client2
PL=Porta Locale
PL=Porta Locale
PR = Porta Remota
Active Socket
Active Socket
Apertura di una connessione* il server accetta la richiesta di connessione e crea un proprio active socket che rappresenta il suo punto terminale della connessione* tutti i segmenti TCP scambiati tra client e server vengono trasmessi mediante la coppia di active sockets creati
Active Socket
Active Socket
SIP = Indirizzo IP del Server
Lezione 6: TCP: Stream Sockets 10Andrea Corradini
Apertura di una connessioneThree ways handshake
• 1) Il client A invia un segmento SYN a B - il flag SYN vale 1 e il campo Sequence number contiene il valore x che specifica l'Initial Sequence Number (ISN) di A;
• 2) B invia un segmento SYN/ACK ad A - i flag SYN e ACK sono impostati a 1, il campo Sequence number contiene il valore y che specifica l'ISN di B e il campo Acknowledgment number contiene il valore x+1 confermando la ricezione del ISN di A;
• 3) A invia un segmento ACK a B - il campo Acknowledgment number contiene il valore y+1 confermando la ricezione del ISN di B.
• Il terzo segmento permette anche all'host B una stima del timeout iniziale, come tempo intercorso tra l'invio di un segmento e la ricezione del corrispondente ACK.
Lezione 6: TCP: Stream Sockets 11Andrea Corradini
Lezione 6: TCP: Stream Sockets 12Andrea Corradini
IL PROTOCOLLO TCP: STREAM SOCKETS
Il server pubblica un proprio servizio associandolo al listening socket, creato sulla porta remota PR Il client C che intende usufruire del servizio deve conoscere l'indirizzo IP del server, SIP ed il riferimento alla porta remota PR a cui è associato il servizio La richiesta di creazione del socket
produce in modo atomico la richiesta di connessione al server il protocollo di richiesta della connessione viene completamente gestito dal
supporto Quando la richiesta di connessione viene accettata dal server, il supporto in esecuzione sul server crea in modo automatico un nuovo active socket AS.
AS è utilizzato per l’interazione con il client. Tutti i messaggi spediti dal client vengono diretti automaticamente sul nuovo socket creato.
Lezione 6: TCP: Stream Sockets 13Andrea Corradini
STREAM SOCKET JAVA API: LATO CLIENTClasse java.net.Socket : costruttori
public Socket(InetAddress host, int port) throws IOException– crea un active socket e tenta di stabilire, tramite esso, una
connessione con l’host individuato da InetAddress, sulla porta port. Se la connessione viene rifiutata, lancia una eccezione di IO
public Socket (String host, int port) throws UnKnownHostException, IOE...– come il precedente, l’host è individuato dal suo nome simbolico
(interroga automaticamente il DNS)
public Socket (String host, int port, InetAddress locIA, int locPort) ....– tenta di creare una connessione verso l’host host, sulla porta port,
dalla interfaccia locale localIA, dalla porta locale locPort– utile per macchine dotate di più schede di rete, ad esempio un host con
due indirizzi IP, uno visibile da Internet, l’altro solo a livello di rete locale.
Lezione 6: TCP: Stream Sockets 14Andrea Corradini
PORT SCANNER: INDIVIDUAZIONE SERVIZI TCP ATTIVI SU UN HOST
import java.net.*; import java.io.*;
public class TCPPortScanner {
public static void main(String args[ ]){
String host;
try { host = args[0];
} catch (ArrayIndexOutOfBoundsException e) { host= "localhost"; };
for (int i = 1; i< 1024; i++){try{ new Socket(host, i);
System.out.println("Esiste un servizio sulla porta" + i); } catch (UnknownHostException ex){
System.out.println("Host Sconosciuto"); } catch (IOException ex) {
System.out.println("Non esiste un servizio sulla porta"+i); }}}}
Lezione 6: TCP: Stream Sockets 15Andrea Corradini
PORT SCANNER: INDIVIDUAZIONE SERVIZI TCP ATTIVI SU UN HOST
• Nella classe TCPPortScanner– il client richiede la connessione tentando di creare un socket su ognuna
delle prime 1024 porte di un host– nel caso in cui non vi sia alcun servizio attivo, il socket non viene creato
e viene invece sollevata un'eccezione– Osservazione: il programma effettua 1024 interrogazioni al DNS, una
per ogni socket che tenta di creare• Per migliorare il comportamento del programma: utilizzare il costruttore
public Socket(InetAddress host, int port) throws IOException
– il DNS viene interrogato una sola volta, prima di entrare nel ciclo di scanning, dalla InetAddress.getByName
– si usa l’InetAddress invece del nome dell’host per costruire i sockets
Lezione 6: TCP: Stream Sockets 16Andrea Corradini
STREAM BASED COMMUNICATION
Dopo che la richiesta di connessione viene accettata, client e server
● associano agli active socket streams di byte di input/output
● poichè gli stream sono unidirezionali si usano due stream diversi, associati agli stessi socket, rispettivamente per l'input e per l'output
● la comunicazione avviene mediante lettura/scrittura di dati sullo stream
● come sempre, si possono usare wrappers con gli stream
Descriptor: Local port, Local IPaddr, Remote Port, Remote IPaddr
OutputStream
InputStream
Send Buffer
Receive Buffer
Struttura del Socket TCP
Lezione 6: TCP: Stream Sockets 17Andrea Corradini
NETSTAT: ANALIZZARE LO STATO DI UN SOCKET
Uno 'snapshot' dei socket a cui sono state associate connessioni attive può essere ottenuto mediante l'utility netstat (network statistics), disponibile sui principali sistemi operativi
Lezione 6: TCP: Stream Sockets 18Andrea Corradini
NETSTAT: ANALIZZARE LO STATO DI UN SOCKET
• Proto: protocollo associato al socket (TCP, UDP,...)
• RecV-Q, Send-Q: numero di bytes presenti nel receive buffer e nel send buffer
• Local Address: indirizzo IP + porta locale a cui è associato il socket
• Foreign Address: indirizzo IP + porta a cui è associato il socket
• State: stato della connessione– LISTEN : il server sta attendendo richieste di connessione– TIMEWAIT: il client ha iniziato la procedura di chiusura della
connessione, che non è ancora stata completata– ESTABLISHED: Il client ha ricevuto il SYN dal server (3-way
handshake completato) e la connessione è stata stabilita– Altri stati corrispondono ai diversi stati del 3-way handshake o del
protocollo definito da TCP per la chiusura del socket
Lezione 6: TCP: Stream Sockets 19Andrea Corradini
STREAM BASED COMMUNICATIONPer associare uno stream di input/output ad un socket esistono i metodi
public InputStream getInputStream( ) throws IOException
public OutputStream getOutputStream( ) throws IOException
che applicati ad un oggetto di tipo Socket restituiscono uno stream associato al socket ogni valore scritto su uno stream di output associato al socket viene
copiato nel Send Buffer ogni valore letto dallo stream viene prelevato dal Receive Buffer
Il client può leggere dallo stream
• un byte/ una sequenza di bytes
• dati di tipo qualsiasi (anche oggetti) mediante l’uso di opportuni filtri (DataInputStream, ObjectInputStream,… )
Lezione 6: TCP: Stream Sockets 20Andrea Corradini
UN SEMPLICE ESEMPIO DI CLIENT TCP: ECHO
import java.net.*; import java.io.*; import java.util.*;
public class TCPEchoClient {
public static void main (String args[]) throws Exception{
Scanner console = new Scanner( System.in); // per leggere da tastiera
InetAddress ia = InetAddress.getByName("localhost");
int port = 12345; Socket echosocket = null;
try{ echosocket = new Socket (ia, port);} //creo socket e connessione
catch (Exception e){System.out.println(e); return;}
InputStream is = echosocket.getInputStream( ); // creo input stream
DataInputStream netIn = new DataInputStream(is);
OutputStream os = echosocket.getOutputStream( ); //creo output str-
DataOutputStream netOut = new DataOutputStream(os);
Lezione 6: TCP: Stream Sockets 21Andrea Corradini
boolean done=false;
while (! done){
String linea = console.nextLine( ); // leggo da tastiera
System.out.println (linea);
netOut.writeUTF(linea); // scrivo sull'output stream del socket
NetworkOut.flush( );
String echo = netIn.readUTF( ); // leggo dall'input stream
System.out.println (“> “ + echo);
if (linea.equals("exit")) {done = true;
echosocket.close ( ); } // chiudo il socket
} } } }
UN SEMPLICE ESEMPIO DI CLIENT TCP: ECHO
Lezione 6: TCP: Stream Sockets 22Andrea Corradini
STRUTTURA DI UN SERVER
Comportamento di un Server Sequenziale:
• crea un Listening Socket LS sulla porta associata al servizio pubblicato.
• si mette in ascolto su LS (si blocca fino al momento in cui arriva una richiesta di connessione)
• quando accetta una richiesta di connessione da parte di un client C, crea un nuovo Active Socket su cui avviene la comunicazione con C
• associa all' Active Socket uno o più stream (di input e/o di output) su cui avverrà la comunicazione con il client
• quando l’interazione con il client è terminata, chiude il data socket e torna ad ascoltare su LS ulteriori richieste di connessione
Lezione 6: TCP: Stream Sockets 23Andrea Corradini
STREAM MODE SOCKET API: LATO SERVER
Classe java.net.ServerSocket: costruttori
public ServerSocket(int port) throws BindException, IOException
public ServerSocket(int port, int length) throws BindException, IOException– costruisce un listening socket, associandolo alla porta port. Il
parametro length indica la lunghezza della coda in cui vengono memorizzate le richieste di connessione (lunghezza massima della coda stabilita dal sistema operativo). Se la coda è piena, eventuali ulteriori richieste di connessione vengono rifiutate.
public ServerSocket(int port, int length, InetAddress bindAddress) throws...– permette di collegare il socket ad uno specifico indirizzo IP locale. – utile per macchine dotate di più schede di rete, ad esempio un host con
due indirizzi IP, uno visibile da Internet, l’altro solo a livello di rete locale.
Lezione 6: TCP: Stream Sockets 24Andrea Corradini
Esempio: ricerca dei servers attivi sull’host locale, catturando le BindException
import java.net.*;
public class TCPLocalServerScanner {
public static void main(String args[]){
for (int port= 1; port<= 1024; port++)
try {new ServerSocket(port);}
catch (BindException ex) {System.out.println(port + "occupata");}
catch (Exception ex) {System.out.println(ex);}
}
}
STREAM MODE SOCKET API: LATO SERVER
Lezione 6: TCP: Stream Sockets 25Andrea Corradini
Per accettare una nuova connessione dal listening socket si usa il metodo:
public Socket accept( ) throws IOException
della classe ServerSocket che restituisce l'active socket per la connessione.
• quando il processo server invoca il metodo accept( ), pone il server in attesa di nuove connessioni.
• se non ci sono richieste, il server si blocca (possibile utilizzo di time-outs)
• se c’è almeno una richiesta, il processo si sblocca e costruisce un nuovo socket tramite cui avviene la comunicazione effettiva tra cliente server
STREAM MODE SOCKET API: LATO SERVER
Lezione 6: TCP: Stream Sockets 26Andrea Corradini
ESEMPIO DI SERVER TCP: ECHO
Echo Server
• si mette in attesa di richieste di connessione
• dopo aver accettato una connessione, si mette in attesa di una stringa dal client e gliela rispedisce
• quando riceve la stringa “exit” chiude la connessione con quel client e torna ad accettare nuove connessioni
Lezione 6: TCP: Stream Sockets 27Andrea Corradini
import java.net.*; import java.io.*;
public class TCPEchoServer {
public static void main(String args[]) throws Exception{
int port= 12345;
ServerSocket ss = new ServerSocket(port,2); // listener socket
while (true){Socket sdati = ss.accept( ); // accetto e creo active socketInputStream is = sdati.getInputStream( ); // creo input streamDataInputStream netIn = new DataInputStream(is);OutputStream out = sdati.getOutputStream( ); // creo output strDataOutputStream netOut = new DataOutputStream(out);
ESEMPIO DI SERVER TCP: ECHO
Lezione 6: TCP: Stream Sockets 28Andrea Corradini
boolean done = false;while (!done){ // ciclo ascolta/ripeti
String echo = netIn.readUTF( );netOut.writeUTF(echo);
if (echo.equals("exit")){ // termine servizio System.out.println("finito"); done = true;} // interrompe ciclo e si rimette in ascolto
} } } }
ESEMPIO DI SERVER TCP: ECHO
Lezione 6: TCP: Stream Sockets 29Andrea Corradini
TCP BUFFERING
Per analizzare il comportamento dei buffer associati ad un socket TCP, consideriamo prima il caso in cui l'applicazione legga/scriva direttamente sequenze di bytes (contenute in array di bytes gestiti dall'applicazione) sugli/dagli stream.
byte [ ]
Send-Q Recv-Q
Gestito dal Supporto
Gestito dalla
applicazione
Gestito dal
Supporto
Gestito dalla
applicazione
byte [ ]
Lezione 6: TCP: Stream Sockets 30Andrea Corradini
TCP BUFFERING• Ipotesi: si utilizzano write/read, per scrivere/leggere array di byte
sugli/dagli streams.– La write( ) trasferisce i byte nel Send-Q buffer, se esiste abbastanza
spazio nel buffer. Se non riesce a scrivere tutti i byte nel Send-Q buffer, si blocca.
– La read( ) legge i dati disponibili nel Recv-Q al momento della invocazione sull'InputStream. Se Recv-Q buffer non contiene dati si blocca.
• Non esiste, in generale, alcuna corrispondenza tra
– le scritture effettuate sull'OutputStream ad un capo dello stream, e
– le letture dall'InputStream effettuate all'altro capo
• I dati scritti sull'OutputStream mediante una singola scrittura possono, in generale, essere letti mediante un insieme di operazioni di lettura
Lezione 6: TCP: Stream Sockets 31Andrea Corradini
TCP BUFFERING: UN ESEMPIO
byte [ ] buffer0 = new byte[1000];
byte [ ] buffer1 = new byte[2000];
byte [ ] buffer2 = new byte[5000];
....
Socket s = new Socket(destAddr, destPort); // creo active socket
OutputStream out = s.getOutputStream( ); // creo output stream
.....
out.write(buffer0); .... // scrivo 1000 bytes
out.write(buffer1); .... // scrivo 2000 bytes
out.write(buffer2); .... // scrivo 5000 bytes
s.close();
Lezione 6: TCP: Stream Sockets 32Andrea Corradini
TCP BUFFERING: STATO DEI BUFFERS
Stato dei buffer dopo l'esecuzione di tutte le write( ), ma prima di una qualsiasi operazione di read, uno scenario possibile
Questo scenario può essere analizzato mediante l'esecuzione di netstat
Mittente
Destinatario
Lezione 6: TCP: Stream Sockets 33Andrea Corradini
TCP BUFFERING: STATO DEI BUFFERS
• Se il ricevente esegue una read con un byte array di dimensione 2000, nella situazione mostrata dalla figura precedente, la read
– riempe parzialmente il byte array
– l'applicazione riceve 1000 byte prodotti dalla prima write( ) e 500 dalla seconda
• Se necessario, l'applicazione deve utilizzare opportuni meccanismi per delimitare i dati prodotti da write( ) diverse
Lezione 6: TCP: Stream Sockets 34Andrea Corradini
TCP BUFFERING: STATO DEI BUFFERS
Se il ricevente esegue una read con un byte array di dimensione 4000, nella
situazione mostrata in figura, la read
• riempe completamente il byte array
• restituisce 1500 caratteri prodotti dalla seconda write( ) e 2500 dalla terza
• alcuni bytes rimangono nel receive buffer e verranno recuperati con
una successiva read( )
Lezione 6: TCP: Stream Sockets 35Andrea Corradini
TCP BUFFERING: USO DI WRITE( ) E READ( ) Supponiamo che il client spedisce una sequenza di byte con write( ) su di un
OutputStream, e il server legge la sequenza con delle read( ) dal corrispondente InputStream
Il client può riempire un array di byte buffer arbitrario e inviarlo con write(buffer): la write( ) blocca finché non ha scritto tutto. Può usare write(byte[ ] b, int off, int len) per spedire solo una porzione dell'array.
Il server può leggere usando un array di byte di grandezza arbitraria, MA DEVE CONTROLLARE SEMPRE QUANTI BYTE HA LETTO:
int read(byte [] buffer) restituisce il numero di byte letti, che può essere al massimo buffer.length, e -1 se lo stream è terminato dal sender.
int read(byte[] buffer, int offset, int length) analogo.
Lezione 6: TCP: Stream Sockets 36Andrea Corradini
TCP BUFFERING: RISCHIO DI DEADLOCK Meccanismo di Controllo del Flusso: quando il RecvQ è pieno, TCP impedisce
il trasferimento di ulteriori bytes dal corrispondente SendQ
Questo meccanismo può provocare situazioni di deadlock
La situazione di deadlock può essere generata nel caso di due programmi che si inviano simultaneamente grosse quantità di dati
Esempio: client e server si scambiano files di grosse dimensioni il receive buffer del server viene riempito così come il send buffer del
client l'esecuzione del client viene bloccata a causa di un'ulteriore write( ). il server non svuota il proprio receive buffer perchè bloccato, a sua
volta, nell'invio di una grossa quantità di dati al client
Lezione 6: TCP: Stream Sockets 37Andrea Corradini
SOCKETS: CHIUSURA• Un socket (oggetto della classe Socket) viene chiuso automaticamente:
– dal garbage collector, se non più raggiungibile
– alla terminazione del programma
• In certi casi (esempio un web browser)
– il numero di sockets aperti può essere molto alto
– il numero massimo di sockets supportati può essere raggiunto prima che la garbage collection ne elimini alcuni
– può essere necessario chiudere esplicitamente alcuni sockets che non vengono più utilizzati
– chiusura esplicita di un socket s : s.close( )
• E' buona prassi chiuderre i socket da programma quando non servono più
Lezione 6: TCP: Stream Sockets 38Andrea Corradini
SOCKETS: CHIUSURA
for (int i = 1; i< 1024; i++){
try { s = new Socket(host, i);
System.out.println("Esiste un servizio sulla porta" + i);
} catch (UnknownHostException ex){System.out.println("Host Sconosciuto");
} catch (IOException ex) {System.out.println("Non esiste un servizio sulla porta"+i);
} finally{try{ if (s!=null) {
s.close( ); s=null; System.out.println("chiuso");}
} catch(IOException ex){ }; } }
Lezione 6: TCP: Stream Sockets 39Andrea Corradini
SOCKETS: CHIUSURA ASIMMETRICA
• I metodi shutdownInput( ) e shutdownOutput( ) – consentono di chiudere indipendentemente gli stream di ingresso/
uscita associati al socket
• Esempio:
– un client non deve inviare ulteriori dati al socket, ma deve attendere una risposta dal socket stesso
– Il client può chiudere lo stream di output associato al socket e mantenere aperto lo stream di input per ricevere la risposta
• La lettura di un dato da un socket il cui corrispondente OutputStream è stato chiuso, restituisce il valore -1, che può essere quindi utilizzato come simbolo di fine sequenza
Lezione 6: TCP: Stream Sockets 40Andrea Corradini
Chiusura di una connessioneThree or Four ways handshake
• Una connessione TCP può essere chiusa in due modi: con un handshake a tre vie, in cui le due parti chiudono contemporaneamente le rispettive connessioni, o con uno a quattro vie, in cui le due connessioni vengono chiuse in tempi diversi.
• L'handshake a 3 vie è simile a quello usato per l'apertura della connessione, ma si usa il flag FIN invece del SYN. Un terminale invia un pacchetto con la richiesta FIN, l'altro risponde con un FIN + ACK, ed infine il primo manda l'ultimo ACK, e l'intera connessione viene terminata.
• L'handshake a 4 vie invece viene utilizzato quando la disconnessione non è contemporanea tra i due terminali in comunicazione. In questo caso uno dei due terminali invia la richiesta di FIN, e attende l'ACK. L'altro terminale farà poi altrettanto, generando quindi un totale di 4 pacchetti.
Lezione 6: TCP: Stream Sockets 41Andrea Corradini
ESERCIZIO; COMPRESSIONE DI FILE
Progettare un'applicazione client/server in cui il server fornisca un
servizio di compressione di dati.
Il client legge chunks di bytes da un file e li spedisce al server che
provvede alla loro compressione. Il server restituisce i bytes in formato
compresso al client che provvede a creare un file con lo stesso nome del
file originario e con estensione gz, che contiene i dati ricevuti dal server.
La comunicazione tra client e server utilizza il protocollo TCP.
Per la compressione si può utilizzare la classe JAVA GZIPOutputStream.
Individuare le condizioni necessarie affinchè il programma scritto generi
una situazione di deadlock e verificare che tale situazione si verifica
realmente quando tali condizioni sono verificate.
Lezione 6: TCP: Stream Sockets 42Andrea Corradini
STREAM MODE SOCKET API:INTERAZIONE CON SERVERS PREDEFINITI
Esercizio: considerare un servizio attivo su una porta pubblicata da un
Server (es 23 Telnet, 25 SMTP, 80 HTTP). Definire un client JAVA che
utilizzi tale servizio, dopo aver controllato che sia attivo.
Provare anche con i seguenti servizi (vedere JAVA Network Programming)
Daytime(porta 13): il client richiede una connessione sulla porta 13, il server invia la data e chiude la connessione
Echo (port 7): il client apre una connesione sulla porta 7 del server ed invia un messaggio. Il server restituisce il messaggio al client
Finger (porta 79): il client apre una connessione ed invia una query, il Server risponde alla query
Whois (porta 43): il client invia una stringa terminata da return/linefeed. La stringa può contenere, ad esempio, un nome. Il server invia alcune informazioni correlate a quel nome
Lezione 6: TCP: Stream Sockets 43Andrea Corradini
CLASSE SOCKET: OPZIONI
• la classe socket offre la possibilità di impostare diverse proprietà del
socket
• Proprietà:
– SO_TIMEOUT
– SO_RCVBUF
– SO_SNDBUF
– SO_KEEPALIVE
– TCP_NODELAY
– SO_LINGER
...........
Lezione 6: TCP: Stream Sockets 44Andrea Corradini
CLASSE SOCKET: SO_TIMEOUT
SO_TIMEOUT – consente di associare un time out al socket
if (s.getSoTimeout( ) == 0) s.setSoTimeout(1800000);
Il timeout viene specificato in millisecondi Quando eseguo una lettura bloccante dal socket, l'operazione si può
bloccare in modo indefinito
• SO_TIMEOUT: definisce un intervallo di tempo massimo per l'attesa dei dati
• Nel caso in cui il time out scada prima della ricezione dei dati, viene sollevata una eccezione
Lezione 6: TCP: Stream Sockets 45Andrea Corradini
CLASSE SOCKET: SO_RCVBUF, SO_SNDBUF SO_RCVBUF controlla la dimensione del buffer utilizzato per ricevere i dati.
– E' possibile impostare la dimensione del buffer di ricezione
sock.setReceiveBufferSize(4096) – La modifica non viene garantita su tutti i sistemi operativi– Per reperire la dimensione del buffer associato
int size = sock. getReceiveBufferSize( ) – Alternativa: utilizzare i BufferedInputStream/BufferedReader.
SO_SNDBUF : analogo per il buffer associato alla spedizione
int size = sock.getSendBufferSize( ); Attenzione: questi comandi non sono implementati correttamente su alcuni sistemi operativi
Lezione 6: TCP: Stream Sockets 46Andrea Corradini
CLASSE SOCKET: SO_KEEPALIVE
• So_keepalive: tecnica utilizzata per monitorare le connessioni aperte e controllare se il partner risulta ancora attivo
• introdotto per individuare i sockets “idle” su cui non sono stati inviati dati per un lungo intervallo di tempo
• Per default, su ogni socket vengono spediti solo dati inviati dalla applicazione
• Un socket può rimanere inattivo per ore, o anche per giorni– Esempio: crash di un client prima dell'invio di un segnale di fine
sequenza. In questo caso, il server può sprecare risorse (tempo di CPU, memoria,...) per un client che ha subito un crash
– Consente un'ottimizzazione delle risorse
Lezione 6: TCP: Stream Sockets 47Andrea Corradini
CLASSE SOCKET: SO_KEEPALIVE
sock.setSoKeepAlive(true) abilita il keep alive sul socket sock.
• il supporto invia periodicamente dei messaggi di keep alive sul socket per testare lo stato del partner.
• se il partner è ancora attivo, risponde mediante un messaggio di ack
• nel caso di mancata risposta viene reiterato l'invio del keep alive per un certo numero di volte
• se non si riceve alcun acknowledgment, il socket viene portato in uno stato di 'reset'
• ogni lettura, scrittura o operazione di chiusura su un socket posto in stato di reset(), solleva un'eccezione
• questa funzionalità può non essere implementata su alcune piattaforme, nel qual caso il metodo solleva un'eccezione
Lezione 6: TCP: Stream Sockets 48Andrea Corradini
CLASSE SOCKET: TCP_NODELAYServe per disabilitare l'algoritmo di Nagle, introdotto per evitare che il TCP
spedisca una sequenza di piccoli segmenti, quando la frequenza di invio dei dati da parte della applicazione è molto bassa
• L'algoritmo di Nagle riduce il numero di segmenti spediti sulla rete fondendo in un unico segmento più dati
• Applicazione originaria dell'algoritmo– Sessioni Telnet, in cui è richiesto di inviare i singoli caratteri
introdotti, mediante keyboard, dall'utente– Se l'algoritmo di Nagle non viene applicato, ogni carattere viene
spedito in un singolo segmento,(1 byte di data e decine di byte di header del messaggio)
• Motivazioni per disabilitare l'algoritmo di Nagle: trasmissioni di dati in 'tempo reale', ad esempio movimenti del mouse per un'applicazione interattiva come un gioco multiplayer.
Lezione 6: TCP: Stream Sockets 49Andrea Corradini
CLASSE SOCKET: TCP_NODELAY
Algoritmo di Nagle:
• In generale, per default, l'algoritmo di Nagle risulta abilitato
• tuttavia alcuni sistemi operativi disabilitano l'algoritmo di default
• per disabilitare l'algoritmo di Nagle
sock.setTcpNoDelay(true)
disabilita la bufferizzazione (no delay = non attendere, inviare subito un segmento, non appena l'informazione è disponibile)
• JAVA RMI disabilita l'algoritmo di Nagle: lo scopo è quello di inviare prontamente il segmento contenente i parametri di una call remota oppure il valore restituito dall'invocazione di un metodo remoto
Lezione 6: TCP: Stream Sockets 50Andrea Corradini
CLASSE SOCKET: SO_LINGER
La proprietà SO_LINGER (to linger = indugiare) viene utilizzata per
specificare cosa accade quando viene invocato il metodo close( ) su un
socket TCP.
A seconda del valore di SO_LINGER può accadere che
• Linger = false (default): il contenuto del buffer di invio associato al socket viene inviato al destinatario, mentre i dati nel buffer di ricezione vengono scartati. Il thread che esegue il metodo close( ) non attende la terminazione di queste attività che avvengono quindi in modo asincrono.
Questo è lo scenario di default, che però non garantisce che i dati vengano consegnati correttamente. In caso di crash del destinatario, ad esempio, i dati nel buffer di spedizione non vengono consegnati
Lezione 6: TCP: Stream Sockets 51Andrea Corradini
CLASSE SOCKET: SO_LINGER• Linger == true, Linger time == 0: Vengono scartati sia gli eventuali
dati nel buffer di ricezione che quelli da inviare. Come prima, lo scarto avviene in modo asincrono.– Utilizzato quando si vuole terminare la connessione
immediatamente, senza spedire i dati
• Linger == true e Linger time != 0: Vengono inviati eventuali dati presenti nel buffer al destinatario e si scartano gli eventuali dati nel buffer di ricezione. Il thread che esegue il metodo close( ) si blocca per il linger time oppure fino a che tutti i dati spediti sono stati confermati a livello TCP. Dopo linger time viene sollevata un'eccezione– Quando si vuole garantire che il metodo close( ) ritorni solo
quando i dati sono stati consegnati, oppure che sollevi un'eccezione nel caso in cui scatti il time-out definito da linger-time
Lezione 6: TCP: Stream Sockets 52Andrea Corradini
CLASSE SOCKET: SO_LINGER
public void setSoLinger (boolean no, int seconds) throws SocketException
public int getSoLinger ( ) throws SocketException
• per default, SO_LINGER = false: il supporto tenta di inviare i datagrams rimanenti, anche dopo che il socket è stato chiuso
• per controllare la gestione dei dati presenti al momento della chiusura
if (s.getSoLinger( ) == -1) s.setSoLinger(true, 240);
il metodo close( ) si blocca ed attende 240 secondi (4 minuti) prima
di eliminare i datagrams rimanenti. Se il tempo di attesa viene
impostato a 0, i datagram vengono eliminati immediatamente.
Lezione 8: TCP Sockets e Multicast 1Andrea Corradini
Lezione n.8LPR-B-09
TCP Sockets & Multicast 24/11/2009
Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
Lezione 8: TCP Sockets e Multicast 2Andrea Corradini
Sommario
• Invio di oggetti tramite TCP con serializzazione (rischio di deadlock)
• Qualcosa sugli esercizi...
• Ancora sugli stream socket e su three-ways handshaking di TCP
• De-multiplexing di frammenti TCP
• Unreliable Multicast: concetti e API Java
• Panoramica su Linux Networking Tools (grazie a Daniele Sgandurra)
Lezione 8: TCP Sockets e Multicast 3Andrea Corradini
INVIO DI OGGETTI SU CONNESSIONI TCP
• Per inviare oggetti su una connessione TCP basta usare la serializzazione: gli oggetti inviati devono implementare l'interfaccia Serializable
• Si possono usare i filtri ObjectInputStream / ObjectOutputStream per incapsulare gli stream ottenuti invocando getInputStream() / getOutputStream() sul socket
• Quando creo un ObjectOutputstream viene scritto lo stream header sullo stream. In seguito scrivo gli oggetti che voglio inviare sullo stream
• L'header viene letto quando viene creato il corrispondente ObjectInputStream
• L'invio/ ricezioni degli oggetti sullo/dallo stream avviene mediante scritture/letture sullo stream (writeObject(), readObject())
Lezione 8: TCP Sockets e Multicast 4Andrea Corradini
INVIO DI OGGETTI SU UNA CONNESSIONE TCP
import java.io.*;
public class Studente implements Serializable {
private int matricola;
private String nome, cognome, corsoDiLaurea;
public Studente (int matricola, String nome, String cognome,String corsoDiLaurea) {
this.matricola = matricola; this.nome = nome;this.cognome = cognome; this.corsoDiLaurea = corsoDiLaurea;}
public int getMatricola () { return matricola; }
public String getNome () { return nome; }
public String getCognome () { return cognome; }
public String getCorsoDiLaurea () { return corsoDiLaurea; } }
Lezione 8: TCP Sockets e Multicast 5Andrea Corradini
INVIO DI OGGETTI SU UNA CONNESSIONE TCP- LATO SERVER
import java.io.*; import java.net.*;
public class TCPObjectServer {
public static void main (String args[]) {
try { ServerSocket server = new ServerSocket (3575);
Socket clientsocket = server.accept();
ObjectOutputStream output =
new ObjectOutputStream (clientsocket.getOutputStream ());
output.writeObject("<Welcome>");
Studente studente = new Studente (14520,"Mario","Rossi","Informatica");
output.writeObject(studente); output.writeObject("<Goodbye>");
clientsocket.close();
server.close();
} catch (Exception e) { System.err.println (e); } } }
Lezione 8: TCP Sockets e Multicast 6Andrea Corradini
INVIO DI OGGETTI SU UNA CONNESSIONE TCP-LATO CLIENT
import java.io.*; import java.net.*;
public class TCPObjectClient { public static void main (String args[ ]) {
try { Socket socket = new Socket ("localhost", 3575);
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
String beginMessage = (String) in.readObject();
Studente stud = (Studente) in.readObject();
System.out.println(beginMessage);
System.out.print(stud.getMatricola() + " - " + stud.getNome() + " ");
System.out.println(stud.getCognome() + " - " + stud.getCorsoDiLaurea());
String endMessage = (String) in.readObject();
System.out.println (endMessage); socket.close();} catch (Exception e) { System.out.println (e); } } }
Lezione 8: TCP Sockets e Multicast 7Andrea Corradini
INVIO DI OGGETTI SU UNA CONNESSIONE TCP- LATO CLIENT
Stampa prodotta lato Client
<Welcome>
14520 - Mario Rossi - Informatica
<Goodbye>
Lezione 8: TCP Sockets e Multicast 8Andrea Corradini
OBJECT INPUT/OUTPUT STREAM: RISCHIO DI DEADLOCK
• La creazione dell'ObjectInputStream cerca di leggere lo header. Se questo non è stato ancora creato, si blocca.
• Quindi si verifica una situazione di deadlock se i due partner della connessione eseguono le istruzioni nel seguente ordine (s è il socket locale):
ObjectInputStream in = new ObjectInputStream(s.getInputStream( ));
ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream( ));
Infatti,
• entrambi tentano di leggere l'header dello stream dal socket
• l'header viene generato quando viene viene creato l'ObjectOutputStream
• nessuno dei due è in grado di generare l'ObjectOutputStream, perchè bloccato
• E' sufficiente invertire l'ordine di creazione degli stream in almeno uno dei partner
Lezione 8: TCP Sockets e Multicast 9Andrea Corradini
ESERCIZIO:ASTA ELETTRONICA
Sviluppare un programma client server per il supporto di un'asta elettronica.
Ogni client possiede un budget massimo B da investire. Il client può
richiedere al server il valore V della migliore offerta pervenuta fino ad un
certo istante e decidere se abbandonare l'asta, oppure rilanciare. Se il valore
ricevuto dal server supera B,l'utente abbandona l'asta, dopo aver avvertito il
server. Altrimenti, il client rilancia, inviando al server un valore maggiore di V.
Il server invia ai client che lo richiedono il valore della migliore offerta
ricevuta fino ad un certo momento e riceve dai client le richieste di rilancio.
Per ogni richiesta di rilancio, il server notifica al client se tale offerta può
essere accettata (nessuno ha offerto di più nel frattempo), oppure è
rifiutata.
Lezione 8: TCP Sockets e Multicast 10Andrea Corradini
ESERCIZIO:ASTA ELETTRONICA
Il server deve attivare un thread diverso per ogni client che intende
partecipare all'asta.
La comunicazione tra clients e server deve avvenire mediante socket
TCP. Sviluppare due diverse versioni del programma che utilizzino,
rispettivamente una codifica testuale dei messaggi spediti tra client e
server oppure la serializzazione offerta da JAVA in modo da scambiare
oggetti tramite la connessione TCP
Lezione 8: TCP Sockets e Multicast 11Andrea Corradini
Verso il MULTICAST:STRUTTURA GENERALE DI UN SOCKET
• remote port ed host significative solo per socket TCP
• SendQ, RecQ: buffer di invio/ricezione
• ogni socket è caratterizzato da informazioni sul suo stato (ad esempio closed). Lo stato del socket è visibile tramite il comando netstat
Lezione 8: TCP Sockets e Multicast 12Andrea Corradini
CONNESSIONE LATO CLIENT: STATO DEL SOCKET
Quando il client invoca il costruttore Socket( ). • lo stato iniziale del socket viene impostato a Closed, la porta (P) e l'indirizzo locale (A.B.C.D) sono impostate dal supporto• dopo aver inviato il messaggio iniziale di handshake, lo stato del socket passa a SYN_SENT (inviato segmento SYN)• il client rimane bloccato fino a che il server riscontra il messaggio di handshake mediante un ack
Lezione 8: TCP Sockets e Multicast 13Andrea Corradini
CONNESSIONE LATO SERVER: STATO DEL SOCKET
Il Server crea un server socket sulla porta Q• se non viene specificato alcun indirizzo IP (wildcard = *), il server può ricevere connessioni da una qualsiasi delle sue interfacce• lo stato del socket viene posto a Listening: questo indica che il server sta attendendo connessioni da una qualsiasi interfaccia, sulla porta Q
Lezione 8: TCP Sockets e Multicast 14Andrea Corradini
CONNESSIONE LATO SERVER: STATO DEL SOCKET
• il server si sospende sul metodo accept( ) in attesa di una nuova connessione
• quando riceve una richiesta di connessione dal client, crea un nuovo socket. In tale socket
– indirizzo e porta remoti vengono inizializzati con l'indirizzo IP e la porta ricevuti dal client che ha richiesto la connessione
– L'indirizzo locale viene settato con l'indirizzo dell'interfaccia da cui è stata ricevuta la connessione.
– La porta locale viene inizializzata con quella a cui associata al server socket
• Quando il three-ways handshake è completato l'esecuzione del metodo accept( ) termina e restituisce un puntatore al socket creato, che viene inserito in una lista di socket associati al server socket.
Lezione 8: TCP Sockets e Multicast 15Andrea Corradini
CREAZIONE DI CONNESSIONI LATO SERVER
Lezione 8: TCP Sockets e Multicast 16Andrea Corradini
DEMULTIPLEXING DEI SEGMENTI TCP
• Quindi tutti i sockets associati allo stesso ServerSocket 'ereditano'
– la porta di ascolto
– l'indirizzo IP da cui è stata ricevuta la richiesta di connessione
• Questo implica che sullo stesso host possano esistere più sockets associati allo stesso indirizzo IP ed alla stessa porta locale (il Server Socket e tutti i Sockets associati,.....)
• Meccanismo di demultiplexing: utilizzato per decidere a quale socket è destinato un segmento TCP ricevuto su quella interfaccia e su quella porta
• La conoscenza dell'indirizzo e porta destinazione non risulta più sufficiente per individuare il socket a cui è destinato il segmento
Lezione 8: TCP Sockets e Multicast 17Andrea Corradini
DEMULTIPLEXING DEI SEGMENTI TCP
Definizione del meccanismo di demultiplexing:
• La porta locale riferita nel socket deve coincidere con quella contenuta nel segmento TCP
• Ogni campo del socket contenente una wildcard (*), può essere messo in corrispondenza con qualsiasi valore corrispondente contenuto nel segmento
• Se esiste più di un socket che corrisponde al segmento in input, viene scelto il socket
che contiene il minor numero di wildcards.
• in questo modo un segmento spedito dal client viene ricevuto sul socket S associato alla connessione con quel client, piuttosto che sul serversocket perchè S risulta 'più specifico' per quel segmento
Lezione 8: TCP Sockets e Multicast 18Andrea Corradini
GRUPPI DI PROCESSI: COMUNICAZIONE● Comunicazioni di tipo unicast = coinvolgono una sola coppia di processi● Ma diverse applicazioni di rete richiedono un tipo di comunicazione che
coinvolga un gruppo di hosts.
Applicazioni classiche usenet news: pubblicazione di nuove notizie ed invio di esse ad un gruppo
di hosts interessati videoconferenze: un segnale audio video generato su un nodo della rete
deve essere ricevuto dagli hosts associati ai partecipanti alla videoconferenza
Altre applicazioni massive multiplayer games: alto numero di giocatori che intergiscono in
un mondo virtuale DNS (Domain Name System): aggiornamenti delle tabelle di naming
inviati a gruppi di DNS, chats, instant messaging, applicazioni p2p
Lezione 8: TCP Sockets e Multicast 19Andrea Corradini
GRUPPI DI PROCESSI: COMUNICAZIONE
Comunicazione tra gruppi di processi realizzata mediante multicasting(one to many communication).
Comunicazione di tipo multicast
un insieme di processi formano un gruppo di multicast
un messaggio spedito da un processo a quel gruppo viene recapitato a tutti gli altri partecipanti appartenenti al gruppo
Un processo può lasciare un gruppo di multicast quando non è più interessato a ricevere i messaggi del gruppo
Lezione 8: TCP Sockets e Multicast 20Andrea Corradini
COMUNICAZIONE TRA GRUPPI DI PROCESSI
Multicast API: deve contenere primitive per
unirsi ad un gruppo di multicast (join). E’ richiesto uno schema di indirizzamento per identificare univocamente un gruppo.
lasciare un gruppo di multicast (leave).
spedire messaggi ad un gruppo. Il messaggio viene recapitato a tutti i processi che fanno parte del gruppo in quel momento
ricevere messaggi indirizzati ad un gruppo
Lezione 8: TCP Sockets e Multicast 21Andrea Corradini
COMUNICAZIONE TRA GRUPPI DI PROCESSI:IMPLEMENTAZIONE
L’implementazione del multicast richiede:
uno schema di indirizzamento dei gruppi
un supporto che registri la corrispondenza tra un gruppo ed i partecipanti
un'implementazione che ottimizzi l’uso della rete nel caso di invio di pacchetti ad un gruppo di multicast
Lezione 8: TCP Sockets e Multicast 22Andrea Corradini
MULTICAST: IMPLEMENTAZIONE
Server A invia un messaggio su un gruppo di multicast composto da 3
clients connessi allo stesso router (router2)
Server AServer A
Soluzione 1: router1 invia 3 messaggi con collegamenti di tipo unicast
Soluzione 2: router1 invia un unico messaggio.router2 replica il messaggio per i tre clients
router1
router2router2
router1
Lezione 8: TCP Sockets e Multicast 23Andrea Corradini
MULTICAST: IMPLEMENTAZIONE
Ottimizzazione della banda di trasmissione: il router che riceve un pacchettodi multicast MP invia un unico pacchetto sulla rete. Il pacchetto viene replicatosolo quando è necessario.Esempio: pacchetto di multicast spedito da Milano agli hosts Hosta, HostB,HostC
MP MP
MPMilano
HostB HostC
MP MP
MP
Roma
Bologna
Firenze
Bari
PisaHost A
Lezione 8: TCP Sockets e Multicast 24Andrea Corradini
INDIVIDUAZIONE GRUPPI DI MULTICAST
Indirizzo di multicast: indirizzo IP di classe D, che individua un gruppo di multicast
Indirizzo di classe D- intervallo 224.0.0.0 – 239-255-255-255
1110 …..
l’indirizzo di multicast è condiviso da tutti i partecipanti al gruppo
è possibile associare un nome simbolico ad un gruppo di multicast
Esempio: 224.0.1.1 ntp.mcast.net (network time protocol distributed service)
Lezione 8: TCP Sockets e Multicast 25Andrea Corradini
INDIVIDUAZIONE GRUPPI DI MULTICAST
• Il livello IP (nei routers) mantiene la corrispondenza tra l’indirizzo di multicast e gli indirizzi IP dei singoli hosts che partecipano al gruppo di multicast
• Gli indirizzi possono essere:
Permanenti : l'indirizzo di multicast viene assegnato dalla IANA (Internet Assigned Numbers Authority).
L'indirizzo rimane assegnato a quel gruppo, anche se, in un certo istante non ci sono partecipanti
Temporanei : Esistono solo fino al momento in cui esiste almeno un partecipante. Richiedono la definizione di un opportuno protocollo per evitare conflitti nell'attribuzione degli indirizzi ai gruppi
Lezione 8: TCP Sockets e Multicast 26Andrea Corradini
INDIVIDUAZIONE GRUPPI DI MULTICAST
• Gli indirizzi di multicast appartenenti all’intervallo
224.0.0.0 - 224.0.0.255
sono riservati per i protocolli di routing e per altre funzionalità a livello di rete
ALL-SYSTEMS.MCAST.NET 224.0.0.1 tutti gli host della rete locale
ALL-ROUTERS.MCAST.NET 224.0.0.2 tutti i routers della rete locale
• I routers non inoltrano mai i pacchetti che contengono questi indirizzi
• la maggior parte degli indirizzi assegnati in modo permanente hanno come prefisso 224.0, 224.1, 224.2, oppure 239
Lezione 8: TCP Sockets e Multicast 27Andrea Corradini
MULTICAST ROUTERS
• Per poter spedire e ricevere pacchetti di multicast oltre i confini della rete locale, occorre disporre di un router che supporta il multicast (mrouter)
• Problema: disponbilità limitata di mrouters
• Per testare se la vostra rete è collegata ad un mrouter, dare il comando % ping all-routers.mcast.net
Se si ottiene una risposta, c'è un mrouter sulla sottorete locale. Routers che non supportano multicast, possono utilizzare la tecnica del
tunnelling = trasmissione di pacchetti multicast mediante unicast UDP
Lezione 8: TCP Sockets e Multicast 28Andrea Corradini
CONNECTIONLESS MULTICAST
La comunicazione Multicast utilizza il paradigma connectionless
Motivazioni: gestione di un alto numero di connessioni richieste n(n-1) connessioni per un gruppo di n processi comunicazione connectionless adatta per il tipo di applicazioni verso cui è
orientato il multicast (trasmissione di dati video/audio).
• Esempio: invio dei frames di una animazione. E’ più accettabile la perdita occasionale di un frame piuttosto che un ritardo costante tra la spedizione di due frames successivi
Lezione 8: TCP Sockets e Multicast 29Andrea Corradini
UNRELIABLE VS. RELIABLE MULTICAST
Unreliable Multicast (multicast non affidabile):
• non garantisce la consegna del messaggio a tutti i processi che partecipano al gruppo di multicast.
• unico servizio offerto dalla multicast JAVA API standard (esistono package JAVA non standard che offrono qualche livello di affidabilità)
Reliable Multicast (multicast affidabile):
• garantisce che il messaggio venga recapitato una sola volta a tutti i processi del gruppo
• può garantire altre proprietà relative all’ordinamento con cui i messaggi spediti al gruppo di multicast vengono recapitati ai singoli partecipanti
Lezione 8: TCP Sockets e Multicast 30Andrea Corradini
MULTICAST API DI JAVA: MulticastSocket
MulticastSocket = socket su cui spedire/ricevere i messaggi verso/da un gruppo di multicast la classe MulticastSocket estende la DatagramSocket con alcune
funzionalità utili per il multicast
il ricevente deve creare un MulticastSocket su una determinata porta, iscrivere il socket al gruppo, e fare una receive.
il mittente deve inviare il messaggio (un DatagramPacket) specificando il gruppo e una porta.
il messaggio è ricevuto da tutti i MulticastSocket iscritti al gruppo e che stanno ricevendo sulla porta indicata.
Lezione 8: TCP Sockets e Multicast 31Andrea Corradini
MULTICAST: L’API JAVA
Uso delle porte per multicast sockets:Unicast • IP Address individua un host, • porta individua un servizioMulticast• IP Address individua un gruppo di hosts, • porta consente di partizionare dati di tipo diverso inviati allo stesso
gruppo
Gruppo dimulticast
invio dati
Host1
50004000
….
5000
4000 Host2
Esempio: porta 5000 traffico audio, porta 4000 traffico video
Lezione 8: TCP Sockets e Multicast 32Andrea Corradini
MULTICAST API DI JAVA: il receiverimport java.net.*;
public class MulticastTestReceiver{
public static void main (String [ ] args) throws Exception{InetAddress group = InetAddress.getByName(args[0]); // gruppoif (!group.isMulticastAddress()){ // controllo se è multicast
throw new IllegalArgumentException();}int port = Integer.parseInt(args[1]); // porta localeMulticastSocket ms = new MulticastSocket(port);ms.joinGroup (group); // mi iscrivo al gruppoDatagramPacket dp = new DatagramPacket(new byte[8192], 8192);ms.receive(dp); // ricevo e stampoSystem.out.println(new String(dp.getData(),0,dp.getLength())); }}
Lezione 8: TCP Sockets e Multicast 33Andrea Corradini
MULTICAST API DI JAVA: il senderimport java.net.*;
public class MulticastTestSender{
public static void main (String [ ] args) throws Exception{InetAddress group = InetAddress.getByName(args[0]); // gruppoif (!group.isMulticastAddress()){ // controllo se è multicast
throw new IllegalArgumentException(); }int port = Integer.parseInt(args[1]); // porta destinatariaSystem.out.println("String to send? ");byte [] data = Input.readLine().getBytes();DatagramPacket dp = // creo il pacchetto
new DatagramPacket(data, data.length, group, port);MulticastSocket ms = new MulticastSocket();ms.setTimeToLive(5); ms.send(dp); }} // spedisco
Lezione 8: TCP Sockets e Multicast 34Andrea Corradini
MULTICAST: più socket sulla stessa porta
Una porta non individua un servizio (processo) su un certo host:• Se attivo due istanze di MulticastTestReceiver sullo stesso host e sulla
stessa porta non viene sollevata una BindException (che viene invece sollevata se MulticastSocket è sostituito da un DatagramSocket)
Lezione 8: TCP Sockets e Multicast 35Andrea Corradini
MULTICAST SNIFFER
• Il programma riceve in input il nome simbolico di un gruppo di multicast si unisce al gruppo e 'sniffa' i messaggi spediti su quel gruppo, stampandone il contenuto
import java.net.*; import java.io.*;
public class MulticastSniffer {
public static void main (String[] args){InetAddress group = null; // indirizzo del gruppoint port = 0; // porta localetry{group = InetAddress.getByName(args[0]);
port = Integer.parseInt(args[1]);} catch(Exception e){System.out.println("Uso: " +
"java multicastsniffer multicast_address port"); System.exit(1); }
Lezione 8: TCP Sockets e Multicast 36Andrea Corradini
MULTICAST SNIFFER
MulticastSocket ms = null;
try{ ms = new MulticastSocket(port);
ms.joinGroup(group); // mi unisco al gruppo
byte [ ] buffer = new byte[8192];
while (true){DatagramPacket dp = new DatagramPacket(buffer, buffer.length);ms.receive(dp); // aspetto un pacchettoString s = new String(dp.getData()); // estraggo il messaggioSystem.out.println(s);} // .lo stampo
} catch (IOException ex){System.out.println (ex);
}
Lezione 8: TCP Sockets e Multicast 37Andrea Corradini
MULTICAST SNIFFER
finally{ // in ogni caso...
if (ms != null) { // se avevo aperto il multicast sockettry{
ms.leaveGroup(group); // lascio il gruppoms.close(); // chiudo il socket
} catch (IOException ex){}
}}}}
•
Lezione 8: TCP Sockets e Multicast 38Andrea Corradini
MULTICAST: TIME TO LIVE
• IP Multicast Scoping: meccanismo utilizzato per limitare la diffusione sulla rete di un pacchetto inviato in multicast
• ad ogni pacchetto IP viene associato un valore rappresentato su un byte, riferito come TTL (Time-To-Live) del pacchetto
• TTL assume valori nell’intervallo 0-255
• TTL indica il numero massimo di routers che possono essere attraversati dal pacchetto
• il pacchetto viene scartato dopo aver attraversato TTL routers
• meccanismo introdotto originariamente per evitare loops nel routing dei pacchetti
Lezione 8: TCP Sockets e Multicast 39Andrea Corradini
MULTICAST: TIME TO LIVE
Internet Scoping, implementazione
il mittente specifica un valore per del TTL per i pacchetti spediti
il TTL viene memorizzato in un campo dell’header del pacchetto IP
TTL viene decrementato da ogni router attraversato
Se TTL = 0 ⇒ il pacchetto viene scartato
Lezione 8: TCP Sockets e Multicast 40Andrea Corradini
MULTICAST: TIME TO LIVE
Valori consigliati per il ttl
Destinazione Valori di ttl processi sullo stesso host 0 processi sulla stessa sottorete locale 1 processi su reti locali gestite dallo stesso router 16
processi allocati su un generico host di Internet 255
Lezione 8: TCP Sockets e Multicast 41Andrea Corradini
TIME TO LIVE: API JAVA
• Assegna il valore di default = 1 al TTL ( i pacchetti di multicast non possono lasciare la rete locale)
• Per modificare il valore di default: posso associare il ttl al multicast socket
MulticastSocket s = new MulticastSocket( );
s.setTimeToLive(16);
Lezione 8: TCP Sockets e Multicast 42Andrea Corradini
MULTICAST: ESERCIZIO
Definire un Server TimeServer, che invia su un gruppo di multicast
dategroup, ad intervalli regolari,la data e l’ora. L’attesa tra un invio ed il
successivo può essere simulata mediante il metodo sleep( ). L’indirizzo IP
di dategroup viene introdotta linea di comando.
Definire quindi un client TimeClient che si unisce a dategroup e riceve, per
dieci volte consecutive, data ed ora, le visualizza, quindi termina.
Lezione 9: RMI - Remote Method Invocation 1Andrea Corradini
Lezione n.9LPR-B-09
RMI: Remote Method Invocation 1/12/2009
Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
Lezione 9: RMI - Remote Method Invocation 2Andrea Corradini
Da UDP/TCP a RPC/RMI
• I protocolli visti (UDP, TCP) permettono a processi su host diversi di scambiarsi dati, più o meno strutturati: sequenze di byte dati primitivi oggetti (con serializzazione)
• In molte applicazioni distribuite il livello di astrazione dei socket, non è adeguato.
• Il paradigma Remote Procedure Call (RPC) permette di astrarre da questi concetti: un programma può eseguire del codice su un host remoto con una normale chiamata di procedura.
• Nel contesto Object Oriented una “procedura” corrisponde a un metodo; in Java si parla quindi di Remote Method Invocation (RMI).
Lezione 9: RMI - Remote Method Invocation 3Andrea Corradini
RPC/RMI: PARADIGMA DI INTERAZIONE A DOMANDA/RISPOSTA
Paradigma di interazione basato su richiesta/risposta• il client invia ad un server un messaggio di richiesta (invocazione di
procedura/metodo)• il server risponde con un messaggio di risposta (risultato)• il client rimane bloccato finchè non riceve la risposta dal server
Client Server
Bloccato In elaborazione
richiesta
risposta
Lezione 9: RMI - Remote Method Invocation 4Andrea Corradini
REMOTE PROCEDURE CALL: ESEMPIO
PROCESSO CLIENT PROCESSO SERVER
print (msg)
proceduraremota print(…)
codice
bloccato
Esempio: richiesta stampa di messaggio e restituzione esito operazione
Lezione 9: RMI - Remote Method Invocation 5Andrea Corradini
Esempi di Remote Procedure Call
Possibili esempi in cui RPC è utile:
• Esecuzione su un server molto potente di codice molto complesso
• Esecuzione su host remoto di interrogazioni su database, compreso
elaborazione parziale dei risultati (es: media salari impiegati, ...)
• In generale, distribuzione del carico computazionale di un programma
CPU-intensive tra più host
• Multiplayer games con stato centralizzato su di un server
Lezione 9: RMI - Remote Method Invocation 6Andrea Corradini
RPC: schema generale di implementazione
• Il server implementa delle procedure (metodi) e li offre come procedure remote tramite un'interfaccia
• Per invocare una procedura remota del server, il client si procura un handle (“maniglia”), una specie di rappresentazione locale della procedura
• L'invocazione comprende: – marshalling dei parametri; – spedizione al server; – unmarshalling; – invocazione della procedura.
• Il ritorno comprende: – marshalling del risultato; – spedizione al client; – unmarshalling; – consegna del valore di ritorno.
Lezione 9: RMI - Remote Method Invocation 7Andrea Corradini
RPC tra piattaforme eterogenee• Se client e server sono scritti in linguaggi arbitrari, eventualmente
diversi e su piattaforme diverse, per realizzare RPC bisogna: fissare un formato per lo scambio di dati, per esempio XDR
(eXternal Data Representation) fornire traduzione tra formato nativo e formato di scambio
• Strumenti per il supporto di RPC: IDL (Interface Description Language) Compilatore
IDL -> Stub (lato client)– procedura che sostituisce quella del server
implementando marshalling e unmarshalling IDL -> Skeleton (lato server)
– routine che realizza unmarshalling dei paramentri, invocazione della procedura, marshalling del risultato
Lezione 9: RMI - Remote Method Invocation 8Andrea Corradini
USER VIEW
Si crea un handle della procedura:ProcedureHandle = lookup(registro, “nome”);
Successivamente si invoca la procedura:result = ProcedureHandle(param1, param2, ...);
Queste semplici operazioni sostituiscono: apertura di connessione con host remoto spedizione dei parametri ricezione del risultato e sua memorizzazione ... oltre all'implementazione del server remoto...
Lezione 9: RMI - Remote Method Invocation 9Andrea Corradini
RPC NEL PARADIGMA ORIENTATO AD OGGETTI
Implementazioni RPC • Open Network Computing Remote Procedure Call (Sun)• Open Group Distributed Computing Environment (DCE)• …
• Nel contesto della Programmazione Orientata ad Oggetti, naturale evoluzione di RPC:
procedure remote => oggetti remoti chiamata di procedura => invocazione di metodo
• CORBA (Common Object Request Broker Architecture)) è un'architettura che supporta RPC in contesto OO tra sistemi eterogenei (es: Java e C++)
• JAVA RMI: API Java per la programmazione distribuita ad oggetti. Sfrutta il fatto che client e server sono entrambi in Java.
Lezione 9: RMI - Remote Method Invocation 10Andrea Corradini
OGGETTI REMOTI: ARCHITETTURA GENERALE
Object Registry
Client ServerOggetto remoto
Esportazione (registrazione)
Server Proxy
Riferimento all’oggetto remoto
Invocazione metodi
Interazione logicaInterazione fisica
Client Proxy
Network Support Network Support
Lezione 9: RMI - Remote Method Invocation 11Andrea Corradini
RMI: ARCHITETTURA LATO SERVER
Il server che definisce l’oggetto remoto:
• definisce un'interfaccia remota che contiene i metodi remoti che possono essere invocati da parte di processi in esecuzione su hosts remoti
• crea un oggetto che implementa l'interfaccia remota, lo esporta per farlo diventare un “RMI server”, e lo pubblica in un registry (registro) associandolo a un nome simbolico
• i metodi remoti invocati da client diversi sono eseguiti concorrentemente, se non sono sincronizzati.
• Importante la separazione tra interfaccia pubblica e implementazione (privata)
Lezione 9: RMI - Remote Method Invocation 12Andrea Corradini
RMI: ARCHITETTURA LATO CLIENT
• Quando il client vuole accedere all’oggetto remoto ricerca un riferimento all’oggetto remoto mediante i servizi offerti dal registry operazione di lookup il cliente deve conoscere
host su cui è eseguito il registry nome simbolico pubblicato nel registry per l'oggetto
il risultato del lookup è un oggetto (handle) il cui tipo è l'interfaccia remota implementata dall'oggetto remoto
sullo handle ottenuto il cliente invoca i metodi definiti dall’oggetto remoto (remote method invocation).
Lezione 9: RMI - Remote Method Invocation 13Andrea Corradini
RMI: JAVA API lato Server
• Interfaccia remota: extends java.rmi.Remote (marker interface)
• Nell'implementazione dell'oggetto remoto i metodi invocabili da remoto devono prevedere il lancio di
java.rmi.RemoteException
• Per esportare l’oggetto remoto e farlo diventare “sever RMI”, due tecniche: extends UnicastRemoteObject nella dichiarazione della classe
che definisce l'oggetto remoto oppure prima della pubblicazione si usa il metodo statico
UnicastRemoteObject.exportObject(Object); bisogna prima creare la classe dello stub usando
> rmic ClasseOggettoRemoto
Lezione 9: RMI - Remote Method Invocation 14Andrea Corradini
RMI: JAVA API lato Server II
• Pubblicazione dell'oggetto: crea un binding (associazione)
nome simbolico oggetto/ riferimento all’oggetto lo pubblica mediante un servizio di tipo registry (registro)
(simile ad un DNS per oggetti distribuiti)
• Attivazione del RMI registry (porta default 1099): rmiregistry & (Linux) start rmiregistry (Windows)
Lezione 9: RMI - Remote Method Invocation 15Andrea Corradini
RMI: JAVA API lato Server II
• Accesso al registro usando la classe java.rmi.Naming void Naming.bind(String name, Remote obj) void Naming.rebind(String name, Remote obj) Remote Naming.lookup(String name)
name ha la forma “//host:port/nome”, con host e port opzionali e default “//localhost:1099/”.
• Accesso al registro usando la classe java.rmi.LocateRegistry Registry LocateRegistry.getRegistry([String host], [int port])
restituisce un riferimento al registro, sul quale posso invocare i metodi bind(), rebind(), lookup(), list(), unbind()
Lezione 9: RMI - Remote Method Invocation 16Andrea Corradini
ESEMPIO DI REMOTE INTERFACE
Esempio: un Host è connesso ad una stazione metereologica che rilevatemperatura, umidità,….mediante diversi strumenti di rilevazione. Sull’host èin esecuzione un server che fornisce queste informazioni agli utentiinteressati. L'interfaccia remota potrebbe essere la seguente:
import java.rmi.*;
public interface weather extends Remote{
public double getTemperature ( ) throws RemoteException;
public double getHumidity ( ) throws RemoteException;
public double getWindSpeed ( ) throws RemoteException;
}
Lezione 9: RMI - Remote Method Invocation 17Andrea Corradini
ESEMPIO RMI: un servizio di ECHO
Definiamo un oggetto remoto che fornisce un servizio di echo,cioè ricevuto un valore come parametro, restituisce al chiamante lostesso valore
• Passo 1. Definire una interfaccia che includa le firme dei metodi che possono essere invocati da remoto
• Passo 2. Definire una classe che implementi l'interfaccia. Questa classe include l'implementazione di tutti i metodi che possono essere invocati da remoto
Lezione 9: RMI - Remote Method Invocation 18Andrea Corradini
Passo1. Definizione dell'interfaccia
import java.rmi.*;
public interface EchoInterface extends Remote {
String getEcho (String Echo) throws RemoteException; }
Passo2. Implementazione dell'interfaccia
public class Server implements EchoInterface {
public Server( ) { } public String getEcho (String echo) throws RemoteException;
{return echo ; } } la classe può definire ulteriori metodi pubblici, ma solamente quelli
definiti nella interfaccia remota possono essere invocati da un altro host
ESEMPIO RMI: un servizio di ECHO
Lezione 9: RMI - Remote Method Invocation 19Andrea Corradini
Passo 3. Creare, esportare e pubblicare un'istanza dell'oggetto remoto
import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
public class ServerActivate{
public static void main(String args[]) {
try { Server obj = new Server( );
EchoInterface stub = (EchoInterface)
UnicastRemoteObject.exportObject(obj);
Registry registry = LocateRegistry.getRegistry ( );
registry.bind ("Echo", stub); System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString()); }}}
ESEMPIO RMI: un servizio di ECHO
Lezione 9: RMI - Remote Method Invocation 20Andrea Corradini
CREAZIONE E PUBBLICAZIONE DELL'OGGETTO REMOTO
Il server
• crea un'istanza dell'oggetto (new)
• invoca il metodo statico UnicastRemoteObject.exportObject(obj) che– esporta l'oggetto remoto obj creato in modo che le invocazioni ai suoi
metodi possano essere ricevute su una porta anonima. – restituisce lo stub dell'oggetto remoto. Il client deve reperire questo
stub per poter invocare i metodi remoti
• NOTA BENE: la classe dello stub deve essere creata prima esplicitamente, mediante il comando rmic (rmi compiler): > rmic Server
• dopo aver eseguito il metodo, sulla porta anonima individuata, un server RMI aspetta invocazioni di metodi remoti su un ServerSocket
• Lo stub generato (da passare al client) contiene indirizzo IP e porta su cui è attivo il server RMI
Lezione 9: RMI - Remote Method Invocation 21Andrea Corradini
CREAZIONE DELLO STUB
• Per invocare i metodi dell'oggetto remoto, il client deve avere a disposizione lo stub dell'oggetto
• Stub = contiene uno scheletro per ogni metodo definito nell'interfaccia (con le solite segnature), ma trasforma l'invocazione di un metodo in una richiesta a un host remoto (a una porta opportuna)
• Il client deve reperire lo stub e utilizzarlo per invocare i metodi remoti
• JAVA mette a disposizione del programmatore un semplice name server (registry) che consente
– Al server di registrare lo stub con un nome simbolico– Al client di reperire lo stub tramite il suo nome simbolico
Lezione 9: RMI - Remote Method Invocation 22Andrea Corradini
JAVA : ESPORTAZIONE DELLO STUB
• Il Server per rendere disponibile lo stub creato agli eventuali clients, inserisce un riferimento allo stub creato in un registry. Normalmente useremo il registry locale, che deve essere attivo sul localhost sulla porta di default 1099 (altrimenti va attivato come visto prima).
Registry registry = LocateRegistry.getRegistry( );
registry.bind ("Echo", stub);• Registry = simile ad un DNS per oggetti remoti, contiene legami tra il
nome simbolico dell’oggetto remoto ed il riferimento all’oggetto
echoriferimento all’oggetto
nome servizio riferimento
Lezione 9: RMI - Remote Method Invocation 23Andrea Corradini
JAVA: IL REGISTRY
• la classe LocateRegistry contiene metodi per la gestione dei registry
• La getRegistry( ) restituisce un riferimento ad un registry allocato sull'host locale e sulla porta di default 1099
• Si può anche specificare il nome di un host e/o una porta per individuare il servizio di registry su uno specifico host e/o porta
public static Registry getRegistry(String host, int port)
throws RemoteException
• nel caso più semplice si utilizza un registry locale, attivato sullo stesso host su cui è in esecuzione il server
• se non ci sono parametri oppure se il nome dell'host è uguale a null, allora l'host di riferimento è quello locale
Lezione 9: RMI - Remote Method Invocation 24Andrea Corradini
JAVA : IL REGISTRY
Supponiamo che registry sia l'istanza di un registro individuato mediantegetregistry( )
• registry.bind ( ) crea un collegamento tra un nome simbolico (qualsiasi) ed un riferimento all’oggetto. Se esiste già un collegamento per lo stesso oggetto all’interno dello stesso registry, viene sollevata una eccezione
• registry.rebind ( ) crea un collegamento tra un nome simbolico (qualsiasi) ed un riferimento all’oggetto. Se esiste già un collegamento per lo stesso oggetto all’interno dello stesso registry, tale collegamento viene sovrascritto
• è possibile inserire più istanze dello stesso oggetto remoto nel registry, con nomi simbolici diversi
Lezione 9: RMI - Remote Method Invocation 25Andrea Corradini
JAVA: ATTIVAZIONE DEL SERVIZIO
Per rendere disponibile i metodi dell’oggetto remoto, è necessario attivaredue tipi di servizi• il server registry che fornisce il servizio di registrazione di oggetti
remoti • Il server implementato fornisce accesso ai metodi remoti
Attivazione del registry in background:$ rmiregistry & (in LINUX)$ start rmiregistry (in WINDOWS)
• viene attivato un registry associato per default alla porta 1099• Se la porta è già utilizzata, viene sollevata un’eccezione. Si può anche
scegliere esplicitamente una porta$ rmiregistry 2048 &
Lezione 9: RMI - Remote Method Invocation 26Andrea Corradini
RMI REGISTRY
• Il registry viene eseguito per default sulla porta 1099
• Se si vuole eseguire il registry su una porta diversa, occorre specificare il numero di porta da linea di comando, al momento dell'attivazione
start rmiregistry 2100
• la stessa porta va indicata sia nel client che nel server al momento del reperimento del riferimento al registro, mediante LocateRegistry.getRegistry
Registry registry = LocateRegistry.getRegistry(2100);
• NOTA BENE: il registry ha bisogno dell'interfaccia e dei .class, per cui attenti acome sono impostati i path!
Lezione 9: RMI - Remote Method Invocation 27Andrea Corradini
IL CLIENT RMI
Il client:• ricerca uno stub per l'oggetto remoto• invoca i metodi dell’oggetto remoto come fossero metodi locali (l'unica
differenza è che occorre intercettare RemoteException)
Per ricercare il riferimento allo stub, il client
• deve accedere al registry attivato sul server.
• il riferimento restituito dal registry è un riferimento ad un oggetto di tipo Object: è necessario effettuare il casting dell’oggetto restituito al tipo definito nell’interfaccia remota
Lezione 9: RMI - Remote Method Invocation 28Andrea Corradini
IL CLIENT RMI
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
public class Client {
private Client( ) { }
public static void main(String[ ] args) throws Exception
{
String host = args[0];
Scanner s = new Scanner(System.in);
String next = s.next();
Lezione 9: RMI - Remote Method Invocation 29Andrea Corradini
IL CLIENT RMI
try {
Registry registry = LocateRegistry.getRegistry(host);
EchoInterface stub = (EchoInterface) registry.lookup("Echo");
String response = stub.getEcho(next);
System.out.println("response: " + response);
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}}}
Lezione 9: RMI - Remote Method Invocation 30Andrea Corradini
ESEMPIO: FIBONACCI
• Il server calcola il numero di Fibonacci per numeri di grandezza arbitraria.
• Rispetto all'esempio Echo, questo esempio usa API più semplici per esportare e pubblicare l'oggetto remoto.
L'Interfaccia Remota
import java.rmi.*;
import java.math.BigInteger;
public interface RemoteFibonacci extends Remote {
public BigInteger getFibonacci(BigInteger n) throws RemoteException;
}
Lezione 9: RMI - Remote Method Invocation 31Andrea Corradini
ESEMPIO: FIBONACCI - L'OGGETTO REMOTO
import java.rmi.*; import java.rmi.server.UnicastRemoteObject;
import java.math.BigInteger;
public class FibonacciImpl extends UnicastRemoteObject implements
RemoteFibonacci {
public FibonacciImpl( ) throws RemoteException {
super( );
}
public BigInteger getFibonacci(BigInteger n) throws RemoteException {
System.out.println("Calculating the " + n + "th Fibonacci number");
//CONTINUA
Lezione 9: RMI - Remote Method Invocation 32Andrea Corradini
ESEMPIO: FIBONACCI - L'OGGETTO REMOTO
// Continua...BigInteger zero = new BigInteger("0");BigInteger one = new BigInteger("1");if (n.equals(zero)) return one;if (n.equals(one)) return one; BigInteger i = one, low = one, high = one; while (i.compareTo(n) == -1) {
BigInteger temp = high;high = high.add(low);low = temp; i = i.add(one);
}
return high; }}
Lezione 9: RMI - Remote Method Invocation 33Andrea Corradini
ESEMPIO: FIBONACCI - PUBBLICAZIONE
import java.net.*; import java.rmi.*;
public class FibonacciServer {
public static void main(String[] args) {try {
FibonacciImpl f = new FibonacciImpl( );Naming.rebind("fibonacci", f);System.out.println("Fibonacci Server ready.");
} catch (RemoteException rex) {System.out.println("Exception in FibonacciImpl.main: " + rex);} catch (MalformedURLException ex) {System.out.println("MalformedURLException " + ex);}
}}
Lezione 9: RMI - Remote Method Invocation 34Andrea Corradini
ESEMPIO: FIBONACCI - IL CLIENT
import java.rmi.*; import java.net.*; import java.math.BigInteger;
public class RemoteFibonacciClient {
public static void main(String args[]) {
if (args.length == 0 || !args[0].startsWith("rmi:")) {System.err.println("Usage: java FibonacciClient " +
" rmi://host.domain:port/fibonacci number"); return; }
try {Object o = Naming.lookup(args[0]);RemoteFibonacci calculator = (RemoteFibonacci) o;for (int i = 1; i < args.length; i++) {
try {BigInteger index = new BigInteger(args[i]);BigInteger f = calculator.getFibonacci(index);System.out.println("The " + args[i] + "th Fibonacci number is " + f); }
Lezione 9: RMI - Remote Method Invocation 35Andrea Corradini
ESEMPIO: FIBONACCI - IL CLIENT
catch (NumberFormatException e) {System.err.println(args[i] + "is not an integer.");
}}
} catch (MalformedURLException ex) {System.err.println(args[0] + " is not a valid RMI URL");
} catch (RemoteException ex) {System.err.println("Remote object threw exception " + ex);
} catch (NotBoundException ex) {System.err.println("Could not find the requested remote object on the server");
}
}}
Lezione 9: RMI - Remote Method Invocation 36Andrea Corradini
ESERCIZIO
Sviluppare una applicazione RMI per la gestione di un’elezione. Il serveresporta un insieme di metodi
• public void vota (String nome). Accetta come parametro il nome del candidato. Non restituisce alcun valore. Registra il voto di un candidato in una struttura dati opportunamente scelta.
• public int risultato (String nome) Accetta come parametro il nome di un candidato e restituisce i voti accumulati da tale candidato fino a quel momento.
• un metodo che consenta di ottenere i nomi di tutti i candidati, con i rispettivi voti, ordinati rispetto ai voti ottenuti
Lezione 10: RMI CallBacks - Miscellanea 1Andrea Corradini
Lezione n.10LPR-B-09
RMI CallBacks 9/12/2009
Andrea Corradini
Università degli Studi di Pisa Dipartimento di Informatica
Lezione 10: RMI CallBacks - Miscellanea 2Andrea Corradini
RMI: PASSAGGIO DI PARAMETRI
Nell'invocazione di un metodo con RMI, i parametri vengono passati nel seguente modo:
parametri di tipo primitivo vengono passati per valore
parametri di tipo riferimento vengono serializzati e il server ne crea una copia– Qundi una modifica fatta dal server non è visibile dal client
parametri di tipo “remoto” vengono passati come riferimenti
Lezione 10: RMI CallBacks - Miscellanea 3Andrea Corradini
Meccanismo RMI prevede: comunicazione unidirezionale (dal client al server) comunicazione sincrona, rendez-vouz esteso: il client invoca un
metodo remoto e si blocca finchè il metodo non termina
Ma in molte applicazioni il client è interessato ad un evento che si verifica sul server e
notifica il suo interesse al server (per esempio utilizzando RMI) il server registra che il client è interessato in quell’evento quando l’evento si verifica, il server notifica ai clients interessati
l’accadimento dell’event
IL MECCANISMO DELLE CALLBACK: MOTIVAZIONI
Lezione 10: RMI CallBacks - Miscellanea 4Andrea Corradini
IL MECCANISMO DELLE CALLBACK: MOTIVAZIONI
Esempi di applicazioni:
Un utente partecipa a un gruppo di discussione (es: Facebook) e vuol essere avvertito quando un nuovo utente entra nel gruppo.
Lo stato di un gioco multiplayer viene gestito da un server. I giocatori notificano al server le modifiche allo stato del gioco. Ogni giocatore deve essere avvertito quando lo stato del gioco subisce delle modifiche.
Gestione distribuita di un’asta: un insieme di utenti partecipa ad un’asta distribuita. Ogni volta che un utente fa una nuova offerta, tutti i partecipanti all’asta devono essere avvertiti.
Lezione 10: RMI CallBacks - Miscellanea 5Andrea Corradini
• Come può essere avvisato il client che un evento si è verificato sul server?
Polling: il client interroga ripetutamente il server, per sapere se si è verificato l’evento atteso. L’interrogazione può avviene tramite mediante RMI Svantaggio: inefficiente, spreco di risorse del sistema
Registrazione dei clients interessati agli eventi e successiva notifica (asincrona) del verificarsi dell’evento al client da parte del server Problema: quale meccanismo può usare il server per avvisare
il client?
IL MECCANISMO DELLE CALLBACK: MOTIVAZIONI
Lezione 10: RMI CallBacks - Miscellanea 6Andrea Corradini
RMI: IL MECCANISMO DELLE CALLBACK
• Il meccanismo delle callback permette di utilizzare RMI sia per l’invocazione client-server (registrazione del client) che per quella server-client (notifica del verificarsi di un evento).
• Come funziona? Il server definisce un'interfaccia remota ServerInterface con un
metodo remoto che serve al client per registrarsi Il client definisce un'interfaccia remota ClientInterface che
definisce un metodo remoto usato dal server per notificare un evento al client
Il client conosce la ServerInterface e ottiene il puntatore all'oggetto remoto tramite il registry
Lezione 10: RMI CallBacks - Miscellanea 7Andrea Corradini
Il client invocando il metodo remoto per registrarsi passa al server un riferimento RC a un oggetto che implementa la ClientInterface
Il server memorizza RC in una sua struttura dati (ad esempio, un Vector)
Quando deve notificare, il server utilizza RC per invocare il metodo remoto di notifica definito dal client.
In questo modo si rende ‘simmetrico’ il meccanismo di RMI, ma… il client non registra l’oggetto remoto in un rmiregistry, ma
passa un riferimento a tale oggetto al server, al momento della registrazione
RMI: IL MECCANISMO DELLE CALLBACK
Lezione 10: RMI CallBacks - Miscellanea 8Andrea Corradini
Da Wikipedia: “In programmazione una callback è una funzione specializzata che viene passata come parametro a un'altra funzione (che invece è generica). Questo permette alla funzione generica di compiere un lavoro specifico attraverso la callback.”
Esempio: la funzione generica quicksort prende come argomento l'array da ordinare e una funzione callback per confrontare gli elementi.
Nel contesto Object Oriented, una callback è un'istanza che fornisce un'implementazione di un metodo specificato in un'interfaccia.
Esempio nelle API Java: Uso di Comparator nei metodi di sorting della classe Arrays
CHE SIGNIFICA “CALLBACK”?
Lezione 10: RMI CallBacks - Miscellanea 9Andrea Corradini
CALLBACKS: UN ESEMPIO
Lato Server: Interfaccia remota che definisce metodi per: (1) Contattare il
server: metodo SayHello( ) (2) Registrare/cancellare una callback:
c
Implementazione dell'interfaccia Esportazione e pubblicazione su registry di oggetto remoto
Lato Client: Intefaccia remota che definisce metodo remoto (callback) Implementazione di interfaccia remota Programma principale che:
Registra una callback presso il server: sarà usata per notificare al client i contatti stabiliti da clients con il metodo SayHello( ).
Effettua un numero casuale di richieste del metodo SayHello( ) Cancella la propria registrazione
Lezione 10: RMI CallBacks - Miscellanea 10Andrea Corradini
L'INTERFACCIA REMOTA DEL SERVER
import java.rmi.*;
public interface CbServerInt extends Remote {
/* metodo di notifica */
public String sayHello(String name) throws RemoteException;
/* registrazione per il callback */
public void registerForCallback(CbClientInt callbackClient)throws RemoteException;
/* cancella registrazione per il callback */
public void unregisterForCallback(CbClientInt callbackClient)throws RemoteException;
}
Lezione 10: RMI CallBacks - Miscellanea 11Andrea Corradini
L'IMPLEMENTAZIONE DEL SERVER
import java.rmi.*; import java.rmi.server.*;import java.util.*;
public class CbServerImpl extends UnicastRemoteObject
implements CbServerInt{
/* lista dei client registrati */
private List<CbClientInt> clients;
/* crea un nuovo servente */
public CbServerImpl( ) throws RemoteException {clients = new ArrayList<CbClientInt>( );
}
/* continua */
Lezione 10: RMI CallBacks - Miscellanea 12Andrea Corradini
L'IMPLEMENTAZIONE DEL SERVER
public synchronized void registerForCallback(CbClientInt callbackClient)
throws RemoteException{ if (!clients.contains(callbackClient)) {
clients.add(callbackClient); }System.out.println(" New client registered." ); }
/* annulla registrazione per il callback */
public synchronized void unregisterForCallback(CbClientInt callbackClient)
throws RemoteException{if (clients.remove(callbackClient)) {
System.out.println("Client unregistered");}else{
System.out.println("Unable to unregister client."); }}
Lezione 10: RMI CallBacks - Miscellanea 13Andrea Corradini
L'IMPLEMENTAZIONE DEL SERVER /* metodo di notifica
* quando viene richiamato, fa il callback a tutti i client registrati */
public String sayHello (String name) throws RemoteException {doCallbacks(name);return "Hello, " + name + "!"; }
private synchronized void doCallbacks(String name) throws RemoteException{ System.out.println("Starting callbacks.");Iterator<CbClientInt> i = clients.iterator( );while (i.hasNext()) {
CbClientInt client = i.next();client.notifyMe(name); }
System.out.println("Callbacks complete."); } }
Lezione 10: RMI CallBacks - Miscellanea 14Andrea Corradini
L'ATTIVAZIONE DEL SERVER
import java.rmi.server.*; import java.rmi.registry.*;
public class CbServer {
public static void main(String[ ] args) {try { /* registrazione presso il registry */
System.out.println("Binding CallbackHello");CbServerImpl server = new CbServerImpl( );String name = "CallbackHelloServer";Registry registry = LocateRegistry.getRegistry ("localhost",2048);registry.rebind (name, server);System.out.println("CallbackHello bound");
} catch (Exception e) { e.printStackTrace();System.exit(-1); } } }
Lezione 10: RMI CallBacks - Miscellanea 15Andrea Corradini
L'INTERFACCIA DEL CLIENT
import java.rmi.*;
public interface CbClientInt extends Remote {
/* Metodo invocato dal server per effettuare
una callback a un client remoto. */
public void notifyMe(String message) throws RemoteException;
}
notifyMe(...)
è il metodo esportato dal client e che viene utilizzato dal server per
la notifica di un nuovo contatto da parte di un qualsiasi client. Viene
notificato il nome del client che ha contattato il server.
Lezione 10: RMI CallBacks - Miscellanea 16Andrea Corradini
L'IMPLEMENTAZIONE DEL CLIENT
import java.rmi.*; import java.rmi.server.*;
public class CbClientImpl extends UnicastRemoteObject
implements CbClientInt {
/* crea un nuovo callback client */
public CbClientImpl( ) throws RemoteException { super( ); }
/* metodo che può essere richiamato dal servente */
public void notifyMe(String message) throws RemoteException {String returnMessage = "Call back received: " + message;System.out.println( returnMessage); } }
Lezione 10: RMI CallBacks - Miscellanea 17Andrea Corradini
ATTIVAZIONE DEL CLIENT
import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*;
public class CbClient {
public static void main(String args[ ]) {
try {System.out.println("Cerco CallbackHelloServer");Registry registry = LocateRegistry.getRegistry("localhost", 2048);String name = "CallbackHelloServer"; /* crea stub di oggetto remoto */CbServerInt h = (CbServerInt) registry.lookup(name);/* si registra per il callback */System.out.println("Registering for callback");CbClientImpl callbackObj = new CbClientImpl( );h.registerForCallback(callbackObj);
Lezione 10: RMI CallBacks - Miscellanea 18Andrea Corradini
ATTIVAZIONE DEL CLIENT
/* accesso al server - fa una serie casuale di 5-15 richieste */int n = (int) (Math.random( ) * 10 + 5);String nickname= "mynick";for (int i=0; i< n; i++) {
String message = h.sayHello(nickname);System.out.println( message);Thread.sleep(1500); }
/* cancella la registrazione per il callback */System.out.println("Unregistering for callback");h.unregisterForCallback(callbackObj);
} catch (Exception e) {
System.err.println("HelloClient exception: " +e.getMessage( ));
e.printStackTrace(); System.exit(-1); }}}
Lezione 10: RMI CallBacks - Miscellanea 19Andrea Corradini
RMI:ECCEZIONI
Eccezione che viene sollevata se non trova un servizio di registry su quella porta. Esempio:
HelloClient exception: Connection refused to host: 192.168.2.103; nested exception is: java.net.ConnectException: Connection refused: connect
Eccezione sollevata se si tenta di registrare più volte lo stesso stub con lo stesso nome nello stesso registry
Esempio
CallbackHelloServer exception: java.rmi.AlreadyBoundException: CallbackHelloServer java.rmi.AlreadyBoundException: CallbackHelloServer
Lezione 10: RMI CallBacks - Miscellanea 20Andrea Corradini
ESERCIZIO 1: ELEZIONI CON CALLBACK
La scorsa lezione precedente è stato assegnato un esercizio per la
gestione elettronica di una elezione a cui partecipano un numero
prefissato di candidati. Si chiedeva di realizzare un server RMI che
consentisse al client di votare un candidato e di richiedere il numero di
voti ottenuti dai candidati fino ad un certo punto.
Modificare l’esercizio in modo che il server notifichi ogni nuovo voto
ricevuto a tutti i clients che hanno votato fino a quel momento. La
registrazione dei clients sul server avviene nel momento del voto.
Lezione 10: RMI CallBacks - Miscellanea 21Andrea Corradini
ESERCIZIO 2: GESTIONE FORUM Si vuole implementare un sistema che implementi un servizio per la gestione di forum in rete. Un forum è caratterizzato da un argomento su cui diversi utenti, iscritti al forum, possono scambiarsi opinioni via rete.
Il sistema deve prevedere un server RMI che fornisca le seguenti funzionalità:
a) apertura di un nuovo forum, di cui è specificato l'argomento (esempio: giardinaggio)
b) registrazione ad un forum, di cui è specificato l'argomento
c) inserimento di un nuovo messaggio indirizzato ad un forum identificato dall'argomento (es: è tempo di piantare le viole, indirizzato al forum giardinaggio); il messaggio deve essere inviato agli utenti iscritti al forum
d) reperimento dell'ultimo messaggio inviato ad un forum di cui è specificato l'argomento.
Quindi il messaggio può essere richiesto esplicitamente dal client oppure può essere notificato ad un client precedentemente registrato.