J0
1
Marco Ronchetti - [email protected]
Java Networking
TCP
Marco Ronchetti - [email protected]
Sockets
J0
2
Java supporta un accesso semplificato e objectoriented alle sockets.
Questo rende la network comunicazione di rete
sensibilmente piu’ semplice.
Molto piu’ semplice che in C++!!
Vedremo che parlare ad un altra applicazione (locale o remota)
puo’ essere semplice come leggere un file o ottenere un input
da un utente.
Marco Ronchetti - [email protected]
Clients and Servers
J0
3
Quando si scrivono network applications, e’ comune
parlare di clients e servers. La distinzione e’ piuttosto
vaga, ma sostanzialmente possiamo pensare che :
•chi inizia la conversazione e’ il client.
•chi accetta la richiesta di parlare e’ il server.
Per I nostri scopi, la piu’ importante differenza tra client e
server e’ che il client puo’ in qualunque istante creare una
socket per iniziare una conversazione con una server
application, mentre un server si deve preparare ad ascoltare in
anticipo per possibili conversazioni in arrivo.
Marco Ronchetti - [email protected]
Sockets
J0
4
La java.net.Socket class rappresenta un singolo lato di
una connessione socket indifferentemente su un client
o su un server.
Inoltre, il server usa la java.net.ServerSocket class per
attendere connessioni da clients. Un applicazione
agente come server crea un ServerSocket object e
attende, bloccato in una chiamata al suo metodo
accept(), finche’ non giunge una connessione. A quel
punto, il metodo accept() create un Socket object che il
server usa per comunicare con il client.
Marco Ronchetti - [email protected]
Sockets
J0
5
Un server mantiene molte conversazioni
simultaneamente.
C’e’ una sola ServerSocket, ma una oggetto Socket
attivo per ogni cliente.
Marco Ronchetti - [email protected]
Server port
J0
6
Un client ha bisogno di due informazioni per connettersi a un
server su Internet: un nome di host (per recuperare l’indirizzo
del server) e un numero di porta (per individuare il processo
sulla macchina server).
Una applicazione server ascolta su una porta predefinita
mentre attende una connessione. I clients selezionano il
numero di porta corrispondente al servizio desiderato.
I numeri di porta sono codificati nelle RFC (Es. Telnet 23, FTP
21, ecc.), ma possono anche essere scelti (quasi)
arbitrariamente per applicazioni custom.
Marco Ronchetti - [email protected]
Client port
J0
7
Il numero di porta del client e’ tipicamente assegnato
dal sistema operativo: la scelta di tale numero non e’
di solito rilevante.
Quando una client socket mand un pacchetto a una
server socket, il pacchetto include la specifica della
porta e dell’indirizzo del cliente: cosi’ il server e’ in
grado di rispondere.
Marco Ronchetti - [email protected]
Sockets
J0
8
Usando le sockets, si deve decidere che tipo di
protocollo si desidera per il trasporto di pacchetti
sulla rete:
Un protocollo connection-oriented o
Un protocollo connectionless.
Marco Ronchetti - [email protected]
Connection-oriented protocols
Un protocollo connection-oriented offre l’equivalente
di una conversazione telefonica.
•Dopo aver stabilito la connessione, due
applicazioni possono scambiarsi dati.
•La connessione rimane in essere anche se nessuno
parla.
Il protocollo garantisce che non vengano persi dati e
che questi arrivino sempre nell’ordine corretto.
La classe Java Socket usa un protocollo connectionoriented, e “parla” TCP
J0
9
Marco Ronchetti - [email protected]
Connectionless Protocols
Un protocollo connectionless somiglia al servizio
postale.
Le applicazioni possono inviarsi brevi messaggi, ma
non viene tenuta aperta una connessione tra un
messaggio e l’altro.
Il protocollo NON garantisce che non vadano persi
dati e che ne’ che questi arrivino nell’ordine corretto.
La classe DatagramSocket usa un protocollo
connectionless, e “parla” UDP
J0
10
Marco Ronchetti - [email protected]
Connection oriented Protocols
J0
11
Con un connection-oriented protocol, una client socket
stabilisce, alla sua creazione, una connessione con una
server socket. Stabilita la connessione, un protocollo
connection-oriented assicura la consegna affidabile dei dati,
ovvero:
1. Ogni pacchetto spedito, il pacchetto viene consegnato. Ad
ogni spedizione, la socket si aspetta di ricevere un
acknowledgement cheil pacchetto e’ stato ricevuto con
sucesso.
Se la socket non riceve l’acknowledgement entro un tempo
prestabilito, la socket ri-invia il pacchetto. La socket
continua a provare finche’ la trasmissione ha successo, o
finche’ decide che la consegna e’ impossibile.
Marco Ronchetti - [email protected]
Connection oriented Protocols
J0
12
2. I paccheti sono letti from dalla socket ricevente nello
stesso ordine in cui sono stati spediti. Per il modo in cui la
rete funziona, i pacchetti possono arrivare alla socket
destinataria in un ordine diverso da quello in cui sono stati
spediti.
Un protocollo reliable, connection-oriented permette alla
socket ricevente di riordinare i paccheti ricevuti, cosi’ che
possano essere letti dal programma ricevente nell’orine in
cui erano stati spediti.
Marco Ronchetti - [email protected]
java.net.Socket
J0
13
Questa classe implementa una socket per interprocess
communication over the network.
I constructor methods creano la socket e la connettono allo
host e porta specificati.
Marco Ronchetti - [email protected]
java.net.Socket
J0
14
Una volta creata la socket, getInputStream() e
getOutputStream() ritornano oggetti InputStream e
OutputStream che possono essere usati come se fossero canali
di I/O su file.
getInetAddress() e getPort() restituicono indirizzo e porta a
cui la socket e’ connessa.
getLocalPort() ritorna la porta locale usata dalla socket .
Marco Ronchetti - [email protected]
java.net.ServerSocket
J0
15
Questa classe e’ usata dai servers per ascoltare le
richieste di connessione.
Quando si crea una ServerSocket, si specifica su
quale porta si ascolta.
Il metodo accept() iniza ad ascoltare su quella porta,
e si blocca finche’ un client non richiede una
connessione su quella porta.
A quel punto, accept() accetta la connessione,
creando e ritornando una Socket che il server puo’
usare per comunicare col client.
Marco Ronchetti - [email protected]
Sockets
J0
16
Clients
Un applicazione client apre una connessione con un
server costruendo una Socket che specifica hostname e
port number del server desiderato:
try {
Socket sock = new Socket(“www.pippo.it", 80);
//Socket sock = new Socket("128.252.120.1", 80);
} catch ( UnknownHostException e ) {
System.out.println("Can't find host.");
} catch ( IOException e ) {
System.out.println("Error connecting to host.");
}
Marco Ronchetti - [email protected]
Connection-oriented protocol
J0
17
Il lato server di un protocollo connection-oriented tipicamente
esegue questa sequenza:
•Crea un ServerSocket object per accettare connessioni.
•Quando il ServerSocket accetta la connection, crea un
oggetto Socket che encapsula la connessione.
•Alla Socket viene chiesto di creare oggetti InputStream e
OutputStream che leggono e scrivono bytes da e verso la
connessione.
•La ServerSocket puo’ opzionalmente creare una nuova
thread per ogni connessione, cosi’ che il server possa ascoltare
per nuove connessioni mentre serve i clienti gia’ acquisiti.
Marco Ronchetti - [email protected]
Reading & Writing raw bytes – Client side
J0
18
Una volta stabilita la connessione, input e output
streams possono essere ottenuti con i metodi
getInputStream() e getOutputStream() di Socket .
try {
Socket server = new Socket("foo.bar.com", 1234);
InputStream in = server.getInputStream();
OutputStream out = server.getOutputStream();
// Write a byte
out.write(42);
// Read a byte
Byte back = in.read();
server.close();
} catch (IOException e ) { }
Marco Ronchetti - [email protected]
Reading & Writing newline delimited strings – Client
J0
19
Incapsulando InputStream & OutputStream e’
possibile accedere agli streams in modo piu’ semplice.
try {
Socket server = new Socket("foo.bar.com", 1234);
InputStream in = server.getInputStream();
DataInputStream din = new DataInputStream( in );
OutputStream out = server.getOutputStream();
PrintStream pout = new PrintStream( out );
// Say "Hello" (send newline delimited string)
pout.println("Hello!");
// Read a newline delimited string
String response = din.readLine();
server.close();
} catch (IOException e ) { }
Reading & Writing raw bytes – Server side
Marco Ronchetti - [email protected]
try {
J0
20
ServerSocket listener = new ServerSocket( 1234 );
while ( !finished ) {
Socket aClient = listener.accept();
// wait for connection
InputStream in = aClient.getInputStream();
OutputStream out = aClient.getOutputStream();
// Read a byte
Byte importantByte = in.read();
// Write a byte
out.write(43);
aClient.close();
}
listener.close();
} catch (IOException e ) { }
Reading & Writing newline delimited strings – Server
Marco Ronchetti - [email protected]
try {
J0
21
ServerSocket listener = new ServerSocket( 1234 );
while ( !finished ) {
Socket aClient = listener.accept();
// wait for connection
InputStream in = aClient.getInputStream();
DataInputStream din = new DataInputStream( in );
OutputStream out = aClient.getOutputStream();
PrintStream pout = new PrintStream( out );
// Read a string
String request = din.readLine();
// Say "Goodbye"
pout.println("Goodbye!");
aClient.close();
}
listener.close();
} catch (IOException e ) { }
This server is single-threaded
Marco Ronchetti - [email protected]
Questo server e’ single-threaded
J0
22
Tratta una sola connessione alla volta; non chiama
accept() per ascoltare nuove connessioni fin quando
non ha finito con quella corrente.
Un server piu’ realistico avrebbe un loop che accetta
connessioni e le passa ad altre thread per servirle
concorrentemente.
Marco Ronchetti - [email protected]
What about the Little Endian/Big Endian issue?
J0
23
Le classi Java
DataInputStream and DataOutputStream
lavorano con i bytes di tipi interi in
network byte order
(most significant to least significant).
Percio’ non c’e’ necessita’ di conversione dei dati!
Marco Ronchetti - [email protected]
Limitations
J0
24
Applets e altre applicazioni che girano sotto il
controllo del SecurityManager possono avere
restrizioni arbitrarie su quali hosts possono
raggiungere, e sul fatto di poter accettare
connessioni.
La security policy standard imposta dai browsers
premette alle applets di aprire socket solo con lo host
da cui provengono.
Le applets are hanno il permesso di aprire server
sockets.
Marco Ronchetti - [email protected]
Limitations
J0
25
Ora, questo non significa che una applet non possa
cooperare con il suo server per comunicare con
chiunque e ovunque. Il server può girare un proxy
che permette alla applet di comunicare
indirettamente con chiunque.
Questo pone l’onere del controllo della sicurezza sul
server originante, e non sulla macchina client.
Un mini-server concorrente HTTP – Introduz.
Marco Ronchetti - [email protected]
TinyHttpd ascolta su una port specificata and serve semplici
richieste HTTP "get file“:
J0
26
GET /path/filename [optional stuff]
Il Web browser manda una o piu’ di queste linee per ogni
documento da ottenere. Letta la richiesta, il server prova ad
aprire il file specificato e ne spedisce il contenuto. Se il
documento contiene referenze ad immagini o altro da mostrare
online, il browser continua con richieste GET addizionali. Per
ragioni di performance, TinyHttpd serve ogni richiesta in una
sua thread. Percio’ TinyHttpd puo’ servire molte richieste
concorrentemente.
Marco Ronchetti - [email protected]
Un mini-server concorrente HTTP
J0
27
import java.net.*;
import java.io.*;
import java.util.*;
public class TinyHttpd {
public static void main( String argv[] ) throws IOException {
ServerSocket ss =
new ServerSocket(Integer.parseInt(argv[0]));
while ( true )
new TinyHttpdConnection( ss.accept() );
}
}
Un mini-server concorrente HTTP
Marco Ronchetti - [email protected]
class TinyHttpdConnection extends Thread {
Socket sock;
TinyHttpdConnection ( Socket s ) {
sock = s;
setPriority( NORM_PRIORITY - 1 );
start();
}
public void run() {
try {
OutputStream out = sock.getOutputStream();
DataInputStream d=new DataInputStream(sock.getInputStream());
String req = d.readLine();
System.out.println( "Request: "+req );
StringTokenizer st = new StringTokenizer( req );
J0
28
Un mini-server concorrente HTTP
Marco Ronchetti - [email protected]
if ( (st.countTokens() >= 2) &&
st.nextToken().equals("GET") ) {
if ( (req = st.nextToken()).startsWith("/") )
req = req.substring( 1 );
if ( req.endsWith("/") || req.equals("") )
req = req + "index.html";
try {
FileInputStream fis = new FileInputStream ( req );
byte [] data = new byte [ fis.available() ];
fis.read( data );
out.write( data );
} catch ( FileNotFoundException e )
new PrintStream( out ).println("404 Not Found"); }
else new PrintStream( out ).println( "400 Bad Request" );
sock.close();
} catch ( IOException e )
System.out.println( "I/O error " + e );
}
J0
29
}
Marco Ronchetti - [email protected]
Un mini-server concorrente HTTP - uso
J0
30
Compila TinyHttpd e mettilo nel tuo class path. Vai in una
directory con qualche documento interessante e avvia il
daemon, specificando un numero di porta non usato come
argomento. Per esempio:
% java TinyHttpd 1234
Ora dovrebbe essere possibile usare un Web browser standard
per ottenere i files dal tuo host. Nella URL dovra’ essere
specificato il numero di porta scelto. Per esempio, se il tuo
hostname e’ venezia.sodalia.it, ed hai avviato il server come
sopra, sara’ possibile ottenere il file welcome.html con:
http://venezia.sodalia,it:1234/welcome.html
Marco Ronchetti - [email protected]
Un mini-server concorrente HTTP - Note
J0
31
Abbassando la sua priorita’ a NORM_PRIORITY-1,
assicuriamo che la threads che serve le connessioni
gia’ stabilite non blocchi la main thread di TinyHttpd
impedendole di accettare nuove richieste.
Marco Ronchetti - [email protected]
Un mini-server concorrente HTTP - Problemi
J0
32
TinyHttpd ha un sacco di possibilita’ di
miglioramento!
Primo: consuma un sacco di memoria allocando un
array enorme per leggere un intero the file tutto d’un
colpo. Un’implementazione piu’ realistica userebbe un
buffer ed invierebbe i dati in piu’ passei.
TinyHttpd inoltre non riconosce semplici directories.
Non sarebbe difficile aggiungere alcune lineee per
leggere directories e generare liste di link HTML
come fanno molti web server.
Marco Ronchetti - [email protected]
Un mini-server concorrente HTTP - Problemi
J0
33
TinyHttpd soffre di limitazioni imposte dalla
debolezza di accesso al filesystem.
E’ importante ricordare che le pathnames sono
architecture dependent—cosi’ come il concetto stesso
di filesystem. TinyHttpd funziona, cosi’ com’e’, su
systemi UNIX e DOS-like, ma richiede alcune
variazioni su altre piattaforme. E’ possibile scrivere
del codice piu’ elaborato code che usa environmental
information offerte da Java per adattarsi al sistema
locale.
Marco Ronchetti - [email protected]
Un mini-server concorrente HTTP - Problemi
J0
34
Il problema principale con TinyHttpd e’ che non ci
sono restrizioni sui files a cui puo’ accedere. Con
qualche trucco, il daemon spedirebbe un qualunque
file del suo filesystem al client.
Sarebbe utile rlimitare TinyHttpd ai files che sono
nella directory corrente, or una sua subdirectory.
mini-server concorrente HTTP – Security Manager
Marco Ronchetti - [email protected]
import java.io.*;
J0
35
class TinyHttpdSecurityManager extends SecurityManager {
public void checkAccess(Thread g) { };
public void checkListen(int port) { };
public void checkLink(String lib) { };
public void checkPropertyAccess(String key) { };
public void checkAccept(String host, int port) { };
public void checkWrite(FileDescriptor fd) { };
public void checkRead(FileDescriptor fd) { };
public void checkRead( String s ) {
if ( new File(s).isAbsolute() ||
(s.indexOf("..") != -1) )
throw new SecurityException
("Access to file : "+s+" denied.");
}
}
mini-server concorrente HTTP – Security Manager
Marco Ronchetti - [email protected]
Il cuore di questo security manager e’ il metodo checkRead().
J0
36
Controlla due cose: assicura che il pathname non sia assoluto,
e che il pathname non contenga un double dot (..) che risale
l’albero delle directories.
Con questi vincoli siamo certi (almeno su un filesystem UNIX
o DOS-like) di aver ristretto l’accesso alle solo subdirectories
della directory corrente. Se il pathname e’ absolute o contiene
"..", checkRead() lancia una SecurityException.
Marco Ronchetti - [email protected]
mini-server concorrente HTTP – Security Manager
J0
37
Gli altri metodi che non fanno nulla --e.g., checkAccess()—
permettono al daemon di fare il suo lavoro senza interferenza
da paret del security manager.
Quando installiamo un security manager, ereditiamo
implementazioni di molte "check" routines.
L’implementazione default non permette di fare nulla; Lancia
una security exception appena chiamata. Dobbiamo aprire
dei buchi cosicche’ il daemon possa fare il suo lavoro; deve
accettare connections, ascoltare sockets, creare threads,
leggere property lists, ecc. Per questo sovrascriviamo i metodi
default.
Marco Ronchetti - [email protected]
mini-server concorrente HTTP – Security Manager
Per installare il security manager, aggiungere la
seguente riga all’inizio del main di TinyHttpd:
System.setSecurityManager( new
TinyHttpdSecurityManager() );
Per catturare le security exception, aggiungere il
seguente catch dopo la catch di
FileNotFoundException':
catch ( SecurityException e )
new PrintStream( out ).println( "403 Forbidden"
);
J0
38
Marco Ronchetti - [email protected]
A simple e-mail client
J0
39
import java.net.*;
import java.io.*;
import java.util.*;
class EMailClient{
public static void main(String[] args){
new EmailClient()
} // end main
EMailClient() {
String server = "put your email server name here";
int port = 25; //mail port
try{
//Get a socket, connected to the specified server
// on the specified port.
Socket socket = new Socket(server,port);
Marco Ronchetti - [email protected]
A simple e-mail client
J0
40
//Get an input stream from the socket
BufferedReader inputStream =
new BufferedReader(new InputStreamReader(
socket.getInputStream()));
//Get an output stream to the socket. Note
// that this stream will autoflush.
PrintWriter outputStream =
new PrintWriter(new OutputStreamWriter(
socket.getOutputStream()),true);
//Begin the conversation with the email server.
outputStream.println(
"mail from: put your email name here");
System.out.println(inputStream.readLine());
outputStream.println(
"rcpt to: " + "put your email name here");
System.out.println(inputStream.readLine());
outputStream.println("data");
System.out.println(inputStream.readLine());
Marco Ronchetti - [email protected]
A simple e-mail client
J0
41
String timeStamp = (new Date()).toString();
outputStream.println("Test message " + timeStamp);
outputStream.println(".");
System.out.println(inputStream.readLine());
//Close the socket
socket.close();
}//end try
catch(UnknownHostException e){
System.out.println(e);
System.out.println(
"Must be online to run properly.");
}//end catch UnknownHostException
catch(IOException e){System.out.println(e);}
}//end constructor
}//end class