TD3 de Java Avancé.

publicité
TD3 de Java Avancé.
Thread et multiThreading
Un serveur HTTP
L'objectif de ce TD est d'écrire un serveur HTTP multi-thread. Pour cela, vous devez :
· suivre
la décomposition proposée ;
· implémenter les classes demandées ;
Vous placerez l'ensemble de vos classes dans un package httpd.
A travers ce TD, vous allez mettre en œuvre différentes classes des packages :
· java.lang
· java.util
· java.io
· java.net
1. Structure générale du serveur
Le programme se composera pour l'instant de deux classes :
· httpd.Main : la classe contenant la méthode main(). Cette classe sert à lire le fichier de
configuration et à démarrer le serveur.
· httpd.Httpd : la classe qui implémente le serveur HTTP.
Le serveur se lancera par la commande :
java httpd.Main config.properties
1.1 Configuration du serveur
L'ensemble des paramètres de configuration du serveur est contenu dans un fichier texte qui est
passé en paramètre au programme. Pour lire et exploiter le contenu de ce fichier, on utilisera la
classe java.util.Properties (voir la méthode load()). Ce fichier contiendra, entre autre, le numéro du
port d'écoute du serveur (champs Port) et le répertoire racine des documents web (champs
WebRoot).
Exemple de fichier :
# Numéro de port du serveur
Port=8080
# Répertoire racine des documents web
WebRoot=c:/www
1/5
1.2 Boucle de traitement des requêtes
Le protocole HTTP est un protocole sans état basé sur TCP. L'algorithme du serveur est le suivant :
Ouvrir une socket sur le port indiqué
Boucle infinie
Attendre une requête
Récupérer la socket de travail
Lire le contenu de la requête
Analyser la requête
Construire la réponse adaptée
Renvoyer la réponse
Fermer la socket
Fin de boucle
Pour ouvrir la socket d'écoute du serveur, on utilisera la classe java.net.ServerSocket.
L'attente de requête se fait par la méthode accept() qui retourne à chaque nouvelle requête une
socket de travail (instance de la classe java.net.Socket). Pour récupérer le contenu de cette socket, il
suffit de lire sur le flux retourné par getInputStream().
1.3 Traitement d'une requête
exemple de requête http réelle de type GET
http://www.perdu.com/
GET / HTTP/1.1
Host: www.perdu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Pour notre td, le serveur ne traitera que les requêtes GET. Ces requêtes sont de la forme :
GET /ressource HTTP/1.1\n
Exemple de requête :
GET /rep1/index.html HTTP/1.1\n
Cette requête demande à obtenir le fichier index.html situé dans le répertoire rep1, lui-même
situé dans le répertoire racine des documents Web.
Exemple de réponse http réelle
HTTP/1.1 200 OK
Date: Thu, 13 Jan 2016 05:54:23 GMT
Server: Apache
Accept-Ranges: bytes
X-Mod-Pagespeed: 1.6.29.7-3566
Vary: Accept-Encoding
Content-Encoding: gzip
Cache-Control: max-age=0, no-cache
Content-Length: 1243
2/5
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/html
<html><head><title>Vous Etes Perdu ?</title></head><body><h1>Perdu sur l'Internet ?</h1><h2>Pas de panique, on va vous
aider</h2><strong><pre> * <----- vous êtes ici</pre></strong>//<!//]]></script></body></html>
Le serveur lit la requête (classe java.io.BufferedReader méthode readLine()), l'analyse (classe
java.util.Scanner) et renvoi une réponse (classe java.io.PrintStream) de la forme :
Header\n
\n
Body
La partie Header se compose de plusieurs champs contenant des informations utiles pour le
programme client (statut de la réponse, date, nom du serveur, type du document retourné, ...)
La partie Body contient soit le document si la requête a pu être correctement traitée, soit un
message d'erreur pour l'utilisateur.
Si la ressource est accessible, la réponse renvoyée est de la forme :
HTTP/1.1 200 OK
Date: Wed, 13 Jan 2016 21:49:50 GMT
Server: JavaHttp/1.0
Content-lenght : 1280
Content-type: text/html
Le contenu du fichier ...
Si la requête est mal formée, la réponse renvoyée est de la forme :
HTTP/1.0 400 Bad Request
Date: Wed, 13 Jan 2016 21:39:48 GMT
Server: JavaHttpd/1.0
Content-type: text/html
<HEAD><TITLE>Mauvaise requête</TITLE></HEAD>
<BODY><H1>Mauvaise requête</H1>
Votre browser a envoyé une requête que ce serveur ne peut pas traiter.<P>
</BODY>
Si la requête est bien formée, le serveur vérifie que la ressource est bien accessible à partir du
répertoire racine précisé dans le fichier de configuration (champs WebRoot).
Si ce n'est pas le cas, la réponse renvoyée est de la forme :
HTTP/1.0 404 Not found
Date: Wed, 13 Jan 2016 21:41:59 GMT
Server: JavaHttpd/1.0
Content-type: text/html
<HEAD><TITLE>Fichier non trouvé</TITLE></HEAD>
<BODY><H1>Fichier non trouvé</H1>
La ressource /rep1/une_ressource n'est pas présente sur ce serveur.<P>
</BODY>
3/5
2. Traitement multi-thread
Actuellement, le serveur ne peut traiter qu'une requête à la fois. Pour pourvoir traiter plusieurs
requêtes simultanément, nous allons créer des objets capables de prendre en charge les requêtes et
qui s'exécuteront dans différentes threads. Ces objets seront des instances d'une nouvelle classe :
httpd.HttpConnection.
Pour que ces instances soient « threadables », cette classe doit implémenter l'interface
java.lang.Runnable ou étendre la classe java.lang.Thread. Une grande partie du code de la classe
Httpd sera délocalisée dans cette nouvelle classes (au sein de la méthode run()).
La boucle de traitement des requêtes (située dans la classe Httpd) devient alors :
Ouvrir une socket sur le port indiqué
Boucle infinie
Attendre une requête
Récupérer la socket de travail
Créer une instance de HttpConnection avec en paramètre la socket et la
configuration
Fin de boucle
La méthode run() de la classe HttpConnection contient le reste du traitement :
Lire le contenu de la requête
Analyser la requête
Construire la réponse adaptée
Renvoyer la réponse
Fermer la socket
3. Journalisation des accès
On souhaite pouvoir suivre l'ensemble des requêtes auxquelles répond notre serveur. Pour cela, on
enregistre chaque requête traitée dans un fichier de journalisation. Les enregistrements sont de la
forme :
[adresse IP du client] [date] [requête] [statut de la réponse]
Exemple :
[127.0.0.1] [Sat Mar 08 14:51:47] [GET /icons/dir.gif HTTP/1.0] [200]
[128.93.7.60] [Sat Mar 08 14:53:44] [GET /docs/index.html HTTP/1.0] [404]
Écrivez une classe httpd.HttpLog de la forme :
package httpd;
class HttpLog {
HttpLog(String logFile) {...}
add(String address, String request, int status) {...}
}
Cette classe sera instanciée avant le démarrage du serveur (dans la classe Httpd) avec le nom du
fichier de journalisation (indiqué dans le fichier de configuration dans le champ LogFile) et sera
4/5
fournie à chaque instance de HttpConnection pour qu'elles puissent y ajouter leur requête une fois
celle-ci traitée.
Remarque : le serveur étant multi-thread, assurez-vous que l'ajout d'une entrée (méthode add) soit
exclusif.
5/5
Téléchargement