Vous devez créer deux programmes client et serveur simples qui vont servir de base de travail : Le serveur ne traite qu'un client à la fois, le serveur et le client n'échangent qu'un message chacun. Le scénario est le suivant :
Ø Le programme serveur ouvre une socket d’écoute et attend une connexion. Dès qu’un client se connecte, il lui envoie une phrase, puis attend une réponse, puis ferme la connection et se remet en attente.
Ci-dessous le code complet de ce serveur :
import java.io.*;
import java.net.*;
class ServeurSimple
{
public static void main( String [] args )
{
int port = 1973;
ServerSocket SocketServeur = null;
Socket socket = null;
int client = 0;
if( args.length == 1 )
port = Integer.parseInt( args [ 0 ] );
try // 5 personnes en attente maxi
{
SocketServeur = new ServerSocket( port, 5 );
}
catch( IOException e )
{
System.err.println( "Impossible de créer un ServerSocket" );
return;
}
System.out.println( "Serveur à l'écoute sur le port :" + port );
while( true )
try
{
socket = SocketServeur.accept();
client++; // un client s'est connecté
PrintWriter out = new PrintWriter( socket.getOutputStream() );
BufferedReader in = new BufferedReader(
new InputStreamReader( socket.getInputStream() ) );
out.println( "Je suis le serveur : quelquechose à declarer?" );
out.flush();
String reply = in.readLine();
System.out.println( "Le client no. " + client + " m'a dit : " + reply );
}
catch( IOException e )
{
System.err.println( "Erreur : " + e );
}
finally
{
try
{
socket.close();
}
catch( IOException e ) {}
}
}
}
Ecrire le programme client (classe « ClientSimple » ) correspondant (50 lignes maximum) :
Ø Il recoit en paramètre l’adresse IP et le port du serveur.
Ø il crée une socket TCP pour se connecter sur la machine/port du serveur, puis affiche à l’écran le texte qu’il a reçut du serveur, puis envoie une réponse au serveur.
Ci-dessous le début du code du programme principal de ce client :
public static void main( String [] args )
{
InetAddress hote = null;
int port = 1973; // par défaut
Socket socket = null;
try
{
if( args.length>=1 )
hote = InetAddress.getByName( args[0] );
else
hote = InetAddress.getLocalHost();
if( args.length==2 )
port = Integer.parseInt( args[1] ) ;
}
catch(UnknownHostException e) {}
...
Créer une classe Serveur Multi-thread nommée « ServeurMultiClient » qui pourra gérer simultanément un nombre illimité de clients : Chaque client a sa propre soquette de connexion et tourne dans un thread dédié.
ci-dessous les modifications à apporter dans le programme principal du serveur de l’exercice 1:
while (true)
{
try {
socket = SocketServeur.accept();
client++;
//on le passe a un nouveau thread et on le demarre
ThreadClient neo= new ThreadClient (socket, client);
neo.start();
}
catch (IOException e)
{
System.err.println( "Erreur : " + e );
}
}
Comme on le voit, une boucle crée un nouveau Thread (classe « ThreadClient ») à chaque nouvelle connexion et lui passe le socket de connexion. Créer cette classe de thread dédiée à chaque client.
Son programme principal sera de :
1. envoyer un premier message amical d'acceuil
2. Attendre une phrase de réponse du client
3. Faire une pause d’une seconde
4. Envoyer une phrase au client en utilisant la méthode message_suivant()
5. Boucler en revenant à l’étape 2
6. Sortir de la boucle si la réponse du client est vide ou si le client s’est déconnecté ( on a une exception )
7. Penser à ajouter une méthode « protected void finalize() » qui fermera proprement socket et streams.
Ci-dessous le début du code :
public class ThreadClient extends Thread {
private Socket socket = null;
public int clientNo;
public int reqcount=0;
PrintWriter out;
BufferedReader in;
public ThreadClient(Socket socket, int no) {
super("ThreadClient");
this.socket = socket;
this.clientNo = no;
...
}
private String message_suivant()
{
reqcount++;
switch(reqcount%5)
{
case 0: return new String("Marrakech est une ville magnifique.");
case 1: return new String("La medina de Fes est splendide au couchant.");
case 2: return new String("Les montagnes de l'Atlas sont impressionnantes.");
case 3: return new String("La place Jamaa alfna est au centre de la ville.");
case 4: return new String("Les cotes du Maroc valent le coup d'oeil.");
}
return new String("ca n'arrive jamais");
}
void FaireUnePause()
{
try
{
System.out.println("pause d'une seconde");
Thread.currentThread().sleep(1000);
}
catch(InterruptedException e) {}
}
public void run() {
...
CLIENT : Se baser sur le client de l’exercice 1 pour créer la classe « ClientExo2 » pour que les clients restent connectés au serveur pour 10 echanges d’affilé, en affichant à l‘écran les phrases du serveur et en répondant à chaque fois. Le programme principal de chaque client sera :
1. recevoir le message du serveur
2. incrémenter la variable « compteur » (le nombre d’échanges)
3. répondre par : "Je suis le client "+hote+" et j'ai fait "+compteur+" appels" (« hote » est défini dans l’exercice 1)
4. faire une pause de 2 secondes
5. boucler à l’étape 1 dix fois
6. Terminer par un envoie d’une phrase vide ""
Tester le serveur en lancant plusieurs clients simultanément (au moins 2).
Modifier le programme client pour qu’il affiche les réponses du serveur de l’exercice 2 dans une fenetre AWT. Il donnera aussi la possilité de saisir du texte dane une zone de saisie qu’il enverra au serveur.
La fenêtre contient deux zones, l'une en haut pour afficher les phrases qui arrivent du serveur (un objet TextArea), l'autre en bas pour taper des phrases qui seront envoyées au serveur (un objet TextField).
Indications : Ø Pour écrire dans un TextArea : methode append(String) Ø Pour lire dans un TextField : méthode String getText()
|
Code principal pour créer la fenetre :
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
class ClientFenetre extends Frame implements Runnable,ActionListener
{
TextArea Output;
TextField Input;
Socket socket = null;
BufferedReader in ;
PrintWriter out ;
public ClientFenetre(InetAddress hote, int port)
{
super("Client en fenetre");
// mise en forme de la fenetre (frame)
setSize(500,700);
setLayout(new BorderLayout());
add( Output=new TextArea(),BorderLayout.CENTER );
Output.setEditable(false);
add( Input=new TextField(), BorderLayout.SOUTH );
Input.addActionListener(this);
pack();
show();
Input.requestFocus();
// ajout d'un window adapter pour reagir si on ferme la fenetre
addWindowListener(new WindowAdapter ()
{ public void windowClosing (WindowEvent e)
{
setVisible(false); dispose(); System.exit(0);
}
}
);
... (ouvrir la connexion sur le host/port du serveur)
Thread t=new Thread(this);
t.start();
}
public void run ()
{
// boucle qui receptionne les messages du serveur
// et les affiche dans le textarea
}
public void actionPerformed (ActionEvent e)
{
if (e.getSource()==Input)
{
String phrase=Input.getText();
... (envoie au serveur)
// efface la zone de saisie
Input.setText("");
}
}
protected void finalize()
{
// Fermer ici toute les soquettes
}
public static void main(String[] args)
{
InetAddress hote = null;
int port = 1973; // par défaut
Socket socket = null;
... ( gestion des paramètres )
ClientFenetre chatwindow = new ClientFenetre(hote, port);
}
}
Faire les modifications necessaire pour ce client puisse se connecter au serveur de l’exercice précédent.
Le pricncipe d’un IRC est qu’un ensemble de clients, tous connectés au même serveur, puisse dialoguer. c'est à dire que chaque client voit tout ce que disent les autres clients, et peut lui-même parler. Pour distinguer les clients les uns des autres, chacun porte un pseudonyme, qui est repris en entete de chaque message. Exemple :
PSEUDONYME> ceci est un message
Le role du serveur IRC est donc de recevoir en simultané les messages de tous les clients et de les renvoyer vers tous les autres clients. Convention : Chaque client doit fournir à la connexion au serveur un Pseudonyme ( « NickName » dans le monde IRC).
Le serveur doit :
Ø Attendre des connexions
Ø Créer un nouveau Thread pour chque nouneau client
Ø Créer et maintenir une liste des clients connecté (voir ci-dessous)
Ø Indiquer à tous les clients les nouveaux connectés et ceux qui se sont déconnectés
Le thread serveur (chargé d’un seul client) doit :
Ø Envoyer un message de bienvenue
Ø Envoyer la liste de tout les connectés
Ø Faire passer chaque message provenant du client au serveur, qui à son tour l’envoi à tous les clients connectés
Pour créer la liste des utilisateurs, on va utiliser la classe java.util.Vector. Elle possède 4 méthodes utiles :
Ø addElement(Object) : Ajouter un objet
Ø removeElement(Object) : Supprimer un objet
Ø int size() : nombre d ‘éléments
Ø Object elementAt(int position) : Retourner l’objet à la postion donnée
ci-dessous un extrait du code du serveur :
class ServeurIRC
{
Vector V;
public static void main (String args[])
{
int port = 1973;
if( args.length == 1 )
port = Integer.parseInt( args [ 0 ] );
new ServeurIRC(port);
}
public ServeurIRC (int port)
{
V=new Vector();
try
{
ServerSocket server=new ServerSocket(port);
while (true)
...
}
catch (Exception e)
{
System.err.println(e);
}
}
synchronized public void EnvoyerATous (String s)
{
for (int i=0; i<V.size(); i++)
{
ThreadClient c=(ThreadClient)V.elementAt(i);
c.Envoyer(s);
}
}
public void ajouterClient(ThreadClient c)
{
... (ajout d’un client dans le vecteur V)
}
synchronized public void EnvoyerListeClients (PrintWriter out)
{
... ( envoyer dans “out” le nom de tous les clients du vecteur V)
}
synchronized public void SupprimerClient (ThreadClient c)
{
... (suppression d’un client dans le vecteur V)
}
}
class ThreadClient extends Thread
{
BufferedReader In;
PrintWriter Out;
ServeurIRC serveur;
String nom="???";
public ThreadClient (Socket socket, ServeurIRC s)
{
... (initialisation des propriétés)
start();
}
public void run ()
{
... (boucle principale. Utiliser EnvoyerATous())
}
public void Envoyer(String s) // Envoie vers le client
{
Out.println(s);
Out.flush();
}
public String nom() { return nom; }
}
Modifier également le client GUI de l’exercice 3 pour que celui-ci commence par envoyer au serveur un pseudonyme, qu’il recevra en parametre de la ligne de commande : la classe se nommera « ClientIRC ».
Ø Ajouter du code sur le serveur pour vérifier que les nouveaux arrivants n’ont pas un pseudonyme déjà utilisé
Ø Ajouter un bouton quitter sur la fenetre client et ajouter le code pour le gérer.
Ø Ajouter des commandes :
· List
· Clear
· Exit ou Quit ou Leave ou Bye
· Msg : enoyer un message privé