La comunicazione e lo scambio dell'informazione sono da sempre alla base di Internet. Anche Java, nato inizialmente con lo stesso obiettivo, supporta la programmazione di rete. Il package java.net
raccoglie le interfacce, le classi e le possibili eccezioni utili proprio alla programmazione per questo tipo di applicazioni, spesse volte dette anche applicazioni client/server.
Queste applicazioni osservano un noto modello di programmazione che è appunto il modello client/server. Durante lo scambio dell'informazione il suddetto modello assegna ai partecipanti dei ruoli ben precisi: il client è l'applicazione che, interfacciandosi attraverso la rete al server, richiede un preciso servizio; il server è invece quell'applicazione che soddisfa, attraverso la rete, la richiesta del client.
La comunicazione fra client e server si svolge inevitabilmente attraverso la rete, grazie ai protocolli di rete. A livello di trasporto essa può allora avvenire attraverso due dei principali protocolli di trasporto: TCP o UDP. Il protocollo di rete TCP realizza un canale logico per la connessione affidabile e orientato alla connessione, si impegna a fare del suo meglio per garantire l'arrivo dei pacchetti e l'ordine con cui essi vengono spediti. Il protocollo di rete UDP è assai più sbrigativo, non prevede una connessione e non garantisce ne l'arrivo ne l'ordine dei pacchetti. UDP viene usato, ad esempio, per lo streaming dei flusi audio e/o video. La scelta del protocollo per il trasporto può quindi variare in base al tipo di applicazione che abbiamo in mente.
Tale scelta va fatta ancora prima di mettersi a scrivere il codice dell'applicazione. Java implementa nel package jav.net
diverse classi, alcune di queste sono orientate al protocollo TCP, altre invece seguono il protocollo UDP. La comunicazione fra client e server avviene attraverso le socket (presa o spina). Ogni applicazione si può dotare di una socket, la finestra sulla rete.
La classe Socket
implementa uno dei due lati della connessione di rete (end-point) ed è orientata al protocollo TCP, tipicamente quella di un'applicazione client. La classe ServerSocket
, invece, realizza la socket per l'applicazione server. Tale socket viene usata dai server per rimanere in ascolto e accettare le richieste di connessione dei client. La classe DatagramSocket
realizza l'astrazione necessaria all'invio e alla ricezione dei dati attraverso il protocollo UDP.
In questo articolo mi occuperò esclusivamente delle classi Socket
e ServerSocket
. E' evidente che l'applicazione server deve trovarsi già in esecuzione prima di ricevere richieste dai client che altrimenti si perdono nella rete. Osserveremo allora prima la struttura di un'applicazione server. L'istanza di una socket per il server avviene attraverso il costruttore della classe ServerSocket
:
public ServerSocket(int port) throws IOException
dove port
rappresenta il numero di porta usato dal server, presso il proprio indirizzo di rete, per l'ascolto. Alcuni numeri di porta sono assegnati di default e spettano a vari servizi di rete (sono cioè usati da altri protocolli per applicazioni). Se l'istanza di una ServerSocket
non riesce vi consiglio di cambiare il numero della porta usato nel costruttore, potrebbe infatti essere già impegnato! Il costruttore va poi eseguito all'interno di un blocco try/catch per intercettare proprio l'errore che vi descrivevo poco fa. Esistono altri tipi di cotruttori, date come sempre uno sguardo alla documentazione della classe:
server=new ServerSocket(10000);
Se l'istanza della socket riesce possiamo allora collocare la stessa in uno stato di attesa. Il metodo accept()
blocca l'esecuzione del programma e lo pone in attesa di richieste di connessione. Il metodo si sblocca alla prima richiesta del client, ritornando l'oggetto socket
usato dallo stesso! Da questo oggetto il server, se vuole, può estrarre l'indirizzo IP con il metodo getInetAddress()
:
client=server.accept();
Affinchè il server possa realmente mandare al client dei byte (testo o informazioni varie) deve necessariamente estrarre dalla socket del client gli stream per l'input:
input=new BufferedReader(new InputStreamReader(client.getInputStream()));e per l'output:
output=new PrintWriter(client.getOutputStream(),true);
getInputStream()
e getOutputStream()
ritornano rispettivamente InputStream
e OutputStream
. La comunicazione adesso è davvero semplice, se il server intende leggere cosa ha scritto il client non deve fare altro che usare lo stream input
. Nell'esempio ho appositamente convogliato lo stream di input in un oggetto BufferedReader
che semplifica la lettura con il metodo readLine()
(che ritorna una stringa). Per leggere, ad esempio, si potrebbe avere questa istruzione:String clientMessage=input.readLine();
output.println("Ciao!");
input.close();
output.close();
client.close();
server.close();
accept()
). Il client può allora usare la classe Socket
in questo modo:client=new Socket(ip,10000);
ip
è la stringa con l'indirizzo ip del server e 10000
il numero di porta del servizio. Se la connessione riesce senza eccezzioni (anche questa operazione, così come le altre, va sempre innescata all'interno di un blocco try/catch) il client può allora ricavare dalla socket (ora connessa al client) gli stream per l'input e per l'output. Per l'input, come visto prima, ad esempio:input=new BufferedReader(new InputStreamReader(client.getInputStream()));per l'output, analogamente:
output=new PrintWriter(client.getOutputStream(),true);
L'applicazione permette lo scambio di messaggi fra client e server, dopo aver effettuato la connessione al server il client inizia a scrivere al server (che si mette quindi in attesa di un messaggio). Subito dopo è il client che si mette in attesa di un messaggio di risposta da parte del server. La comunicazione si svolge quindi su turni. Va inoltre osservata la capacità del server di rispondere a una sola richiesta di connessionne del client. Per implementare un server che accetta più connessioni quest'ultimo deve necessariamente usare una socket per l'ascolto e generare ad ogni connessione una socket per il client.
Nessun commento:
Posta un commento