programmation Java (école d'ingénieur) · Chapitre 16 · Application directe du cours
Serveur de discussion
Réseau : sockets client/serveur · niveau 3 (difficile)
Énoncé
On veut un serveur de discussion (port ) : le premier message d'un client est son pseudonyme ; ensuite, chaque ligne qu'il envoie est transmise à tous les clients connectés, sous la forme « pseudo : message » ; les arrivées et départs sont annoncés ; la ligne /quitter termine la session.
- Quelle donnée est partagée entre les fils du serveur ? Comment la protéger ?
- Écrire le serveur.
- Pourquoi un client de discussion a-t-il besoin de deux fils ?
- Tester avec deux clients, Alice et Bob.
Corrigé
- La liste des flux de sortie des participants est partagée : tous les fils la parcourent pour diffuser, et chacun y ajoute ou retire son client. Tout accès se fait sous
synchronizedsur la liste. -
public class ServeurDiscussion {
private static final List<PrintWriter> participants = new ArrayList<>(); // partagée
static void diffuser(String message) {
synchronized (participants) { // la liste est partagée par les fils
for (PrintWriter p : participants) p.println(message);
}
}
public static void main(String[] args) throws IOException {
try (ServerSocket serveur = new ServerSocket(5005)) {
while (true) {
Socket s = serveur.accept();
new Thread(() -> servir(s)).start();
}
}
}
static void servir(Socket s) {
PrintWriter out = null;
String pseudo = "?";
try (s; BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8))) {
out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8), true);
pseudo = in.readLine();
synchronized (participants) { participants.add(out); }
diffuser("* " + pseudo + " entre");
String ligne;
while ((ligne = in.readLine()) != null && !ligne.equals("/quitter"))
diffuser(pseudo + " : " + ligne);
} catch (IOException e) {
// client parti brutalement : on le retire comme les autres
} finally {
if (out != null) synchronized (participants) { participants.remove(out); }
diffuser("* " + pseudo + " sort");
}
}
}
Le finally retire le client de la liste quelle que soit la façon dont il part.
- Un client de discussion doit à la fois lire le clavier de l'utilisateur et afficher les messages des autres, qui arrivent à tout moment : les deux lectures sont bloquantes, il faut un fil pour chacune.
- Avec deux clients scriptés (Alice se connecte, puis Bob 0,2 s plus tard ; chacun envoie deux messages espacés, puis
/quitter), Bob voit :
[Bob voit] * Bob entre
[Bob voit] Alice : Bonjour !
[Bob voit] Bob : Salut Alice
[Bob voit] Alice : Quelqu'un a fini l'exercice ?
[Bob voit] * Alice sort
[Bob voit] Bob : Oui, presque
et Alice, partie la première :
[Alice voit] * Alice entre
[Alice voit] * Bob entre
[Alice voit] Alice : Bonjour !
[Alice voit] Bob : Salut Alice
[Alice voit] Alice : Quelqu'un a fini l'exercice ?
Chacun reçoit aussi ses propres messages : c'est la confirmation que le serveur les a bien diffusés.
100 % gratuit
La suite du chapitre est sur Adloun
- 9 autres exercices corrigés dans ce chapitre
- Les 2 QCM du chapitre, le premier sans compte
- Le tuteur, qui guide pas à pas sans donner la réponse
Sans compte : le cours et le premier QCM. Exercices et tuteur : inscription gratuite en 30 secondes, sans carte bancaire.