Blue Flower

Chercher

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) {}
    }
  }
}


Code du programme client: