Ces deux termes désignent des notions très distinctes mais elles sont ici rassemblées car sont souvent indissociables dans les applications client-serveur Java. Un flux est un objet gérant le transport d'un objet d'un "point" à un autre. Il permet ainsi la lecture ou l'écriture. Par exemple, un objet peut être écrit sous la forme d'un fichier sur un support de stockage. Dans le cadre de ce tutorial, nous ne présenterons que les flux les plus courants. Les sockets quant à elles sont des objets de connexion réseau.
Il existe principalement deux types de flux:
Les flux traitant des caractères avec les Reader en entrée et les Writer en sortie
Les flux traitant des suites d'octets avec les InputStream en entrée et les OutputStream en sortie.
Les objets java peuvent facilement être stockés de façon persistante dans des fichiers. Pour cela, il faut que la classe de ces objets implémente l'interface Serializable ainsi que toutes ses variables d'instance. Les types primitifs sont tous sérialisables. Dès lors, il suffit d'utiliser un ObjectOutputStream pour sérialiser un objet ou un ObjectInputStream pour dé-sérialiser. Exemple: Nous voulons stocker de façons permanente une instance de ExempleSerialisable sous la forme d'un fichier '/tmp/toto' :
1- Instancier un flux de sortie sur fichier:
FileOutputStream fos=new FileOutputStream("/tmp/toto");
2- Instancier un ObjectOutputStream qui vient se "brancher" sur le flux de fichier précédant:
ObjectOutputStream oos=new ObjectOutputStream(fos);
3- Écrire dans le flux:
ExempleSerialisable es=new ExempleSerialisable();
oos.writeObject(es);
4- Fermer le flux
fos.close(); ( le flux oos sera lui aussi fermé )
Nous obtenons alors un fichier '/tmp/toto' contenant un instance persistante de notre objet 'es'.
Pour le dé-sérialiser, c'est-à-dire obtenir un objet Java à partir d'un tableau d'octets, il suffit de procéder à l'opération inverse:
1- Instancier un flux FileInputStream sur le fichier "/tmp/toto"
FileInputStream fis=new FileInputStream("/tmp/toto");
2- Instancier un flux lisant les objets java: un ObjectInputStream
ObjectInputStream ois=new ObjectInputStream(fis);
3- Lire l'objet, le transtyper et le stocker dans une référence de type ExempleSerialisable:
ExempleSerialisable es=(ExempleSerialisable)(ois.readObject());
4- Fermer le flux
fis.close(); ( le flux ois sera lui aussi fermé )
Dans toute communication entre deux machines, il y a un dialogue de type client-serveur. Le client est celui qui a initié le dialogue et le serveur celui qui était en attente de requête.
Coté serveur, un listener écoute à l'affût de toute demande. Il écoute sur un port spécifique. En effet, même si chaque machine possède une adresse IP unique, elle peut posséder une multitude de services différents ( http, telnet, sqlnet, netbios, ftp ....).
Coté client, une connexion avec l'un de ces listeners est réalisée via une socket qui pointe alors sur une socket serveur à une IP et un port donnés.
Il est possible d'écrire facilement une application client-serveur qui échange des données. Pour cela, il faut:
Instancier un listener coté serveur.
Instancier une socket coté client.
Instancier les flux d'entrée/sortie associés à la communication.
Ensuite, il est possible d'envoyer des données entre les machines.
Exemple: le serveur envoie l'objet de type String "ok" au client lors d'une connexion réussie.
Coté serveur
import
java.net.*;
import java.io.*;
class TestServer{
TestServer() throws Exception{
ServerSocket ssServer=new ServerSocket(80); //port d'écoute du
listener
Socket
sSocket=null;
while(sSocket==null){//on boucle en attendant une connexion
cliente
sSocket=ssServer.accept();
}
OutputStream
os=sSocket.getOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(os);
oos.writeObject(new String("ok"));
}
}
Coté client
import
java.net.*;
import java.io.*;
class TestClient{
TestClient() throws Exception{
Socket sSocket=new Socket("213.84.64.18",80);
InputStream is=sSocket.getInputStream();
ObjectInputStream ois=new ObjectInputStream(is);
System.out.println((String)(ois.readObject()));
}
}