Corso di Reti di Calcolatori Programmazione Java 3. Socket TCP e UDP 4. Sviluppare un webserver 20/12/2002 ing. G. Lo Presti
Interfacce: esempio d’uso Abbiamo visto l’interfaccia Runnable, che useremo per gestire il multithreading: class MyRun implements Runnable { public void run( ) { …… // method implementation } …… Thread threadB = new Thread(new MyRun()); threadB.start( ); Thread target Runnable Thread constructor Object instance of MyRun class run() MyRun run() …come funziona il metodo start( ) ? ing. G. Lo Presti
Interfacce: esempio d’uso Questa è la pseudocodifica della classe Thread: public class Thread { Runnable target; public Thread(Runnable target) { this.target = target; } public void start( ) { create a thread; asynchronously execute target.run( ); immediately return; Siamo certi che questa chiamata venga correttamente eseguita perché target deve contenere un metodo run( ). Object Object Se target fosse di qualunque altro tipo (ad es. Object), il compilatore non riuscirebbe a trovare il metodo run( ) neanche se fosse effettivamente presente. ing. G. Lo Presti
I Socket (richiami) socket Le socket API consentono alle applicazioni di comunicare con altri processi Approccio Client/Server Un socket viene identificato dalla quintupla: (protocol, clientIP, clientPort, serverIP, serverPort) Il protocollo di trasporto può essere TCP o UDP TCP: reliable, in-order transfer of byte (“pipe”) between client and server UDP: unreliable transfer of group of bytes (“datagrams”) between client and server A host-local, application-created/owned, OS-controlled interface (a “door”) into which application process can both send and receive messages to/from another (remote or local) application process socket ing. G. Lo Presti
Client/server socket interaction: TCP Server (running on hostname) Client create socket, port=x, for incoming request: welcomeSocket = ServerSocket(x) TCP connection setup close connectionSocket read reply from clientSocket create socket, connect to hostname, port=x clientSocket = Socket(host, x) wait for incoming connection request connectionSocket = welcomeSocket.accept() send request using clientSocket read request from connectionSocket write reply to ing. G. Lo Presti
Esempio: TCP server class TCPServer { public static void main(String argv[]) { String clientSentence, capitalizedSentence; try { ServerSocket welcomeSocket = new ServerSocket(6789); } catch (IOException ioe) { System.err.println(“Could not listen on port 6789.”); } while(true) try { Socket connectionSocket = welcomeSocket.accept(); // bloccante! BufferedReader inFromClient = new BufferedReader( new InputStreamReader(connectionSocket.getInputStream())); DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream()); clientSentence = inFromClient.readLine(); // bloccante capitalizedSentence = clientSentence.toUpperCase() + '\n'; outToClient.writeBytes(capitalizedSentence); // connectionSocket.close(); non necessaria dato che viene fatto lato client } catch (IOException ioe) { System.err.println(“I/O error: ”+ ioe); } catch (Exception e) { System.err.println(“Other error: ”+ e); e.printStackTrace(); } } ing. G. Lo Presti
Esempio: TCP client class TCPClient { public static void main(String argv[]) { String sentence, modifiedSentence; try { BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in)); Socket clientSocket = new Socket(“hostname”, 6789); DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream()); BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); sentence = inFromUser.readLine(); // bloccante outToServer.writeBytes(sentence + '\n'); modifiedSentence = inFromServer.readLine(); // bloccante System.out.println("FROM SERVER: " + modifiedSentence); clientSocket.close(); } catch (IOException ioe) { System.err.println(“I/O error: ”+ ioe); } catch (Exception e) { System.err.println(“Other error: ”+ e); e.printStackTrace(); } } ing. G. Lo Presti
Client/server socket interaction: UDP Server (running on hostname) create socket, clientSocket = DatagramSocket() Client Create, address=hostname, port=x, send datagram request using clientSocket create socket, port=x, for incoming request: serverSocket = DatagramSocket(x) read request from serverSocket close clientSocket read reply from clientSocket write reply to serverSocket specifying client host address, port number ing. G. Lo Presti
Esempio: UDP server class UDPServer { public static void main(String args[]) { try { DatagramSocket serverSocket = new DatagramSocket(9876); } catch (IOException ioe) { System.err.println(“Could not listen on port 9876.”); } byte[] receiveData = new byte[1024]; byte[] sendData = new byte[1024]; while(true) try { DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); serverSocket.receive(receivePacket); // bloccante String sentence = new String(receivePacket.getData()); InetAddress IPAddress = receivePacket.getAddress(); // ricava dal pacchetto in arrivo int port = receivePacket.getPort(); // le informazioni per rispondere String capitalizedSentence = sentence.toUpperCase(); sendData = capitalizedSentence.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port); serverSocket.send(sendPacket); } catch (IOException ioe) { System.err.println(“I/O error: ”+ ioe); } catch (Exception e) { System.err.println(“Other error: ”+ e); e.printStackTrace(); } } ing. G. Lo Presti
Esempio: UDP client class UDPClient { public static void main(String args[]) { try { BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in)); DatagramSocket clientSocket = new DatagramSocket(); InetAddress IPAddress = InetAddress.getByName("hostname"); byte[] sendData = new byte[1024]; byte[] receiveData = new byte[1024]; String sentence = inFromUser.readLine(); sendData = sentence.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 9876); clientSocket.send(sendPacket); DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); clientSocket.receive(receivePacket); // bloccante String modifiedSentence = new String(receivePacket.getData()); System.out.println("FROM SERVER: " + modifiedSentence); clientSocket.close(); // obbligatorio chiudere il socket anche se UDP è connectionless } catch (IOException ioe) { System.err.println(“I/O error: ”+ ioe); } catch (Exception e) { System.err.println(“Other error: ”+ e); e.printStackTrace(); } } ing. G. Lo Presti
Il protocollo HTTP (richiami) Protocollo stateless basato su TCP (porta 80) 2 tipi di messaggi HTTP: request, response formato: ASCII in chiaro HTTP request message: request line (GET, POST, HEAD commands) GET /somedir/page.html HTTP/1.1 User-agent: Mozilla/4.0 Accept: text/html, image/gif,image/jpeg Accept-language:it (extra carriage return, line feed) header lines Carriage return, line feed indicates end of message ing. G. Lo Presti
Il protocollo HTTP (richiami) HTTP response message: status line (protocol status code status phrase) HTTP/1.0 200 OK Date: Thu, 06 Aug 1998 12:00:15 GMT Server: Apache/1.3.0 (Unix) Last-Modified: Mon, 22 Jun 1998 ... Content-Length: 6821 Content-Type: text/html data data data data data ... header lines data, e.g. requested html file ing. G. Lo Presti
Un semplice webserver Obiettivo: sviluppare un semplice webserver con le seguenti specifiche: HTTP 1.0 compliant; stare in ascolto sulla porta TCP 8080; soddisfare le richieste di tipo HEAD e di tipo GET, quest’ultima con una pagina HTML standard creata al volo (non gestire file); gestire eventuali eccezioni senza terminare il demone in ascolto; gestire un client alla volta (mono thread); ing. G. Lo Presti
Il linguaggio HTML Hyper-Text Markup Language Linguaggio per la descrizione di documenti Payload del protocollo HTTP Organizzato a tag, con una struttura ad albero il tag è l’elemento informativo elementare e può contenere attributi es. <a href=‘http://www.unipa.it/’>Link</a> i tag si possono nidificare es. <p><font face=‘Helvetica’>Paragrafo di testo</font></p> Riferimenti: www.w3c.org/markup, www.html.it Sviluppi recenti: XML ing. G. Lo Presti
Un semplice webserver Package per gestire connessioni di rete e I/O import java.net.*; import java.io.*; public class SimpleWebServer { public static void main(String[] args) { ServerSocket serverSocket = null; int reqCount = 1; try { serverSocket = new ServerSocket(8080); } catch (IOException e) { System.err.println("Could not listen on port 8080."); System.exit(1); } System.out.println("Server started.” + “ Waiting for client connections...\n”); while (true) { Socket clientSocket = null; clientSocket = serverSocket.accept(); System.err.println("Accept failed."); continue; Package per gestire connessioni di rete e I/O Si mette in ascolto sulla porta 8080 Aspetta indefinitamente un client (browser) ing. G. Lo Presti
Un semplice webserver try { PrintWriter out = new PrintWriter( clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream())); String inputLine; boolean doGet = false; System.out.println("*** Client request:"); while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); if (inputLine.indexOf("GET") != -1) doGet = true; if (inputLine.length() == 0) break; } Alla connessione di un client vengono creati gli stream associati al socket: PrintWriter e BufferedReader (orientati ai caratteri) Viene letto l’intero comando di request: se è presente la parola chiave GET allora si tratta di una richiesta di tipo GET La lettura termina quando vengono rilevati due EOL consecutivi ing. G. Lo Presti
Un semplice webserver Prepara la pagina HTML da restituire String data = "<html>\n<head><title>Simple WebServer”+ + " Test</title></head>\n" + "<body>\n<h2>Hello world!<br>This is the " + reqCount + "th request from startup.</h2>\n" + "</body></html>\n"; out.println("HTTP/1.0 200 ok"); out.println("Date: " + (new java.util.Date())); out.println("Server: Simple java web server/1.0"); out.println("Last-Modified: " + (new java.util.Date())); if (doGet) out.println("Content-Length: " + data.length()); out.println("Content-Type: text/html"); out.println(); System.out.println("\tHeader sent"); if (doGet) { out.println(data); System.out.println("\tData sent"); } System.out.println(); Prepara la pagina HTML da restituire Inizia l’invio del response HTTP verso il client: viene inviato l’header seguito da un EOL Se si tratta di un comando GET invia la pagina HTML, per qualunque altro comando invia solo l’header. ing. G. Lo Presti
Un semplice webserver Chiude entrambi gli stream ed il socket verso il client Gestisce qualunque errore di I/O segnalandolo sulla console In ogni caso incrementa il contatore delle richieste effettuate e ricomincia ad aspettare un nuovo client. out.close(); in.close(); clientSocket.close(); } catch (IOException ioe) { System.err.println("I/O error: " + ioe); reqCount++; ing. G. Lo Presti
Webserver Test ing. G. Lo Presti
Riepilogo Socket in Java Esempio: un webserver Socket ServerSocket DatagramSocket Esempio: un webserver HTTP monothread! come realizzare un server multithread? ing. G. Lo Presti