Utilisation des Sockets
L'objectif des exemples ci-dessus est d'illustrer l'utilisation des sockets. Dans des programmes client/serveur, on dispose d'un serveur qui propose un service et d'un client qui va consommer ce service. On a vu dans la définition des Sockets selon le système UNIX de BSD, que les Sockets sont une généralisation des tubes de communication entre processus; comme dans le monde UNIX les tubes sont des cas particuliers de fichier, les sockets en généralisant la notion de tube, sont implémentés comme des fichiers particuliers.
Ici notre but est de montrer à travers des exemples, les mécanismes de communication qui sont mis en route par l'utilisation des sockets. Donc on ne tient pas compte de l'affichage mais dans la partie amélioration, on verra comment faire pour afficher dans des fenêtres bien soignées les résultats des applications.
Présentation
Premiers programmes client/serveur
Pour illustrer l'emploi des Sockets, étudions l'exemple d'un serveur echo; ce serveur renvoie en echo le texte saisi au clavier. Nous étudierons ensuite un client qui utilise ce serveur. Voyons le code source du serveur echo :
ServeurEcho.java import java.io.*; import java.net.*; class ServeurEcho { public static void main(String [] argv) { try { // On crée une socket serveur ( le port est passé en argument) ServerSocket s_ecoute = new ServerSocket(Integer.valueOf (argv[0]).intValue()); System.out.println("Serveur démarré sur la socket d'écoute " + s_ecoute); // Attente d'une connexion Socket s_service = s_ecoute.accept(); // Une connexion a été ouverte System.out.println("Ouverture de la connexion sur la socket de service " + s_service); // On crée un flot d'entrée et un de sortie BufferedReader entree = new BufferedReader (new InputStreamReader ( s_service.getInputStream())); PrintWriter sortie = new PrintWriter ( new OutputStreamWriter ( s_service.getOutputStream())); // On boucle sur l'echo while (true){ // On lit une ligne en entrée String buff = entree.readLine(); // On quitte si c'est égal à "FIN" if (buff.equals("FIN")) break; // On renvoie l'echo au client sortie.println(buff); sortie.flush(); } // Le client s'est déconnecté System.out.println("Fermeture de la connexion..."); // On ferme la socket de service s_service.close(); } catch (Exception e) { return; } } }
Analyse du code
Tout d'abord, on commence par importer (import java.net.*;) les classes du package dans lequel sont définies les sockets, à savoir java.net, ainsi que celui correspondant aux classes gérant les entrées/sorties java.io (import java.io.*;).
On crée ensuite une socket serveur qui est la socket d'écoute, sur le port indiqué (ce sera saisi sur la ligne de commande).
Ensuite, on attend la réception d'une demande de connexion sur cette socket; cela est fait via la méthode bloquante accept() qui retourne une Socket dite socket de service, via laquelle le serveur et le client vont pouvoir dialoguer. Nous définissons ensuite deux flots, l'un en entrée et l'autre en sortie, avant d'entrer dans une boucle infinie qui constitue le cœur du serveur echo : elle copie le texte reçu du client dans une chaîne et le renvoie tel quel au client. La communication se termine quand le client envoie la chaîne "FIN" au serveur.
Précisons que la méthode flush() de la classe PrintWriter a pour effet d'envoyer immédiatement le texte voulu sur la socket. Si Voyons maintenant le code source du client correspondant:
Code source du client correspondant:
//ClientEcho.java import java.io.*; import java.net.*; class ClientEcho { public static void main (String argv[]) { try { // On crée un objet InetAdress sur l'interface de loopback InetAddress adr = InetAddress.getByName("127.0.0.1"); // On crée une socket Socket s = new Socket (adr, Integer.valueOf (argv[0]).intValue()); System.out.println("Socket crée"); // On crée 2 flots d'entrée et un de sortie DataInputStream saisie = new DataInputStream(new BufferedInputStream (System.in)); DataInputStream entree = new DataInputStream (new BufferedInputStream (s.getInputStream())); PrintStream sortie = new PrintStream ( new BufferedOutputStream(s.getOutputStream())); // On envoie du texte au serveur et on // affiche l'echo reçu while (true) { // Saisie du texte à envoyer au serveur System.out.println("Texte ? "); String buff = saisie.readLine(); // Si on entre "FIN", on quitte if (buff.equals("FIN")) break; // On envoie le texte saisi au serveur sortie.println(buff); sortie.flush(); // On affiche l'écho du serveur String buff2 = entree.readLine(); System.out.println(buff2); } // On ferme la socket s.close(); } catch (Exception e) { return; }; } }
Analyse du code:
Dans la fonction main(), on crée un objet de type InetAddress qui va contenir l'adresse IP de la machine sur laquelle tourne le serveur. Ici, on suppose que le client et le serveur sont sur la même machine; on indique donc l'adresse de loopback, via la méthode getByName(). On crée ensuite une socket de communication vers le serveur, en spécifiant comme numéro de port celui indiqué sur la ligne de commande. Bien sûr, cela doit être le même numéro que pour le serveur.
Ensuite, on crée trois flots, deux en entrée (un pour la saisie au clavier, l'autre pour lire la réponse du serveur) et un en sortie (pour envoyer le texte au serveur).
Nous entrons alors dans une boucle, telle que celle que nous avions créée pour le serveur, dans laquelle s'effectuent trois opération successives :
lecture de la chaîne à envoyer, à partir du clavier (String buff = saisie.readLine();),
envoi de cette chaîne au serveur, sauf si elle est égale à "FIN", signifiant que l'on veut fermer la connexion avec le serveur (sortie.println(buff);)
lecture de l'écho envoyé par le serveur (String buff2 = entree.readLine();) et affichage de celui-ci à l'écran. Enfin, on ferme la socket quand on sort de la boucle.
deuxièmes programmes client/serveur
Voici un exemple de code permettant de transférer un objet en socket : public class Clientstream { /** * */ public Clientstream() { File[] fs = new File[3]; fs[0] = new File("c:/TestFiles/Fichier1.txt"); fs[1] = new File("c:/TestFiles/Fichier2.txt"); fs[2] = new File("c:/TestFiles/Fichier3.txt"); FileIO files = new FileIO(fs); Socket socket; try { socket = new Socket("adresse du server",port_du_server); OutputStream os = socket.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(files); oos.close(); os.close(); socket.close(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { new Clientstream(); } }
Ici un objet de type "File" sera envoyé vers un server.(Voir exemple suivant pour le server) Voici la classe "FileIO" correspondant à l'objet envoyé:
troisième programme
public class FileIO implements Serializable{
/**
* Comment for serialVersionUID
*/ private static final long serialVersionUID = 1L; private File[] file; private int lenght; /** * @param files */ public FileIO(File[] file) { super(); this.file = file; this.lenght=file.length; } public int lenght() { return lenght; } /** * @return Returns the files. */ public File getFile(int i) { return file[i]; } }
Quatrième exmple: des programmes client/serveur
L'exemple complet suivant est celui d'un serveur qui renvoie la chaine de caractère que lui envoie un client, précédée par "Je répète:". Le client lit l'entrée standard et l'envoie au serveur. On l'arrête en tapant "bye". A peu de chose près ce programme suit le même cheminement que le premier exemple sur le serveur
echo.
Code du programme serveur:
import java.io.*; import java.net.*; public class Serveur { public static int PORT=5000; public static String MACHINE="vlaminck.prism.uvsq.fr"; public static void main(String [] a) { ServerSocket serverSocket=null; // Création de la socket de connexion try { serverSocket=new ServerSocket(Serveur.PORT); } catch(IOException e) { // échec de la création System.exit(-1); } try { System.out.println("En attente..."); // En attente de connexion Socket clientSocket=serverSocket.accept(); // Un client s'est connecté... System.out.println("Connexion d'un client"); try { // Création des flots d'entrée/sortie PrintWriter out=new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in= new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String entree,sortie; // Envoi d'un premier message lors de la connexion out.println("Bienvenue !"); while (true) { // Lecture de ce qu'envoie le client entree=in.readLine(); if(entree!=null) System.out.println("Client>"+entree); // Si c'est bye, alors on renvoie et on quitte if(entree.equals("bye")) { sortie="bye"; out.println(sortie); break; } else { // Sinon on renvoie sortie="Je répète:"+entree; System.out.println("Serveur>"+sortie); out.println(sortie); } } in.close() ; out.close(); } catch (IOException e) { } finally { clientSocket.close(); } } catch (IOException e) { } finally { try { serverSocket.close(); } catch (IOException e) {} } } }