Applications en mode Client-Serveur

publicité
Université Paris Sud
Licence d’informatique/IUP2 - Année 2003/2004
TD PAC - Java n◦ 10
(Correction)
Applications en mode Client-Serveur
Exercice 1 On désire réaliser une application de type Serveur de fichiers en mode client-serveur.
1. Le Serveur
Pour cela on va créer une classe Serveur qui possèdera un attribut de type ServerSocket
qui sera initialisé dans le constructeur pour attendre des connexions sur un numéro de port,
éventuellement passé en ligne de commande (sinon on prendra comme port par défaut : 3456).
Lors de la connection d’un client, le serveur devra créer un nouveau thread pour traiter les
requètes en provenance de ce client. Bien sûr, pendant ce temps, le serveur devra continuer
d’attendre de nouvelles connection éventuelles.
Correction :
/*
* Fichier Serveur.java
*/
import java.io.*;
import java.net.*;
/**
* Serveur de connection pour les clients.
* Le choix du port de communication peut ^
etre passé en ligne de commande.
*/
public class Serveur {
// Constantes
private static final int PORT_PAR_DEFAUT = 3456;
// Attributs
private ServerSocket _sock;
// Constructeur
public Serveur (int port){
try {
// Creation du socket d’écoute
_sock = new ServerSocket (port);
for (;;) {
System.out.println ("Server: en attente ");
// Detétection d’une connection
Socket s = _sock.accept();
// Création d’un thread pour gérer la connection
(new Connexion(s)).start();
}
} catch (IOException e){
System.out.println("ServerSocket: " + e);
System.exit (1);
}
}
/** programme principal*/
public static void main (String [] args) {
1
int numero_de_port ;
// Choix du numero de port
if (args.length > 0)
numero_de_port = Integer.parseInt(args[0]);
else
numero_de_port = PORT_PAR_DEFAUT;
// Lancement du serveur
Serveur s = new Serveur (numero_de_port);
}
}
2. La gestion des connections
Nous considèrerons ici tout simplement qu’une requète est simplement une chaı̂ne de caractères
qui correspond
• soit à un nom d’un fichier a transférer (résidant sur la machine où tourne le serveur)
• soit à la commande fin qui provoque l’arrêt du serveur.
Le thread traitant les requètes sera implémenté par la classe Connexion. Son constructeur est
appelé avec le Socket issu du accept qu’a fait le serveur. Sa méthode run doit lire la requète
en provenance du client, la traiter, fermer la connexion, puis se terminer.
Correction :
/*
* Fichier Connection.java
*/
import java.io.*;
import java.net.*;
/**
* Thread qui gère une connection au serveur.
* On peut la faire attendre le temps qu’on veut,
* la planter éventuellement, le serveur reste à
* l’écoute des autres demandes de service.
**/
class Connexion extends Thread
{
// Attributs
/** le socket retourné par <tt>accept</tt> */
private Socket _sock;
// Constructeur
public Connexion (Socket s)
{
_sock = s;
}
/**
* Traitement d’une requète :<br>
* 1. on lit la requète<br>
* 2. si c’est <tt>‘fin’</tt> on termine le serveur<br>
* 3. sinon on espère que c’est le nom d’un fichier à transmettre<br>
* on le recopie octet par octet sur le socket.<p>
* A la fin (exception de fin de fichier) on ferme la connexion et on
* termine la thread.
*/
public void run (){
String requete = null;
try {
2
for (;;) {
BufferedReader in = new BufferedReader(
new InputStreamReader(_sock.getInputStream ()));
// lire la requète.
requete = in.readLine();
if (requete.equals ("fin")) {
System.out.println ("Arr^
et du serveur par un client");
System.exit (0);
}
// ouvrir le fichier correspondant.
// On utilise un DataInputStream car on ne connait pas la nature du fichier
DataInputStream din = new DataInputStream (new FileInputStream (requete));
System.out.println ("Ouverture du fichier : "+ requete);
// recopier le fichier (on s’en sortira par l’exception de fin de fichier).
DataOutputStream dout = new DataOutputStream (_sock.getOutputStream ());
for (;;)
dout.writeByte (din.readByte ());
}
} catch (FileNotFoundException e) {
System.out.println ("Error: " + e.getMessage ());
} catch (EOFException e) {
System.out.println ("Fichier : "+ requete + " Envoyé");
} catch (IOException e) {
System.out.println ("run: " + e);
}
try {
_sock.close ();
} catch (IOException e) {}
}
}
3. Le Client
Pour tester notre serveur de fichiers, on va écrire un autre programme qui va demander au serveur
de lui envoyer un fichier. Ce programme prend en argument (dans la ligne de commande)
• le nom de la machine correspondant au serveur
• le numéro de port sur lequel se connecter
• la requête (i.e. le nom du fichier à transférer ou bien ”fin”
Il est implémenté avec la classe Client qui utilisera un objet de type Socket pour se connecter
au serveur. Le fichier récupéré est enregistré localement sous le même nom que celui utilisé dans
la requête.
Correction :
/*
* Fichier Client.java
*/
import java.io.*;
import java.net.*;
public class Client{
// Constantes
private static final int PORT_PAR_DEFAUT = 3456;
// Attributs
private Socket _sock;
// Constructeur
public Client (String machine, int port, String requete){
DataOutputStream dout=null;
3
try {
// Connection au serveur
_sock = new Socket (machine, port);
// Envoi de la requète au serveur
PrintWriter out = new PrintWriter(_sock.getOutputStream ());
out.println(requete);
out.flush();
if (! requete.equals("fin")) {
// attente de la réponse du serveur
DataInputStream din = new DataInputStream(_sock.getInputStream ());
dout = new DataOutputStream(new FileOutputStream(requete));
// on sortira de la boucle par l’exception de fin de fichier
for (;;) {
dout.writeByte(din.readByte ());
}
}
}
/** sortie ’normale’ de la boucle for */
catch (EOFException e) {
try {
dout.close();
_sock.close();
} catch (IOException ex) {
System.err.println(ex);
}
}
/** véritable erreur (pas de serveur, etc...) */
catch (IOException e) {
System.err.println (e);
try {
_sock.close ();
} catch (IOException ex) {}
}
}
public static void main (String [] args)
{
int nb_args = args.length;
String machine ;
int numero_de_port = PORT_PAR_DEFAUT;
String requete ;
if ((nb_args < 2) || (nb_args > 3)) {
System.err.println ("Usage: java client <machine> [<port>] <requete>");
}
else {
machine = args[0];
if (nb_args == 2)
requete = args[1] ;
else {
numero_de_port = Integer.parseInt(args[1]);
requete = args[2];
}
Client c = new Client (machine, numero_de_port, requete);
}
}
}
4
Téléchargement