You are on page 1of 396

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

You might also like