Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.1
LPR-B-09
JAVA: Tasks e Threads
22/9/2009
Andrea Corradini
(basato su materiale di Laura Ricci e Marco Danelutto)
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 1
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 2
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 3
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 4
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 5
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...
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 6
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()
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 7
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 8
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( ); } }
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 9
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!!!")+"),"; }
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 10
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 11
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 12
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!!!),
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 13
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!!!),
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 14
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 15
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 16
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 17
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 18
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"); }}
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 19
GERARCHIA DELLE CLASSI
• Thread estende Object e implementa l'interfaccia Runnable
• DecolloThread estende Thread e sovrascrive il metodo run() di
Thread
Object Runnable
Thread
implements
DecolloThread
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 20
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 21
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 22
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 23
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");}}
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 24
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...");
}}
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 25
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 26
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 27
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
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 28
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)
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 29
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.
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 30
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(); }}
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 31
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()); } }
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 32
THREAD PRIORITY: Un possibile output
thread B ha priorita' 2
thread D ha priorita' 7
thread C ha priorita' 7
thread A ha priorita' 8
thread B ha priorita' 2
thread D ha priorita' 7
thread A ha priorita' 8
thread C ha priorita' 7
main ha priorita' 5
thread B ha priorita' 2
thread D ha priorita' 7
thread A ha priorita' 3
thread C ha priorita' 7
thread B ha priorita' 2
thread D ha priorita' 7
thread A ha priorita' 3
thread C ha priorita' 7
U
Lezione 1: JAVA Tasks e Threads Andrea Corradini 33
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.2
LPR-B-09
Thread Pooling e Callable
29/9-6/10/2009
Andrea Corradini
U
Lezione 2: JAVA pooling Andrea Corradini 1
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.
U
Lezione 2: JAVA pooling Andrea Corradini 2
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");}}
U
Lezione 2: JAVA pooling Andrea Corradini 3
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"); }}
U
Lezione 2: JAVA pooling Andrea Corradini 4
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
U
Lezione 2: JAVA pooling Andrea Corradini 5
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.
U
Lezione 2: JAVA pooling Andrea Corradini 6
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...)
U
Lezione 2: JAVA pooling Andrea Corradini 7
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.
U
Lezione 2: JAVA pooling Andrea Corradini 8
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
U
Lezione 2: JAVA pooling Andrea Corradini 9
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
U
Lezione 2: JAVA pooling Andrea Corradini 10
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( )
U
Lezione 2: JAVA pooling Andrea Corradini 11
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){ }
}
}}
U
Lezione 2: JAVA pooling Andrea Corradini 12
THREAD POOLING: ESEMPIO 1
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.
U
Lezione 2: JAVA pooling Andrea Corradini 13
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!!!),
U
Lezione 2: JAVA pooling Andrea Corradini 14
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
U
Lezione 2: JAVA pooling Andrea Corradini 15
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!!!),
U
Lezione 2: JAVA pooling Andrea Corradini 16
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.
U
Lezione 2: JAVA pooling Andrea Corradini 17
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!!!),
U
Lezione 2: JAVA pooling Andrea Corradini 18
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
U
Lezione 2: JAVA pooling Andrea Corradini 19
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
U
Lezione 2: JAVA pooling Andrea Corradini 20
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
U
Lezione 2: JAVA pooling Andrea Corradini 21
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ì k
threads, corePoolSize ≤ k ≤ maxPoolSize
U
Lezione 2: JAVA pooling Andrea Corradini 22
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
U
Lezione 2: JAVA pooling Andrea Corradini 23
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)
U
Lezione 2: JAVA pooling Andrea Corradini 24
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
U
Lezione 2: JAVA pooling Andrea Corradini 25
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()
U
Lezione 2: JAVA pooling Andrea Corradini 26
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);
}
U
Lezione 2: JAVA pooling Andrea Corradini 27
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();
}
}
}
U
Lezione 2: JAVA pooling Andrea Corradini 28
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>( ) );
U
Lezione 2: JAVA pooling Andrea Corradini 29
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();
}
}
U
Lezione 2: JAVA pooling Andrea Corradini 30
GESTIONE DINAMICA: ESEMPI
Parametri: tasks= 8, core = 3, MaxPoolSize= 4, SynchronousQueue, timeout=50000msec
dimensione del pool 1
Starting task Task0 eseguito da pool-1-thread-1
Risultato 0 da task Task0 eseguito dapool-1-thread-1
dimensione del pool 2
dimensione del pool 3
dimensione del pool 3
Starting task Task3 eseguito da pool-1-thread-1
Starting task Task1 eseguito da pool-1-thread-2
Starting task Task2 eseguito da pool-1-thread-3
dimensione del pool 4
Risultato 1 da task Task1 eseguito dapool-1-thread-2
Starting task Task4 eseguito da pool-1-thread-4
RisultatO 2 da task Task3 eseguito dapool-1-thread-1
Risultato 1 da task Task2 eseguito dapool-1-thread-3
java.util.concurrent.RejectedExecutionException
Risultato 3 da task Task4 eseguito dapool-1-thread-4
U
Lezione 2: JAVA pooling Andrea Corradini 31
GESTIONE DINAMICA: ESEMPI
Tutti I threads attivati inizialmente mediante tpe.prestartAllCoreThreads( );
Parametri: tasks= 8, core = 3, MaxPoolSize= 4, SynchronousQueue
dimensione del pool 3
Starting task Task0 eseguito da pool-1-thread-3
dimensione del pool3
Risultato 0 da task Task0 eseguito da pool-1-thread-3
dimensione del pool 3
Starting task Task2 eseguito da pool-1-thread-1
Starting task Task1 eseguito da pool-1-thread-2
Risultato 1 da task Task2 eseguito dapool-1-thread-1
Risultato 1 da task Task1 eseguito dapool-1-thread-2
Starting task Task3 eseguito da pool-1-thread-3
dimensione del pool 3
Risultato 2 da task Task3 eseguito dapool-1-thread-3
dimensione del pool 3
U
Lezione 2: JAVA pooling Andrea Corradini 32
GESTIONE DINAMICA: ESEMPI
(CONTINUA)
Starting task Task4 eseguito da pool-1-thread-2
dimensione del pool 3
Starting task Task5 eseguito da pool-1-thread-3
Risultato 3 da task Task4 eseguito dapool-1-thread-2
dimensione del pool 3
Starting task Task6 eseguito da pool-1-thread-1
Risultato 5 da task Task5 eseguito dapool-1-thread-3
dimensione del pool 3
Starting task Task7 eseguito da pool-1-thread-2
Risultato 8 da task Task6 eseguito dapool-1-thread-1
Risultato 13 da task Task7 eseguito dapool-1-thread-2
U
Lezione 2: JAVA pooling Andrea Corradini 33
GESTIONE DINAMICA: ESEMPI
Parametri: tasks= 10, core = 3, MaxPoolSize= 4, SynchronousQueue,
timeout=0msec
dimensione del pool 1
Starting task Task0 eseguito da pool-1-thread-1
Risultato 0 da task Task0 eseguito dapool-1-thread-1
dimensione del pool 2
Starting task Task1 eseguito da pool-1-thread-2
Risultato 1 da task Task1 eseguito dapool-1-thread-2
dimensione del pool 3
dimensione del pool 3
Starting task Task2 eseguito da pool-1-thread-3
Starting task Task3 eseguito da pool-1-thread-2
dimensione del pool 3
Starting task Task4 eseguito da pool-1-thread-1
Risultato 1 da task Task2 eseguito dapool-1-thread-3
Risultato 2 da task Task3 eseguito dapool-1-thread-2
dimensione del pool 4
U
Lezione 2: JAVA pooling Andrea Corradini 34
GESTIONE DINAMICA:ESEMPI
(CONTINUA)
Risultato 3 da task Task4 eseguito dapool-1-thread-1
Starting task Task5 eseguito da pool-1-thread-4
dimensione del pool 3
Starting task Task6 eseguito da pool-1-thread-2
Risultato 5 da task Task5 eseguito dapool-1-thread-4
Starting task Task7 eseguito da pool-1-thread-1
dimensione del pool 3
Risultato 8 da task Task6 eseguito dapool-1-thread-2
dimensione del pool 3
Starting task Task8 eseguito da pool-1-thread-4
dimensione del pool 3
Starting task Task9 eseguito da pool-1-thread-2
Risultato 13 da task Task7 eseguito dapool-1-thread-1
Risultato 21 da task Task8 eseguito dapool-1-thread-4
Risultato 34 da task Task9 eseguito dapool-1-thread-2
U
Lezione 2: JAVA pooling Andrea Corradini 35
GESTIONE DINAMICA: ESEMPI
Parametri: tasks= 10, core = 3, MaxPoolSize= 4,LinkedBlockingQueue
dimensione del pool 1
Starting task Task0 eseguito da pool-1-thread-1
Risultato 0 da task Task0 eseguito dapool-1-thread-1
dimensione del pool 2
dimensione del pool 3
Starting task Task1 eseguito da pool-1-thread-2
Risultato 1 da task Task1 eseguito dapool-1-thread-2
Starting task Task3 eseguito da pool-1-thread-2
dimensione del pool 3
Risultato 2 da task Task3 eseguito dapool-1-thread-2
dimensione del pool 3
Starting task Task2 eseguito da pool-1-thread-3
Starting task Task4 eseguito da pool-1-thread-1
Starting task Task5 eseguito da pool-1-thread-2
dimensione del pool 3
U
Lezione 2: JAVA pooling Andrea Corradini 36
GESTIONE DINAMICA:ESEMPI
(CONTINUA)
Risultato 1 da task Task2 eseguito dapool-1-thread-3
Risultato 3 da task Task4 eseguito dapool-1-thread-1
Risultato 5 da task Task5 eseguito dapool-1-thread-2
dimensione del pool 3
Starting task Task6 eseguito da pool-1-thread-3
dimensione del pool 3
Starting task Task7 eseguito da pool-1-thread-1
Risultato 8 da task Task6 eseguito dapool-1-thread-3
dimensione del pool 3
Starting task Task8 eseguito da pool-1-thread-2
Risultato 13 da task Task7 eseguito dapool-1-thread-1
dimensione del pool 3
Starting task Task9 eseguito da pool-1-thread-3
Risultato 21 da task Task8 eseguito dapool-1-thread-2
Risultato 34 da task Task9 eseguito dapool-1-thread-3
U
Lezione 2: JAVA pooling Andrea Corradini 37
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
U
Lezione 2: JAVA pooling Andrea Corradini 38
TERMINAZIONE DI EXECUTORS
Alcuni metodi definiti dalla interfaccia ExecutorService
• void shutdown( )
• List<Runnable> shutdownNow( )
• boolean isShutdown( )
• boolean isTerminated( )
• boolean awaitTermination(long timeout, TimeUnit unit)
U
Lezione 2: JAVA pooling Andrea Corradini 39
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.
U
Lezione 2: JAVA pooling Andrea Corradini 40
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.
U
Lezione 2: JAVA pooling Andrea Corradini 41
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
U
Lezione 2: JAVA pooling Andrea Corradini 42
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;
}}}
U
Lezione 2: JAVA pooling Andrea Corradini 43
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
U
Lezione 2: JAVA pooling Andrea Corradini 44
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
U
Lezione 2: JAVA pooling Andrea Corradini 45
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();
U
Lezione 2: JAVA pooling Andrea Corradini 46
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){ } } }
U
Lezione 2: JAVA pooling Andrea Corradini 47
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
U
Lezione 2: JAVA pooling Andrea Corradini 48
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(...........){ }..............}}
U
Lezione 2: JAVA pooling Andrea Corradini 49
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.3
LPR-B-09
Threads: Sincronizzazione e
Mutua Esclusione
6/10/2009
Andrea Corradini
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 1
SULLA TERMINAZIONE: THREAD DEMONI
• Thread Demone (Daemon): fornisce un servizio, generalmente in
background, 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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 2
TERMINAZIONE: THREAD DEMONI
public 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);
} }
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 3
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'.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 4
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 5
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(); }
}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 6
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 EvenValue
assegna 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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 7
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 8
ACCESSO A RISORSE CONDIVISE
import 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( ); }}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 9
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]ok2
Thread[pool-1-thread-2,5,main]ok4
Thread[pool-1-thread-1,5,main]errore7
Thread[pool-1-thread-2,5,main]errore9
Thread[pool-1-thread-1,5,main]ok10
Thread[pool-1-thread-2,5,main]errore13
Thread[pool-1-thread-1,5,main]ok14
Thread[pool-1-thread-1,5,main]errore17
Thread[pool-1-thread-2,5,main]ok18
Thread[pool-1-thread-2,5,main]ok20
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 10
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 11
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 12
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 13
Esercizio 1
Si 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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 14
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 15
RACE CONDITIONS: ESEMPI
public 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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 16
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 17
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 lock
sull'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)
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 18
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 19
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( ) } } }
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 20
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( );}}}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 21
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 22
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 23
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 24
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 25
I METODI SYNCHRONIZED
L'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);}}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 26
I METODI SYNCHRONIZED
import 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) { }; }}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 27
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 è sincronizzato
Thread[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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 28
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()
•
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 29
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 30
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 31
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)
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 32
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 la
mutua esclusione sull’oggetto
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 33
ESERCIZIO 3: ANCORA RACE CONDITIONS
Simulare 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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 34
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 35
THREADS COOPERANTI: I MONITOR
• 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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 36
THREADS COOPERANTI: I MONITOR
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).
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 37
THREADS COOPERANTI: I MONITOR
MONITOR
CAMPI DELL'OGGETTO
Coda dei threads in
. Metodo sincronizzato attesa della lock
Metodo non sincronizzato
. Coda dei threads in
. .
. . attesa del verificarsi di
. una condizione (con wait())
.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 38
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 39
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 40
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 41
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
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 42
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 inserito
return result; }
public synchronized void put(int n) throws InterruptedException{
while (size == buffer.length) wait();
buffer[size++] = n;
notify(); } }
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 43
IL PRODUTTORE
public 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");
}}}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 44
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");
}}}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 45
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();
}}
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 46
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
• Si noti che poiché il buffer ha una
[Consumer] Got 8502 politica Last In First Out (LIFO),
[Producer] Put 4162
[Producer] Put 954 l'ordine non viene preservato. Per
[Producer] Put 880
[Consumer] Got 880 esempio, il numero 1515 non viene mai
[Producer] Put 8503 estratto dal buffer.
[Producer] Put 4980
[Consumer] Got 4980
[Consumer] Got 8503
[Producer] Put 3013
[Consumer] Got 3013
[Consumer] Interrupted
[Producer] Interrupted
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 47
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 48
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)
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 49
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.
U
Lezione 3: Threads: Sincronizzazione e Mutua Esclusione Andrea Corradini 50
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.4
LPR-A-09
Indirizzi IP e URL
13/10/2009
Vincenzo Gervasi
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 1
PROGRAMMAZIONE DI RETE: INTRODUZIONE
Programmazione 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 Vincenzo Gervasi 2
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 Vincenzo Gervasi 3
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 Vincenzo Gervasi 4
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 Vincenzo Gervasi 5
INDIRIZZI IP
Un indirizzo IPV4
10010001 00001010 00100010 00000011
145. 10. 34. 3
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:39a1
2000:fdb8::1:00ab:853c:39a1
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 6
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 Vincenzo Gervasi 7
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 Vincenzo Gervasi 8
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 Vincenzo Gervasi 9
JAVA: LA CLASSE INETADDRESS
public 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
public byte [ ] getAddress ( )
public String getHostAddress ( )
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 10
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 Vincenzo Gervasi 11
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 Vincenzo Gervasi 12
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 Vincenzo Gervasi 13
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 Vincenzo Gervasi 14
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 Vincenzo Gervasi 15
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 Vincenzo Gervasi 16
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 Vincenzo Gervasi 17
Esercizio 2
• 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.
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 18
Esercizio 3
• 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.
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 19
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.com
http: identificativo del protocollo.
java.sun.com: nome della risorsa.
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 20
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 Vincenzo Gervasi 21
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 Vincenzo Gervasi 22
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 Vincenzo Gervasi 23
Parsare un URL
• La classe URL offre metodi per accedere ai componenti di una URL
import 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 Vincenzo Gervasi 24
Parsare un URL
• Eseguendo l'esempio precedente si ottiene:
protocol = http
authority = www.cli.di.unipi.it:80
host = www.cli.di.unipi.it
port = 80
path = /faq
query = null
filename = /faq
ref = null
ULezione 4: Indirizzi IP e URL Vincenzo Gervasi 25
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 Vincenzo Gervasi 26
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.*;
args[0]
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 Vincenzo Gervasi 27
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 Vincenzo Gervasi 28
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 Vincenzo Gervasi 29
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 4: Indirizzi IP e URL Vincenzo Gervasi 30
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.6
LPR-B-09
Il protocollo UDP:
Socket e Datagram
27/10/2009
Andrea Corradini
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 1
MECCANISMI DI COMUNICAZIONE
TRA PROCESSI
Meccanismi di comunicazione tra processi (IPC)
Processo 1 Processo 2
(Mittente) dato (Destinatario)
HOST 2
HOST 1
Processo 1
dato . (Destinatario)
. HOST 1
Processo 1
(Mittente) dato .
HOST 1
Processo n
(Destinatario)
HOST n
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 2
COMUNICAZIONE
CONNECTION 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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 3
COMUNICAZIONE
CONNECTION ORIENTED VS. CONNECTIONLESS
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 4
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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 5
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)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 6
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).
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 7
IPC: MECCANISMI BASE
Comunicazione sincrona (o bloccante): il processo che esegue la send o la
receive 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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 8
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)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 9
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 10
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 11
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 12
JAVA IPC: I SOCKETS
Socket Application Program Interface = Definisce un insieme di meccanismi
che 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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 13
FORMATO DEL PACCHETTO IP
Lungh.
IP Version Header TOS Lungh. Datagram 0
frammentazione Identific. Flag Offset 32
TTL Protocollo Checksum 64
Indirizzo Mittente 96
Indirizzo Destinatario 128
Opzioni 160
160/
Dati 192+
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 14
LIVELLO IP: FORMATO DEL PACCHETTO
IP 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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 15
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 16
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
0 Porta sorgente (0-65535) Porta Destinazione(0-65535)
32 Lunghezza Dati Checksum
64
DATI
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 17
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 18
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!)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 19
TRASMISSIONE PACCHETTI UDP
Per 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 SM
Ogni 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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 20
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 21
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 socket
allocato da un processo destinatario
socket
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 22
JAVA : SOCKETS UDP
public class DatagramSocket
Costruttori:
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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 23
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)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 24
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);}
} }
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 25
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 26
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)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 27
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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 28
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
offset 4 length 6
0 1 2 3 4 5 6 7 8 9 10 ...
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 29
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 30
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
Server Client
receive
richiesta in esecuzione
send
bloccato
risposta
send
receive
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 31
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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 32
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)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 33
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 34
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
Write ….. ….. Read
• 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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 35
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 36
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,…) .
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 37
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,…) .
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 38
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)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 39
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 rete
Nessun problema se i dati vengono scambiati tra programmi JAVA.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 40
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
DataOutputStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(“data.txt”)))
applicazione
interi
DataOutputStream bytes WRAPPER
BufferedOutputStream
buffered bytes
FileOutputStream
file (data.txt)
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 41
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()
puntatore all’ultimo
elemento inserito
BUFFER
ByteArrayOutputStream
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 42
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 43
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 baos
possono essere copiati in un array di bytes, di dimensione uguale alla
dimensione attuale di B
byte [ ] barr = baos. toByteArray( )
rete
dati
DataOutputStream ByteArrayOutputStream ByteArray Pacchetto
Buffer
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 44
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:
Dati
pacchetto Byte ByteArrayInputStream DataInputStream
array
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 45
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!
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 46
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);
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 47
INVIO DI UDP DATAGRAMS
for (int i=0; i< 10; i++){
dout.writeInt(i); // scrivo 4 bytes nello stream
data = bout.toByteArray(); // estraggo l'array di byte
dp.setData(data,0,data.length); // lo inserisco nel DatagramPacket
dp.setLength(data.length); // definisco la lunghezza del buffer
ds.send(dp); // invio il DatagramPacket sul socket
bout.reset( ); // svuoto lo stream
dout.writeUTF("***"); // scrivo una stringa nello stream
data = bout.toByteArray( ); // ...
dp.setData (data,0,data.length);
dp.setLength (data.length);
ds.send (dp);
bout.reset( ); } } }
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 48
RICEZIONE DI UDP DATAGRAMS
Ipotesi semplificativa: non consideriamo perdita/riordinamento di pacchetti
import java.io.*;
import java.net.*;
public class MultiDataStreamReceiver{
public static void main(String args[ ]) throws Exception{
// fase di inizializzazione
FileOutputStream 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);
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 49
RICEZIONE DI UDP DATAGRAMS
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 50
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 51
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
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 52
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?
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 53
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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 54
ESERCIZIO 2: COUNT DOWN SERVER
Si richiede di programmare un server CountDownServer che fornisce
un semplice servizio: ricevuto da un client un valore intero n, il server
spedisce 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.
U Lezione 6: Il Protocollo UDP, sockets e datagrams Andrea Corradini 55
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.7
LPR-B-08
UDP: Costruzione di
pacchetti, e esercizi avanzati
10/11/2009
Andrea Corradini
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi Andrea Corradini 1
SOMMARIO
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
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi Andrea Corradini 2
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 Andrea Corradini 3
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 Andrea Corradini 4
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 Andrea Corradini 5
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 Andrea Corradini 6
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 Andrea Corradini 7
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 Andrea Corradini 8
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 Andrea Corradini 9
CODIFICARE VALORI DI TIPO INTERO
public 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 Andrea Corradini 1 0
DECODIFICARE VALORI DI TIPO INTERO
public 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 Andrea Corradini 1 1
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 Andrea Corradini 1 2
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 Andrea Corradini 1 3
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 Andrea Corradini 1 4
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 Andrea Corradini 1 5
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 Andrea Corradini 1 6
TRASMISSIONE DI OGGETTI,SCHEMATICAMENTE
toByteArray()
ObjectOutputStream(ByteArrayOutputStream)
DatagramPacket
{oggetti}
byte[ ]
writeObject( )
send( )
DatagramSocket UDP/IP on Internet
DatagramSocket
receive( )
{oggetti}
DatagramPacket byte[ ]
readObject( )
ObjectInputStream(ByteArrayInputStream)
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi Andrea Corradini 1 7
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 object
Vector<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 stream
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(bout);
oout.writeObject(vett);
byte[] arr = bout.toByteArray();
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi Andrea Corradini 1 8
UN ESEMPIO DI TRASMISSIONE DI OGGETTI
// continua la classe UDP_SendObject
// prepare Datagram socket and packet
DatagramSocket ds = new DatagramSocket();
InetAddress ia = InetAddress.getByName("localhost");
int port = 24309;
DatagramPacket dp = new DatagramPacket(arr,arr.length,ia,port);
// send
ds.send(dp);
}
}
Lezione 7: Il Protocollo UDP: costruzione pacchetti ed esercizi Andrea Corradini 1 9
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 packet
ByteArrayInputStream 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 Andrea Corradini 2 0
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 Andrea Corradini 2 1
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 0
ObjectOutputStream out= new ObjectOutputStream(bout);
System.out.println (bout.size( )); // Stampa 4: l'header è creato
out.writeObject("prova");
System.out.println (bout.size( )); //Stampa 12
// Spedisco il contenuto
bout.reset ( ); //Necessario per eliminare bytes già spediti
out.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 Andrea Corradini 2 2
ESERCIZIO 1: UNA CLASSE AUSILIARIA PER
TRASMETTERE 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 Andrea Corradini 2 3
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 Andrea Corradini 2 4
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 Andrea Corradini 2 5
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 Andrea Corradini 2 6
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 Andrea Corradini 2 7
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 7: Il Protocollo UDP: costruzione pacchetti ed esercizi Andrea Corradini 2 8
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.7
LPR-B-09
TCP: Stream Sockets
17/11/2009
Andrea Corradini
Lezione 6: TCP: Stream Sockets Andrea Corradini 1
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 Andrea Corradini 2
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 Andrea Corradini 3
COUNT DOWN SERVER UDP
CountDownClient CountDownServer
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
Lezione 6: TCP: Stream Sockets Andrea Corradini 4
COUNT DOWN SERVER UDP
CountDownClient CountDownServer
n
sock1
n,n-1,n-2,...
.
sock2
●
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
Lezione 6: TCP: Stream Sockets Andrea Corradini 5
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 Andrea Corradini 6
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 Andrea Corradini 7
IL PROTOCOLLO TCP: STREAM SOCKETS
Client1
SIP = Indirizzo IP del Server Active Socket
PL=Porta Locale
Listening
Socket PR = Porta Remota
Client2
Active Socket
PL=Porta Locale
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 Andrea Corradini 8
IL PROTOCOLLO TCP: STREAM SOCKETS
Client1
SIP = Indirizzo IP del Server Active Socket
PL=Porta Locale
Listening
Socket PR = Porta Remota
Client2
Active Socket
Active Socket
Active Socket
PL=Porta Locale
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
Lezione 6: TCP: Stream Sockets Andrea Corradini 9
Apertura di una connessione
Three 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 Andrea Corradini 1 0
Lezione 6: TCP: Stream Sockets Andrea Corradini 1 1
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 Andrea Corradini 1 2
STREAM SOCKET JAVA API: LATO CLIENT
Classe 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 Andrea Corradini 1 3
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 Andrea Corradini 1 4
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 Andrea Corradini 1 5
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 Send Buffer
InputStream Receive Buffer
Struttura del Socket TCP
Lezione 6: TCP: Stream Sockets Andrea Corradini 1 6
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 Andrea Corradini 1 7
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 Andrea Corradini 1 8
STREAM BASED COMMUNICATION
Per 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 Andrea Corradini 1 9
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 Andrea Corradini 2 0
UN SEMPLICE ESEMPIO DI CLIENT TCP: ECHO
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
}}}}
Lezione 6: TCP: Stream Sockets Andrea Corradini 2 1
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 Andrea Corradini 2 2
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 Andrea Corradini 2 3
STREAM MODE SOCKET API: LATO SERVER
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);}
}
}
Lezione 6: TCP: Stream Sockets Andrea Corradini 2 4
STREAM MODE SOCKET API: LATO SERVER
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
Lezione 6: TCP: Stream Sockets Andrea Corradini 2 5
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 Andrea Corradini 2 6
ESEMPIO DI SERVER TCP: ECHO
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 socket
InputStream is = sdati.getInputStream( ); // creo input stream
DataInputStream netIn = new DataInputStream(is);
OutputStream out = sdati.getOutputStream( ); // creo output str
DataOutputStream netOut = new DataOutputStream(out);
Lezione 6: TCP: Stream Sockets Andrea Corradini 2 7
ESEMPIO DI SERVER TCP: ECHO
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
}} }}
Lezione 6: TCP: Stream Sockets Andrea Corradini 2 8
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 [ ] byte [ ]
Send-Q Recv-Q
Gestito dalla Gestito dal Gestito dal Gestito dalla
applicazione Supporto Supporto applicazione
Lezione 6: TCP: Stream Sockets Andrea Corradini 2 9
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 Andrea Corradini 3 0
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 Andrea Corradini 3 1
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 Andrea Corradini 3 2
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 Andrea Corradini 3 3
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 Andrea Corradini 3 4
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 Andrea Corradini 3 5
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 Andrea Corradini 3 6
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 Andrea Corradini 3 7
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 Andrea Corradini 3 8
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 Andrea Corradini 3 9
Chiusura di una connessione
Three 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 Andrea Corradini 4 0
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 Andrea Corradini 4 1
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 Andrea Corradini 4 2
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 Andrea Corradini 4 3
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 Andrea Corradini 4 4
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 Andrea Corradini 4 5
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 Andrea Corradini 4 6
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 Andrea Corradini 4 7
CLASSE SOCKET: TCP_NODELAY
Serve 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 Andrea Corradini 4 8
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 Andrea Corradini 4 9
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 Andrea Corradini 5 0
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 Andrea Corradini 5 1
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 6: TCP: Stream Sockets Andrea Corradini 5 2
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.8
LPR-B-09
TCP Sockets & Multicast
24/11/2009
Andrea Corradini
Lezione 8: TCP Sockets e Multicast Andrea Corradini 1
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 Andrea Corradini 2
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 Andrea Corradini 3
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 Andrea Corradini 4
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 Andrea Corradini 5
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 Andrea Corradini 6
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 Andrea Corradini 7
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 Andrea Corradini 8
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 Andrea Corradini 9
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 Andrea Corradini 1 0
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 Andrea Corradini 1 1
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 Andrea Corradini 1 2
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 Andrea Corradini 1 3
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 Andrea Corradini 1 4
CREAZIONE DI CONNESSIONI LATO SERVER
Lezione 8: TCP Sockets e Multicast Andrea Corradini 1 5
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 Andrea Corradini 1 6
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 Andrea Corradini 1 7
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 Andrea Corradini 1 8
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 Andrea Corradini 1 9
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 Andrea Corradini 2 0
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 Andrea Corradini 2 1
MULTICAST: IMPLEMENTAZIONE
Server A invia un messaggio su un gruppo di multicast composto da 3
clients connessi allo stesso router (router2)
router2
router2
router1
router1
Server A
Server A
Soluzione 1: router1 invia 3 messaggi Soluzione 2: router1 invia un unico messaggio.
con collegamenti di tipo unicast router2 replica il messaggio per i tre clients
Lezione 8: TCP Sockets e Multicast Andrea Corradini 2 2
MULTICAST: IMPLEMENTAZIONE
Ottimizzazione della banda di trasmissione: il router che riceve un pacchetto
di multicast MP invia un unico pacchetto sulla rete. Il pacchetto viene replicato
solo quando è necessario.
Esempio: pacchetto di multicast spedito da Milano agli hosts Hosta, HostB,
HostC
Bari
Bologna
MP MP
Milano MP
Firenze
MP
MP MP
Roma
HostB HostC
Host A Pisa
Lezione 8: TCP Sockets e Multicast Andrea Corradini 2 3
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 Andrea Corradini 2 4
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 Andrea Corradini 2 5
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 Andrea Corradini 2 6
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 Andrea Corradini 2 7
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 Andrea Corradini 2 8
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 Andrea Corradini 2 9
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 Andrea Corradini 3 0
MULTICAST: L’API JAVA
Uso delle porte per multicast sockets:
Unicast
• IP Address individua un host,
• porta individua un servizio
Multicast
• IP Address individua un gruppo di hosts,
• porta consente di partizionare dati di tipo diverso inviati allo stesso
gruppo
invio dati
Gruppo di
multicast
5000 5000
4000
Host1 4000 Host2
….
Esempio: porta 5000 traffico audio, porta 4000 traffico video
Lezione 8: TCP Sockets e Multicast Andrea Corradini 3 1
MULTICAST API DI JAVA: il receiver
import java.net.*;
public class MulticastTestReceiver{
public static void main (String [ ] args) throws Exception{
InetAddress group = InetAddress.getByName(args[0]); // gruppo
if (!group.isMulticastAddress()){ // controllo se è multicast
throw new IllegalArgumentException();
}
int port = Integer.parseInt(args[1]); // porta locale
MulticastSocket ms = new MulticastSocket(port);
ms.joinGroup (group); // mi iscrivo al gruppo
DatagramPacket dp = new DatagramPacket(new byte[8192], 8192);
ms.receive(dp); // ricevo e stampo
System.out.println(new String(dp.getData(),0,dp.getLength())); }}
Lezione 8: TCP Sockets e Multicast Andrea Corradini 3 2
MULTICAST API DI JAVA: il sender
import java.net.*;
public class MulticastTestSender{
public static void main (String [ ] args) throws Exception{
InetAddress group = InetAddress.getByName(args[0]); // gruppo
if (!group.isMulticastAddress()){ // controllo se è multicast
throw new IllegalArgumentException(); }
int port = Integer.parseInt(args[1]); // porta destinataria
System.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 Andrea Corradini 3 3
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 Andrea Corradini 3 4
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 gruppo
int port = 0; // porta locale
try{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 Andrea Corradini 3 5
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 pacchetto
String s = new String(dp.getData()); // estraggo il messaggio
System.out.println(s);} // .lo stampo
} catch (IOException ex){System.out.println (ex);
}
Lezione 8: TCP Sockets e Multicast Andrea Corradini 3 6
MULTICAST SNIFFER
finally{ // in ogni caso...
if (ms != null) { // se avevo aperto il multicast socket
try{
ms.leaveGroup(group); // lascio il gruppo
ms.close(); // chiudo il socket
} catch (IOException ex){}
}}}}
Lezione 8: TCP Sockets e Multicast Andrea Corradini 3 7
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 Andrea Corradini 3 8
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 Andrea Corradini 3 9
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 Andrea Corradini 4 0
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 Andrea Corradini 4 1
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 8: TCP Sockets e Multicast Andrea Corradini 4 2
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.9
LPR-B-09
RMI: Remote Method
Invocation
1/12/2009
Andrea Corradini
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 1
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 Andrea Corradini 2
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
richies
ta
Bloccato In elaborazione
ta
rispos
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 3
REMOTE PROCEDURE CALL: ESEMPIO
PROCESSO CLIENT PROCESSO SERVER
procedura
remota print(…)
print (msg)
bloccato
codice
Esempio: richiesta stampa di messaggio e restituzione esito operazione
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 4
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 Andrea Corradini 5
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 Andrea Corradini 6
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 Andrea Corradini 7
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 Andrea Corradini 8
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 Andrea Corradini 9
OGGETTI REMOTI: ARCHITETTURA GENERALE
Object Registry
Riferimento
all’oggetto remoto Esportazione (registrazione)
Client Server
Oggetto
Invocazione metodi remoto
Client Proxy Server Proxy
Network Support Network Support
Interazione logica
Interazione fisica
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 10
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 Andrea Corradini 11
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 Andrea Corradini 12
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 Andrea Corradini 13
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 Andrea Corradini 14
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 Andrea Corradini 15
ESEMPIO DI REMOTE INTERFACE
Esempio: un Host è connesso ad una stazione metereologica che rileva
temperatura, umidità,….mediante diversi strumenti di rilevazione. Sull’host è
in esecuzione un server che fornisce queste informazioni agli utenti
interessati. 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 Andrea Corradini 16
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 lo
stesso 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 Andrea Corradini 17
ESEMPIO RMI: un servizio di ECHO
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
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 18
ESEMPIO RMI: un servizio di ECHO
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()); }}}
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 19
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 Andrea Corradini 20
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 Andrea Corradini 21
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
nome
servizio riferimento
riferimento
echo all’oggetto
Lezione 9: RMI - Remote Method Invocation Andrea Corradini 22
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 Andrea Corradini 23
JAVA : IL REGISTRY
Supponiamo che registry sia l'istanza di un registro individuato mediante
getregistry( )
• 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 Andrea Corradini 24
JAVA: ATTIVAZIONE DEL SERVIZIO
Per rendere disponibile i metodi dell’oggetto remoto, è necessario attivare
due 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 Andrea Corradini 25
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 Andrea Corradini 26
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 Andrea Corradini 27
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 Andrea Corradini 28
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 Andrea Corradini 29
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 Andrea Corradini 30
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 Andrea Corradini 31
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 Andrea Corradini 32
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 Andrea Corradini 33
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 Andrea Corradini 34
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 Andrea Corradini 35
ESERCIZIO
Sviluppare una applicazione RMI per la gestione di un’elezione. Il server
esporta 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 9: RMI - Remote Method Invocation Andrea Corradini 36
Università degli Studi di Pisa
Dipartimento di Informatica
Lezione n.10
LPR-B-09
RMI CallBacks
9/12/2009
Andrea Corradini
Lezione 10: RMI CallBacks - Miscellanea Andrea Corradini 1
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 Andrea Corradini 2
IL MECCANISMO DELLE CALLBACK: MOTIVAZIONI
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
Lezione 10: RMI CallBacks - Miscellanea Andrea Corradini 3
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 Andrea Corradini 4
IL MECCANISMO DELLE CALLBACK: MOTIVAZIONI
• 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?
Lezione 10: RMI CallBacks - Miscellanea Andrea Corradini 5
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 Andrea Corradini 6
RMI: IL MECCANISMO DELLE CALLBACK
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
Lezione 10: RMI CallBacks - Miscellanea Andrea Corradini 7
CHE SIGNIFICA “CALLBACK”?
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
Lezione 10: RMI CallBacks - Miscellanea Andrea Corradini 8
CALLBACKS: UN ESEMPIO
Lato Server:
Interfaccia remota che definisce metodi per: (1) Contattare il
server: metodo SayHello( ) (2) Registrare/cancellare una callback:
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 Andrea Corradini 9
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 Andrea Corradini 1 0
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 Andrea Corradini 1 1
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 Andrea Corradini 1 2
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 Andrea Corradini 1 3
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 Andrea Corradini 1 4
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 Andrea Corradini 1 5
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 Andrea Corradini 1 6
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 Andrea Corradini 1 7
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 Andrea Corradini 1 8
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 Andrea Corradini 1 9
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 Andrea Corradini 2 0
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.
Lezione 10: RMI CallBacks - Miscellanea Andrea Corradini 2 1