Socket java : Créer une application de chat Client/Serveur

Dans ce tutoriel, vous allez apprendre à créer une application de chat java entre deux machines en utilisant les sockets. Avant d'entamer la partie programmation, il faut configurer votre réseau afin de faire interagir ces deux machines sur le réseau local de votre domicile par exemple. Nous allons mettre en oeuvre ce qu'on a vu en théorie: le célèbre modèle Client/Serveur.

A la fin de ce tutoriel, vous saurez comment les sockets sont capables d’échanger des messages.

Serveur

Le serveur java initialise la connexion, il lance l'écoute sur un port et se met en attente des connexions entrantes pour qu'il les accepte. Par exemple, le numéro de port est 5000, le client envoie une demande au serveur avec ce numéro de port 5000. Le serveur accepte la demande et transmit ses informations (adresse ip) au client. Maintenant, la connexion est établie et un échange de messages peut se faire.

Java fournit un package java.net qui traite tout ce qui est réseau, on a besoin seulement de deux classes:

  • java.net.ServerSocket: cette classe accepte les connexions venues des clients.
  • java.net.Socket: cette classe permet de se connecter à la machine distante.
On a besoin aussi d'un outil pour saisir, envoyer et recevoir le flux:
  • Scanner: lire les entrées clavier.
  • BufferedReader: lire le texte reçu à partir de l'émetteur.
  • PrintWriter: envoyer le texte saisi.
ServerSocket socketserver;
Socket socketduserveur;
final BufferedReader in;
final PrintWriter out;
final Scanner sc=new Scanner(System.in);
try {
socketserver = new ServerSocket(5000);
socketduserveur = socketserver.accept();
out = new PrintWriter(socketduserveur.getOutputStream());
in = new BufferedReader (new InputStreamReader (socketduserveur.getInputStream()));
String s;
s = sc.next();
out.println(s);
out.flush();
String message_client ;
message_client = in.readLine();
System.out.println("Client : "+message_client);
}
catch (IOException e) {
e.printStackTrace();
}
Après la création du socket serveur qui porte le numéro de port 5000, le serveur attend les connexions entrantes et dès que une est détectée, il l'accepte. Les deux variables qui gèrent le flux de lecture et d'écriture in et out sont initialisées pour qu'elles soient reliées directement avec le flux d'envoi et de réception.
La variable s stocke le texte saisi avec la méthode next(), puis elle est envoyée avec la méthode println(s).
La méthode flush() est importante car elle vide le buffer d'écriture vers la sortie sinon un null va être reçu par l'autre machine.
La variable message_client stocke le message  reçu qui est affiché avec println().

La limite de ce code est qu'il est capable d'envoyer et recevoir un message qu'une seule fois. Vous aurez dans la tète une idée: ajouter une boucle while(true). C'est vrai, mais si par exemple le serveur envoie un message au client, ce dernier ne peut pas récupérer le message tant qu'il n'a pas envoyer lui aussi. La solution optimale est de créer deux Threads: un pour l'envoie et l'autre pour la réception. Ces deux processus permettent que l'envoi et la réception se fassent simultanément.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/*
* www.codeurjava.com
*/
public class Serveur {

public static void main(String[] test) {

final ServerSocket serveurSocket ;
final Socket clientSocket ;
final BufferedReader in;
final PrintWriter out;
final Scanner sc=new Scanner(System.in);

try {
serveurSocket = new ServerSocket(5000);
clientSocket = serveurSocket.accept();
out = new PrintWriter(clientSocket.getOutputStream());
in = new BufferedReader (new InputStreamReader (clientSocket.getInputStream()));
Thread envoi= new Thread(new Runnable() {
String msg;
@Override
public void run() {
while(true){
msg = sc.nextLine();
out.println(msg);
out.flush();
}
}
});
envoi.start();

Thread recevoir= new Thread(new Runnable() {
String msg ;
@Override
public void run() {
try {
msg = in.readLine();
//tant que le client est connecté
while(msg!=null){
System.out.println("Client : "+msg);
msg = in.readLine();
}
//sortir de la boucle si le client a déconecté
System.out.println("Client déconecté");
//fermer le flux et la session socket
out.close();
clientSocket.close();
serveurSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
recevoir.start();
}catch (IOException e) {
e.printStackTrace();
}
}
}

La séparation des deux processus est clair, le Serveur et le Client peuvent échanger les données à tout moment et infiniment. La boucle while de la lecture teste si la connexion est encore établie. N'oubliez pas de bien fermer vos flux de lecture et d'écriture ainsi que la connexion après la sortie de la boucle avec la méthode close().

Client

Le client n'a besoin qu'à la classe Socket pour établir la connexion serveur, le constructeur prend en entrée l'adresse IP du serveur et le numéro de port. Le reste du code est le même que celui du serveur.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/*
* www.codeurjava.com
*/
public class Client {

public static void main(String[] args) {

final Socket clientSocket;
final BufferedReader in;
final PrintWriter out;
final Scanner sc = new Scanner(System.in);//pour lire à partir du clavier

try {
/*
* les informations du serveur ( port et adresse IP ou nom d'hote
* 127.0.0.1 est l'adresse local de la machine
*/
clientSocket = new Socket("127.0.0.1",5000);

//flux pour envoyer
out = new PrintWriter(clientSocket.getOutputStream());
//flux pour recevoir
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

Thread envoyer = new Thread(new Runnable() {
String msg;
@Override
public void run() {
while(true){
msg = sc.nextLine();
out.println(msg);
out.flush();
}
}
});
envoyer.start();

Thread recevoir = new Thread(new Runnable() {
String msg;
@Override
public void run() {
try {
msg = in.readLine();
while(msg!=null){
System.out.println("Serveur : "+msg);
msg = in.readLine();
}
System.out.println("Serveur déconecté");
out.close();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
recevoir.start();

} catch (IOException e) {
e.printStackTrace();
}
}
}
Exécution:

Serveur socket tcp java
Fenêtre serveur
Client socket tcp java
Fenêtre client
Remarque : Si vous travaillez sur la même machine, vous devez lancer Eclipse deux fois, un pour le serveur et l'autre pour le client. Suivez la vidéo YouTube suivante:
https://www.youtube.com/watch?v=d-eD6EDa3io


Références:
Réseau en Java - Manipulation des adresses IP
Comment Récupérer le nom et l'adresse IP local du serveur/client
Openclassrooms: Introduction aux sockets
Java doc: Class Socket