Vous êtes sur la page 1sur 26

TP Sockets

Serveur multi-protocole transport (TCP et UDP)


TCP concurrent (multi thread)
Clients simples
But du TP
!  Application Client/Serveur multi-protocole
transport
!  TCP/UDP
!  Quelque soit le protocole le traitement est le
même
!  Les résultats sont partagés (si on fait une modif
en TCP, elle est visible en UDP et vice versa)
Fonctionnement
Sock Client
Client TCP Sock Ecoute
1 Th
Ecoute
Sock Service
Client TCP Th1
2
Th2
Serveur 
multi-protocole
Sock DG
Client UDP
1
Th DG
Client UDP
2
Gestion Proto
!  But : traiter les requêtes indépendamment du
protocole transport (TCP ou UDP)

!  Utilise une BanqueSimple

!  En fonction des types de requêtes appelle la


bonne méthode de BanqueSimple

!  Méthode synchronized pour les threads


Gestion Proto
public class GestionProto {

private BanqueSimple b = new BanqueSimple();

public synchronized String traiter(String req) {

try {

String[]tab = req.split(" ");

switch(tab[0]){

case "CREATION":
Gestion Proto
!  Le serveur devra implanter les requêtes suivantes :
!  CREATION id somme_initiale
permet de demander la création d’un compte identifié par id sur
lequel sera placé la somme_initiale.
!  POSITION id
permet d’obtenir la position courante du compte identifié par id.
!  AJOUT id somme
ajoute une somme sur le compte identifié par id.
!  RETRAIT id somme
retire une somme sur le compte identifié par id.
Gestion Proto
!  Le client recevra les réponses du serveur sous la forme suivante :
!  OK commande
informe le client que la commande s'est correctement déroulée.
!  ERREUR raison
la raison de l'échec de la commande sous forme de chaîne de caractères.
!  POS solde date_dernière_opération
envoi au client le solde actuel du compte et la date de la dernière opération.

!  Les différents éléments constitutifs (commandes paramètres) seront séparés


par des espaces.

!  NB : la date de dernière opération sera envoyée sous la forme : Thu Nov 07


14:24:11 CET 2013 en utilisant la méthode toString() de la classe
java.util.Date.
Gestion Proto
case "CREATION":

double somme = Double.parseDouble(tab[2]);

b.creerCompte(tab[1],somme);

return "OK CREATION";

case "AJOUT":

somme = Double.parseDouble(tab[2]);

b.ajouter(tab[1],somme);

return "OK AJOUT";


Gestion Proto
case "RETRAIT":

somme = Double.parseDouble(tab[2]);

b.retirer(tab[1],somme);

return "OK RETRAIT";

case "POSITION":

return "POS "+b.getSolde(tab[1])+" "+

b.getDerniereOperation(tab[1]);

} // switch
Gestion Proto
} // switch

return "ERREUR Requete inconnue";

} catch(RuntimeException e) {

return "ERREUR "+e;

} // traiter

} // class
Serveur
!  Version finale gérant TCP et UDP

!  Partage un objet GestionProto


!  Modif TCP visible dans UDP et vice-versa
!  Tous les threads TCP utilisent le même objet
GestionProto

!  Partie UDP gérée par un thread


!  Equivalent du serveur UDP fait précédemment
Main du Serveur
public class Serveur {

public static void main(...) {

GestionProto proto = new GestionProto();

ThreadDG dg = new ThreadDG(proto);

dg.start();

...
Serveur
!  Version TCP en multi-thread
!  Socket d’écoute sur le port 1234 (même port
que UDP)
!  Boucle infinie faisant des accept + création de
thread
!  Threads gérant chacun un socket de service
!  Tous les threads partagent le même Gestion
Proto
Th Ecoute TCP
try {
ServerSocket ecoute = new ServerSocket(1234);
while(true) {
Socket service = ecoute.accept();
ThreadService s = new ThreadService(service,
proto);
s.start();
}
} catch(IOException e) { e.printStackTrace();}
} // main

} // class
Serveur
!  Thread service TCP
!  Attention : méthode run() sans paramètre.
!  On doit travailler avec le socket de service et le gestion
proto
!  Récupération du socket de service (un par thread)
!  Et du GestionProto (partagé par tous les threads – d’où
le synchronized) dans le constructeur
!  On recopie les références dans des attributs pour
pouvoir les utiliser dans la méthode run()
Thread Service
public class ThreadService extends Thread {
private Socket service;
private GestionProto proto;
public ThreadService(Socket service, GestionProto proto) {
this.service = service;
this.proto = proto;

}
...
Serveur
!  Thread service TCP
!  Méthode run()

!  Construit des flux de haut niveau permettant la lecture et l’écriture de String


(requête et réponse)

!  Lecture : BufferedReader

!  Reader alors que les sockets renvoient un InputStream (uniquement lecture


d’octets)

!  Utilisation de InputStreamReader qui transforme un InputStream en Reader

!  BufferedReader permet de faire des readLine()

!  Ecriture : PrintStream
!  C’est le type de System.out

!  PrintStream permet de faire des println()

!  Appel de la méthode traiter pour “transformer” une requête en réponse


Thread Service
public class ThreadService extends Thread {

...

public void run() {


try {

BufferedReader entree = new BufferedReader(


new InputStreamReader(service.getInputStream()) );

PrintStream sortie = new PrintStream(service.getOutputStream());

while (true) {

String requete = entree.readLine();

if (requete == null) break; // Le client a ferme la connexion

String reponse = proto.traiter(requete);

sortie.println(reponse);

service.close(); // on ferme nous aussi le socket

} catch(IOException e) { e.printStackTrace();}

}
}
Serveur
!  Thread gérant UDP
!  Constructeur qui récupère le Gestion Proto (partagé
avec la partie TCP)
!  On recopie la référence dans un attribut pour l’utiliser
dans la méthode run()
Thread DG
public class ThreadDG extends Thread {

private GestionProto proto;


public ThreadDG (GestionProto proto) {
this.proto = proto;

...
Serveur
!  Thread gérant UDP
!  DatagramSocket utilisant le même numéro de port que la partie TCP (il y a
autant de ports TCP que de ports UDP)

!  DatagramPacket utilisant un tampon de 1024 octets (suffisamment long pour


contenir requêtes et réponses)

!  On reçoit un datagramme UDP

!  Les données vont dans le tampon, l’adresse IP et le port distants dans des
attributs (getAddress() et getPort())

!  On construit une String à partir de la partie du tampon correspondant à la


réception (de 0 jusqu’à getLength() )

!  On appelle la méthode traiter (comme pour TCP)

!  On transforme la réponse en tableau d’octets

!  On place ce tableau d’octet dans le DatagramPacket reçu

!  On renvoie à l’expéditeur (son addresse et son port sont dans les attributs)
Thread DG
public class ThreadDG extends Thread {

...

public void run() {


try {

DatagramSocket sock = new DatagramSocket(1234);

byte[] tampon = new byte[1024];

while (true) {

DatagramPacket dg = new DatagramPacket(tampon, tampon.length);

sock.receive(dg);

String requete = new String(tampon, 0, dg.getLength());

String reponse = proto.traiter(requete);

byte[] repBytes = reponse.getBytes();

dg.setData(repBytes);

sock.send(dg);

} catch(IOException e) { e.printStackTrace();}

}
Client TCP
!  Créer un socket client en précisant le nom de la machine
serveur (ou son adresse IP) et le numéro de port

!  Construire des flux de haut niveau (BufferedReader et


PrintStream) comme côté serveur

!  Envoyer une requête (ici en dur mais on pourrait demander


à l’utilisateur de la taper, ou faire un menu texte, une
interface graphique...)

!  Attendre la réponse puis l’afficher

!  Puis fermer la connexion (on aurait pû envoyer plusieurs


requêtes avant)
Client TCP
try {
Socket client = new Socket("localhost",1234);
BufferedReader entree = new BufferedReader(new
InputStreamReader(client.getInputStream()) );
PrintStream sortie = new
PrintStream(client.getOutputStream());
sortie.println("CREATION 123 1000");
String reponse = entree.readLine();
System.out.println(reponse);
client.close(); // on ferme la connexion
} catch(IOException e) { e.printStackTrace();}
Client UDP
!  On crée un socket datagramme en laissant le système choisir le port
local

!  On récupère l’adresse IP du serveur

!  On construit un DatagramPacket qui va porter la requête (ici en dur


mais...) en précisant l’adresse IP et le port du serveur

!  On envoit le DatagramPacket au serveur

!  On construit un DatagramPacket pour recevoir la réponse (avec un


tampon suffisamment grand)

!  On attend la réponse

!  On reconstruit une String avec la partie du tampon qui correspond à


ce qu’on a reçu

!  On affiche la chaîne de caractères


Client UDP
try {

// le système choisit le port (comme pour un client TCP)

DatagramSocket sock = new DatagramSocket();

InetAddress adrServeur =
InetAddress.getByName("localhost");

String requete = "AJOUT 123 500";

byte[] reqBytes = requete.getBytes();

DatagramPacket dg = new DatagramPacket(reqBytes,


reqBytes.length, adrServeur, 1234);

sock.send(dg);

byte[] tampon = new byte[1024];

DatagramPacket dg2 = new DatagramPacket(tampon, tampon.length);

sock.receive(dg2);

String reponse = new String(tampon, 0, dg2.getLength());

System.out.println(reponse);

} catch(IOException e) { e.printStackTrace();}

Vous aimerez peut-être aussi