Per
l'impaziente...
Potete
provare subito gli esempi di questa volta: scaricateli da qui,
compilate il tutto e lanciate i due file .bat ("data.bat" per i
tipi primitivi e "obj.bat" per gli oggetti). Trovate un paio di screenshot
più avanti. Anche se in realtà non vedrete un gran chè
di speciale, dato che per apprezzare il risultato bisogna leggersi il resto
dell'articolo :-)
Attenzione,
per chiudere i programmi bisogna battere "^C" sul corrispondente prompt
di DOS, non ho gestito il WindowEvent di chiusura della finestra (si lascia
per esercizio... :-)
Una nota iniziale
In
questo articoletto, dedicato alla pratica, non mi soffermerò sui
dettagli implementativi, nel senso che non spiegherò il codice riga
per riga. E' un sorgente così banale e soprattutto è così
simile al codice che abbiamo già visto in precedenza che mi sembrerebbe
offensivo... Vi racconterò soltanto quali sono le modifiche sostanziali...
Se
vi sarete incuriositi al termine della lettura, vi consiglio di seguire
questa serie di puntatori sicuramente interessanti:
-
la sezione
"networking" del Java Tutorial
-
gli articoli
pubblicati su MokaByte 35 (novembre 1999) e successivi
-
il documento
sulla "serialization specification" scaricabile dal sito java.sun.com
-
la parte
sulla serializzazione del mio articolo su "Serializzazione&RMI" di
MB Luglio-Agosto 1999
I tipi primitivi
Rispetto
all'esempio della volta scorsa il sorgente non ha cambiato struttura, se
lo guardate attentamente vedrete che ho sostituito tutti gli Stream (sia
Input che Output) con dei DataStream.
L'effetto
di questa modifica è che ora posso usare dei metodi più intelligenti
del
semplice write di un singolo byte o di un array. Infatti i DataStream,
incapsulando gli Stream di base, offrono le funzioni per trasmettere tutti
i dati di tipo primitivo più le stringhe.
"Incapsulando"
in questo caso vuol dire semplicemente che un DataStream si può
istanziare solo avendo già a disposizione uno stream. Infatti per
istanziare un DataStream dovete scrivere (nel caso delle socket):
new
DataOutputStream(s.getOutputStream());
Le
due classi DataStream (DataInputStream e DataOutputStream) sono accoppiate,
infatti hanno i metodi per così dire simmetrici:
DataOutputStream |
DataInputStream |
writeInt |
readInt |
writeFloat |
readFloat |
writeUTF
(questo
è quello per le stringhe) |
readUTF |
... |
... |
Cioè
se uso un metodo per scrivere, devo usare il suo gemello per leggere, altrimenti
mi becco una sana eccezione a runtime.
A
questo punto il nostro ChatServer è diventato più intelligente,
riesce a trasmettere intere stringhe (potete divertirvi voi a trasmettere
altri tipi di dati) invece dei singoli caratteri, per cui i vari utenti
della chat non si intersecano più.
Perdo
qualcosa? Purtroppo sì. Dal momento in cui vi sganciate dal puro
canale "a singolo byte" (lo stream puro così come ve lo fornisce
la Socket) non potete più utilizzare un client non-java. Infatti
ho dovuto realizzare i mini-client per poter provare il tutto (se non ci
credete provate a collegarvi al nuovo server con un semplice telnet e guardate
cosa accade). Questo accade perchè il DataStream (come anche l'ObjectStream
che vedremo tra un attimo) trasmette le informazioni usando un suo protocollo,
per cui i casi sono due: o lo simulate "a mano" (se usate un telnet) o
scrivete un programma che "parla" lo stesso protocollo...
I tipi classe
(spedire oggetti)
Con
la stessa logica è possibile mandare un intero oggetto, basta costruire
un ObjectOutputStream sul solito stream di base con:
new
ObjectOutputStream(s.getOutputStream());
E
da quel momento è possibile usare writeObject() per spedire un oggetto
(quasi) qualunque, anche complesso (cioè che contenga altri oggetti
a sua volta).
Nel
nostro esempio ho costruito un oggetto Message (da spedire avanti e indietro)
con un po' di dati dentro (qui ho tolto i metodi rispetto al sorgente che
trovate nel file zip):
public
class Message implements Serializable{
Date data;
String nome,msg;
}
Cosa
ha di strano questa classe? Che implementa l'interfaccia Serializable.
Gli oggetti che si possono spedire attraverso un ObjectOutputStream devono
implementarla, altrimenti otterrete la solita eccezione a runtime |