1. Un exemple de Serveur Jouet Monoclient

1.1. Une petite classe

On peut utiliser le code Java suivant, on commence par quelques déclarations standard, les champs de Serveur_jouet sont statiques.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  import java.net.*;
  import java.io.*;
  import java.util.*;

  public class Serveur_Jouet {
  // FINISH string
  private static final        String Finish=""+(char) 4;; // caractère de fin ctrl-d
  private static ServerSocket  gestionnaire_de_connexion;  // Objet gerant les sockets
  private  final static int port = 12000;         /* Port d'écoute */
  private static Socket ma_connection;      /* file instanciée */

1.2. La creation du ServerSocket

Le main de la classe commence par créer le gestionnaire de sockets. Ici dès sa création l’objet gestionnaire_de_connexion qui est un ServerSocket va attendre et accepter les connexions internet sur le port 12000, les clients arrivants sont tous mis en attente en attendant que nous les traitions. Le ServerSocket rst donc un obket assez complexe géré par la JVM, il s’exécute en quelque sort en tâche de fond sans que nous ayons à nous en préoccuper.

Dans le bloc qui suit on crée juste le serveur de socket (que la jvm gère) .

public static void main(String[] args) {
     /* Creation du gestionnaire de socket   */
     try{
         gestionnaire_de_connexion  = new ServerSocket(port);
         System.out.println("Serveur Jouet lancé sur " + (port)  );}
     catch (IOException e) {
         System.out.format(" Cannot create to the server, port %d may be busy\n", port);
          System.exit(-1);     }

1.3. L’accueil d’un Client

Maintenant on attend un client et on le sert.

  1. La ligne 4 est importante car elle est bloquante, elle signifie que l’on attend que le ServerSocket nous délivre un client via une Socket. Si aucun client n’est en attente dans le ServerSocket notre fil de calcul va attendre.
  2. Les lignes 12 et 13 permettent de lire ce qui arrive sur la socket, il s’agit de notre input. La ligne 12 convertit le flux d’octets retourné par getInputStream en flux de caractères tandis que la ligne 13 permet d’utiliser un tampon de lecture ce qui améliore les performances.
  3. La ligne 17 permet de récupérer un flux sortant: getOutputStream retourne un flux de sortie et on l’entoure d’une PrintWriter, on va ainsi pouvoir écrire sur la socket comme sur une sortie classique.
  4. La ligne 21 envoie un message au client, on utilise le flux de sortie exactement comme la sortie standard. L’opération est asynchrone.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
     try{
         //  Attente d'une bloquante connexion
         System.out.println("waiting for connexion") ;
         ma_connection = gestionnaire_de_connexion.accept();

         // Connection recupérée, on determine l'ip et le port
         String c_ip = ma_connection.getInetAddress().toString() ;
         int c_port= ma_connection.getPort();
         System.out.format("client admis IP %s  sur le port %d\n", c_ip, c_port);

         // Recuperation du flux entrant  la socket (lecture)
         InputStreamReader isr = new InputStreamReader(ma_connection.getInputStream(), "UTF-8");
         BufferedReader flux_entrant = new BufferedReader(isr) ;
         System.out.println("Mon Tampon de lecture est attache ");

         // Récupération de notre flux de sortie (écriture)
         PrintWriter ma_sortie = new PrintWriter(ma_connection.getOutputStream() , true);
         System.out.println("Mon Tampon pour ecrire  attache ");
         System.out.format("Pret à servir  IP %s  sur le port %d\n", c_ip, c_port);

         ma_sortie.format("Hello %s  sur le port %d,  Ready!\n" ,  c_ip, c_port );

1.4. La lecture des données envoyées par le client

Il ne reste plus qu’à lire en boucle les données transmises par le client, on effectue ici une lecture Bloquante.

  1. La ligne 1 signifie que l’on attend et bloque jusqu’à ce que le flux d’entrée contienne une ligne, Attention cette ligne lit aussi les données et les place dans message_lu, enfin on quitte la boucle si la connexion est defectueuse.
  2. En ligne 3 on vérifie si le message est une requête de de fin de connexion
  3. En ligne 9 on affiche juste les données recues avec le numéro de ligne sur la sortie standard
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
         while (   (message_lu = flux_entrant.readLine())    != null      ){
             // Si le client demande de terminer
                 if (message_lu.contains(Finish) ){
                     // on termine proprement
                     System.out.format ("[%s] recu, Transmission finie\n",message_lu);
                     ma_sortie.println("Vous etes VIRE");
                     terminer();
                 }
                 System.out.format( "[%d]--> [%s]]\n", line_num, message_lu);
                 line_num++;
         }

1.5. Un exemple de Code pour le serveur Monoclient.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import java.net.*;
import java.io.*;
import java.util.*;

public class Serveur_Jouet {
    // FINISH string 
    private static final  	String Finish=""+(char) 4;;

    private static ServerSocket  gestionnaire_de_connexion;  // Objet gerant les sockets
    private  final static int port = 12000;         /* Port d'écoute */
    /* Unique connecteur/socket du serveur */ 
    private static Socket ma_connection;      /* file instanciée */  

    public static void main(String[] args) {
	/* Creation du gestionnaire de socket   */ 
	try{
	    gestionnaire_de_connexion  = new ServerSocket(port);
	    System.out.println("Serveur Jouet lancé sur " + (port)  );}
	catch (IOException e) {
	    System.out.format(" Cannot create to the server, port %d may be busy\n", port);	 System.exit(-1);	  }
	try{
	    //  Attente d'une bloquante connexion  
	    System.out.println("waiting for connexion") ;
	    ma_connection = gestionnaire_de_connexion.accept();
	    
	    // Connection recupérée, on determine l'ip et le port
	    String c_ip = ma_connection.getInetAddress().toString() ;
	    int c_port= ma_connection.getPort();
	    System.out.format("client admis IP %s  sur le port %d\n", c_ip, c_port);  
	    
	    /* On Associe un tampon pour lire sur  le flux  connection  
	       Input streamreader permet de transformer le flux d'octets en flux de caracteres
	       le second argument et le type d'encodage des caractere --utf-8, isoXXXX etc ... */
	    InputStreamReader isr = new InputStreamReader(ma_connection.getInputStream(), "UTF-8");
	    // Une seconde encapsulation qui permet d'améliorer les perfomances en lisant par blocs -- pour les gros fichiers 
	    BufferedReader flux_entrant = new BufferedReader(isr) ; 
	    System.out.println("Mon Tampon de lecture est attache ");
	    
	    // Stream de sortie,  getOutputStream renvoie un Outputstream sur lequel on peut juste écrire des bit
	    // PrintWriter l'encapsule ce qui permet d'érire comme sur Sys
	    // le second parametre impose l option autoflush .. ce qui evite de faire de forcer l'envoi des messages partout
	    PrintWriter ma_sortie = new PrintWriter(ma_connection.getOutputStream() , true);
	    System.out.println("Mon Tampon pour ecrire  attache ");
	    
	    System.out.format("Pret à servir  IP %s  sur le port %d\n", c_ip, c_port);
	    ma_sortie.format("Hello %s  sur le port %d,  vous etes, pour faire simple, disons Admis\n" ,  c_ip, c_port );  
	    
	    String  message_lu = new String();
	    int line_num =0 ;
	    /* On lit une ligne dans le flux_entrant     La fonction readline est Bloquante
	       La condition du while fait diverses choses
	       elle attend que le client ai ecrit au moins une ligne
	       si la connection est  brisée ou fautive  ce message vaudra null et l'on quitera la boucle while    
	    */
	    while (   (message_lu = flux_entrant.readLine())    != null      ){
		// Si le client demande de terminer 
		    if (message_lu.contains(Finish) ){
			// on termine proprement 
			System.out.format ("[%s] recu, Transmission finie\n",message_lu);
			ma_sortie.println("Vous etes VIRE");
			terminer();
		    }
		    System.out.format( "[%d]--> [%s]]\n", line_num, message_lu);
		    line_num++;
	    }
	    // Si on est ici à priori le client à fermé la connection, sans envoyer finish (pex on peut tuer le processus telnet) 
	    System.out.println( "Client deconnecté, je termine\n" )  ;
	    terminer();
	    
	    
	}
	catch (IOException e) {
	    System.err.println(" Erreur de reception");
	    e.printStackTrace(); terminer();}
	
     }

    private static void terminer(){
	try{
			if (ma_connection != null) ma_connection.close();
			if (gestionnaire_de_connexion != null) gestionnaire_de_connexion.close();
	}
	catch (IOException e) {
	    e.printStackTrace();
	}
	System.exit(0);
    }

    
}