
Quando cominciai a sviluppare le mie prime applicazioni in Java, non pensavo nemmeno lontanamente che sviluppare delle applicazioni che facessero dialogare un client con un server, che sfruttassero il multithreading e che permettessero ad un numero arbitrario di client di fare richieste al server, fosse così facile.
E’ un po’ come quando decidi di crearti un sito web. Navighi in quelli degli altri e ti chiedi “Ma quanto cavolo di tempo ci vorrà per fare una cosa del genere?”.
Un paio di ricerche sull’argomento e … scopri che per fare tutto quello di cui hai bisogno ti bastano non più di un paio di click. Tutto questo grazie a chi ha lavorato prima di noi, molte volte senza pretendere niente in cambio.
Non hai bisogno di reinventare la ruota!
Questa è una frase che devi tenere bene a mente sia nella programmazione, sia in tantissime altre occasioni della vita. Nella programmazione in particolare è opportuno conoscere tutte le tecniche, tutti i meccanismi che regolano una determinata procedura, ma al fine di una personalizzazione, non di una creazione dal nulla.
Pensa ad esempio se tu dovessi ora creare dal nulla una procedura di login per il tuo sito web. Certo, non è nulla di complicato, ma perché devo farlo se con una ricerca su Google ne trovo 1000 di form di login?
Devi però conoscere le procedure che regolano quel login, il linguaggio con cui è stato scritto … devi saperlo personalizzare, non creare.
Tornando all’argomento di questo articolo, con le librerie e le API già integrate in Java riuscirai a creare un sistema completo di dialogo tra un server ed un client con tanto di supporto per il multithreading. Mentre avanzeremo con questa applicazione, nei prossimi articoli, andremo a creare una vera e propria sala da gioco che supporta fino a 10 giocatori in contemporanea per il famosissimo gioco di poker TexasHoldem.
E adesso, vediamo come si fa.
ALCUNE BASI SUL NETWORKING
Prima di vedere il codice, voglio fare un brevissimo ripasso su alcuni concetti che hai bisogno di sapere per poter realizzare un’applicazione del genere.
(Se pensi di sapere già tutto ciò che occorre puoi saltare questa parte)
Ogni computer connesso alla rete (qualsiasi rete, che sia quella locale o che sia Internet) possiede un proprio indirizzo IP numerico. Per contattare un computer in rete è quindi necessario conoscere il suo IP (Internet Protocol), ma fortunatamente c’è un particolare servizio che si chiama DNS che permette di associare a questo IP un nome più semplice o comunque più vicino alla logica umana, ad esempio www.computergeek.it.
Direttamente collegato a questi c’è il concetto di socket, un particolare sistema che permette di associare all’indirizzo un secondo valore, la porta.
Cosa fa il server?
Non fa altro che “ascoltare” (è così che si dice) una porta in attesa di eventuali connessioni in entrata. Tanto per farti un esempio, i server FTP ascoltano la porta 21, quelli Web la porta 80 e così via per tutti i servizi esistenti.
Cosa fa il client?
Il client è l’altra faccia del server, colui che utilizza i servizi forniti dal server. Per stabilire una connessione il client deve conoscere l’indirizzo IP del server e la relativa porta ascoltata.
Stabilita la connessione comincia lo scambio dati tra i due computer (client e server) che avviene seguendo delle precise regole, il protocollo applicativo, che dà un senso alle sequenze di byte scambiate.
Stabiliamo il protocollo applicativo …
Non è altro che un insieme di regole che dobbiamo determinare per far si che la comunicazione avvenga in modo giusto e senza problemi.
Ad esempio, di che natura deve essere il dato scambiato? In questo caso utilizzeremo il protocollo ASCII.
Utilizzando questo protocollo sorgono subito altri problemi: come trattare i caratteri accentati che non rientrano nel charset ASCII?
I comandi hanno questa forma:
COMANDO PARAMETRO1 PARAMETRO2
Come trattare quei dati che contengono degli spazi, come un semplice testo?
Questi due problemi (che dobbiamo risolvere per permettere poi agli utenti di chattare, nel nostro esempio) possiamo risolverli utilizzando l’URL Encoding che permette di aggiungere un “+” al posto dello spazio e sostituire i caratteri accentati con delle particolari sequenze.
Può sembrarti complicato, ma con le API di Java possiamo fare tutto questo davvero con pochissimo codice e con facilità.
Il paradigma sul quale faremo basare l’intero protocollo è quello del request-response: il client invia una richiesta e il server risponde. Ovviamente, ad una richiesta possono seguire diverse risposte da parte del server.
Come avrai già capito quindi, i comandi disponibili (e i parametri leciti) vengono stabiliti dal programmatore. Il server andrà ad ignorare e a restituire determinati messaggi quando non riconoscerà il comando inviato.
Ti faccio adesso qualche esempio di possibili comandi che andremo ad inviare, tanto per farti capire quello che ti ho detto finora.
Dal client: MESSAGE Questo+e+un+messaggio Dal server: BEGIN TEXAS-SERVER (Messaggio di saluto all’atto della connessione) END
Questi sono solo due di tutti i comandi che andremo a stabilire.
Tanto per anticiparti qualcosa, i primi comandi che svilupperemo saranno VERSION e EXIT.
Il primo non fa altro che restituire la versione dell’applicazione, l’altro richiede la chiusura della connessione e restituisce un messaggio di saluto.
La struttura quindi sarà qualcosa di questo tipo:
Dal client: VERSION Dal server: BEGIN TEXAS-SERVER 1.0 END Dal client: EXIT Dal server: BEGIN BYE END
Perfetto, adesso possiamo davvero passare alla pratica.
Per questione di pulizia dell’articolo, questa volta non includo tutto il codice all’interno, ma mi limito a spiegarlo. Il codice completo come sempre puoi trovarlo in fondo all’articolo. Ciascuna istruzione è spiegata all’interno del codice stesso tramite i commenti.
Eccoti spiegate le tre classi essenziali per poter costruire un architettura client/server in JAVA col supporto del multithreading.
L’intero modulo in generale funziona in questo modo:
Server server = new Server(); server.init(porta); ---------------------- Server.destroy();
La classe Server
Cosa viene fatto qui? Quali sono i moduli che la classe offre?
Il metodo init(porta)
Questo è il metodo base, il metodo che inizializza il server. Possiamo avere due tipi di chiamate al metodo, una in cui indichiamo una porta arbitraria, l’altro in cui non indichiamo alcuna porta e verrà usata quindi quella impostata per default dal codice.
Anzitutto viene creato un oggetto Java (ServerSocket) che è in grado di attendere e ricevere le connessioni TCP. Dopo viene chiamato l’oggetto Thread (da questo dipende il supporto del multithreading) che permette di eseguire su un thread parallelo la routine di istruzioni espressa dal metodo run().
Il metodo run()
E’ formato da un ciclo che ad ogni giro attende che un client si connetta al server. Il metodo accept() di ServerSocket ne ferma l’esecuzione fin quando non avviene un contatto da parte del client. Quando avviene, la connessione è restituita sotto oggetto Socket. L’oggetto Socket a sua volta serve ad istanziare una seconda classe: Connection.
Tutte le connessioni vengono memorizzate in un ArrayList di nome connections che abbiamo dichiarato nel modulo Server. Così facendo siamo sempre a conoscenza di quanti e quali connessioni sono gestite. La connessione comunque viene automaticamente eliminata dall’ArrayList quando viene terminata la sessione.
Il metodo connectionTerminated(Connection connection)
Questo metodo serve a terminare la sessione di un solo client.
Il metodo destroy()
Serve a terminare tutte le sessioni, il Thread non è più in attesa di una connessione e tutte le connessioni vengono eliminate svuotando l’ArrayList.
La classe Connection
Anzitutto vengono definite tutte le variabili, principalmente quella del Server, del Socket, del Thread e del flag dello stato della connessione.
Il metodo Connection(Server server, Socket socket)
Prepara l’oggetto per la connessione, impostandone il server e il socket.
Il metodo init()
Crea ed avvia il Thread parallelo per il supporto al multithreading attraverso il flusso di istruzioni del metodo run().
Il metodo run()
Vengono stabiliti i canali di comunicazione di entrata e di uscita. Come abbiamo già visto è importante definirli per stabilire con quale protocollo devono viaggiare ed essere scambiate le comunicazioni. Vengono quindi stabilite le sorgenti (stream) di input e di output, viene stabilita la natura di questi messaggi (stringhe codificate in ASCII) e per comodità vengono utilizzati BufferedReader e BufferedWriter, che hanno la capacità di leggere il testo riga per riga attraverso il metodo readLine().
Il metodo release()
Serve a rilasciare le risorse occupate quando il termine della connessione è stato riscontrato e non forzato tramite l’utilizzo del metodo destroy().
Quindi viene eseguito quando la chiusura della connessione avviene a causa di un guasto della rete oppure quando è il client a chiudere la connessione.
Il metodo readRequest(BufferedReader reader)
Si occupa di leggere il messaggio inviato dal client, elaborarlo ed inviare una risposta da parte del server.
Essenzialmente si tratta di leggere il comando, convertirlo tramite appositi metodi in UTF-8 in modo tale da leggere gli spazi, i caratteri speciali etc…
Il metodo writeResponse
In questo metodo viene composto il vero e proprio corpo della risposta del server. Come abbiamo concordato in precedenza, la risposta comincia con BEGIN, seguita dal messaggio e termina con END.
Il metodo handleRequest()
Qui vengono elaborate le richieste riconoscendo i vari comandi impartiti dal client. Come abbiamo già detto, gli unici comandi riconosciuti in questa versione sono VERSION ed EXIT. Tutti gli altri comandi causano una risposta UNKNOWN proprio perché non riconosciuti. Torneremo su questa classe nei prossimi articoli per creare tutta una serie di comandi riconosciuti per rendere il nostro programma utile allo scopo.
Oltre alle tre classi principali che poi rappresentano il cuore dell’intero funzionamento Server, c’è la classe GUI (Graphic User Interface) che serve ad avere un interfaccia grafica per avviare o arrestare il server. I metodi e le istruzioni presenti riguardano solamente il lato grafico del problema, quindi non le andremo ad analizzare.
Comunque, in sostanza vengono utilizzate le API grafiche Swing di Java.
COME TESTARE IL PROGRAMMA
Perfetto. Abbiamo finito ed il nostro server è già funzionante e pronto per essere testato.
Per farlo apri il prompt dei comandi e digita “telnet localhost 63301”.
Così facendo stabilisci una connessione con te stesso (dichiarata da localhost) attraverso telnet (attento che sia abilitato) sulla porta 63301, o su qualsiasi altra porta hai avviato il tuo server.
Stabilita la connessione riceverai subito il messaggio di saluto e provando i comandi che abbiamo concordato riceverai le risposte desiderate.
Ecco finalmente il link da cui puoi scaricare l’intero codice, inclusi tutti i commenti relativi (molti dei quali però non sono i miei):
Architettura Client/Server in Java (5,2 KiB, 33 hits)
You do not have permission to download this file.

Parte 1 – Come realizzare un’applicazione Client-Server in Java, un esempio col TexasHold’em…
Quando cominciai a sviluppare le mie prime applicazioni in Java, non pensavo nemmeno lontanamente che sviluppare delle applicazioni che facessero dialogare un client con un server, che sfruttassero il……
Parte 1 – Come realizzare un’applicazione Client-Server in Java, un esempio col TexasHold’em…
Quando cominciai a sviluppare le mie prime applicazioni in Java, non pensavo nemmeno lontanamente che sviluppare delle applicazioni che facessero dialogare un client con un server, che sfruttassero il multithreading e che permettessero ad un numero arb…
scusa ma tutto questo si fà su blocco note?
Ciao, in teoria molti linguaggi di programmazione possono essere scritti in blocco note. Ma per questione di comodità vengono utilizzati gli ambienti di sviluppo e ne sono diversi, io utilizzo Eclipse.
P.S. Ho un problema con il download dei file quindi quindi chiedo scusa se al momento non è possibile scaricare nulla.
ciao scusami se la disturbo ma anche noi stiamo realizzando il pocker come caso di studio per reti ma abbiamo delle difficoltà nella stesura del codice; Vorrei chiederle se è possibile pubblicare i file del Vostro caso per peterli esaminare e ottenere chiarimenti.La ringrazio e distinti saluti
scusami anche se provo non riesco a farlo funzionare mi puoi dare il codice?
Salve, ho difficoltà a scaricare il codice in quanto al posto del link compare il messaggio: “You do not have permission to download this file.” . Cosa devo fare per scaricarlo? Grazie mille in anticipo