feat: refactor client and added a receive thread, possibility to chat with server via client handler

This commit is contained in:
Morph01
2025-04-01 22:20:53 +02:00
committed by Morph01
parent 3f4ff22cea
commit 55a7499339
2 changed files with 157 additions and 77 deletions

View File

@@ -1,12 +1,12 @@
package clientserver.client; package clientserver.client;
import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.SocketException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Scanner; import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import clientserver.server.Server; import clientserver.server.Server;
@@ -17,6 +17,56 @@ public class Client {
private InetAddress serverAddress; private InetAddress serverAddress;
private int serverPort; private int serverPort;
private String pseudo; private String pseudo;
private Scanner scan;
private DatagramSocket clientSocket;
private int handlerPort;
public Client(InetAddress address, int port) {
this.clientAddress = address;
this.clientPort = port;
}
private static class MessageReceiver implements Runnable {
private final DatagramSocket socket;
private volatile boolean running = true;
public MessageReceiver(DatagramSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
socket.setSoTimeout(500); // Petit timeout pour pouvoir vérifier régulièrement si on doit s'arrêter,
// evite thread zombie car receive() est bloquant
while (running && !socket.isClosed()) {
try {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
// Afficher le message reçu avec une mise en forme claire
System.out.println("\n>>> Message from server: \n" + message);
System.out.print("Enter your message (or 'exit' to quit): ");
} catch (SocketTimeoutException e) {
}
}
} catch (IOException e) {
if (!socket.isClosed()) {
System.err.println("Error in the reception of messages: " + e.getMessage());
}
}
System.out.println("Message receiver stopped");
}
public void stop() {
running = false;
}
}
public void setPort(int port) { public void setPort(int port) {
this.clientPort = port; this.clientPort = port;
@@ -50,30 +100,50 @@ public class Client {
this.pseudo = pseudo; this.pseudo = pseudo;
} }
public Client(InetAddress address, int port) { public Scanner getScanner() {
this.clientAddress = address; return this.scan;
this.clientPort = port; }
public void setScanner(Scanner scan) {
this.scan = scan;
}
public DatagramSocket getClientSocket() {
return this.clientSocket;
}
public void setClientSocket(DatagramSocket clientSocket) {
this.clientSocket = clientSocket;
}
public int getHandlerPort() {
return handlerPort;
}
public void setHandlerPort(int handlerPort) {
this.handlerPort = handlerPort;
} }
public static Client createAndConfigureClient(DatagramSocket socketClient) { public static Client createAndConfigureClient(DatagramSocket socketClient) {
Scanner scan = new Scanner(System.in);
System.out.println("Enter your Pseudo :"); System.out.println("Enter your Pseudo :");
String userPseudo = scan.next();
// Créer le client avec l'adresse locale et le port du socket // Créer le client avec l'adresse locale et le port du socket
Client client = new Client(socketClient.getLocalAddress(), socketClient.getLocalPort()); Client client = new Client(socketClient.getLocalAddress(), socketClient.getLocalPort());
client.pseudo = userPseudo; client.setScanner(new Scanner(System.in));
String userPseudo = client.getScanner().next();
client.setPseudo(userPseudo);
System.out.println("Enter the IP address of the server :"); System.out.println("Enter the IP address of the server :");
try { try {
client.serverAddress = InetAddress.getByName(scan.next()); client.serverAddress = InetAddress.getByName(client.getScanner().next());
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
System.err.println("The address you choose create an UnknownHostException : " + e); System.err.println("The address you choose create an UnknownHostException: " + e);
} }
System.out.println("Enter the port of the server :"); System.out.println("Enter the port of the server:");
client.setServerPort(scan.nextInt()); client.setServerPort(client.getScanner().nextInt());
scan.close(); client.getScanner().nextLine(); // Consume the remaining newline to avoid sending a void line message like
// (morph (/127.0.0.1:39056) : ...)
if (socketClient != null && client.serverAddress != null) { if (socketClient != null && client.serverAddress != null) {
Server.sendMessage(socketClient, client.getPseudo(), client.serverAddress, client.getServerPort()); Server.sendMessage(socketClient, client.getPseudo(), client.serverAddress, client.getServerPort());
@@ -93,76 +163,80 @@ public class Client {
client.getServerAddress(), client.getServerAddress(),
handlerPort); handlerPort);
socketClient.send(disconnectPacket); socketClient.send(disconnectPacket);
System.out.println("Déconnexion envoyée au serveur sur le port " + handlerPort); System.out.println("Deconnection sended on the server port: " + handlerPort);
} catch (Exception e) { } catch (Exception e) {
System.err.println("Erreur lors de l'envoi du message de déconnexion : " + e); System.err.println("Error during sending the deconnection message: " + e);
} }
} }
public void run() {
// 1 - Boucle principale pour l'envoi des messages
boolean running = true;
System.out.println("\n=== Chat client ready ===");
System.out.println("Type your messages then press 'Enter' to send them to the server.");
System.out.println("Type /list to see all the clients connected.");
System.out.println("Type 'exit' to quit.");
while (running) {
System.out.print("Enter your message: ");
String message = this.getScanner().nextLine();
if ("exit".equalsIgnoreCase(message)) {
running = false;
} else {
Server.sendMessage(this.clientSocket, message, this.getServerAddress(), this.getHandlerPort());
}
}
// 2 - Gérer la déconnexion proprement
System.out.println("Deconnection in progress...");
deconnexionClient(this.getClientSocket(), this, this.getHandlerPort());
}
public static void main(String[] args) { public static void main(String[] args) {
try { try {
DatagramSocket socketClient = null; // 1 - Création du canal avec un port libre
try { DatagramSocket socketClient = new DatagramSocket();
// 1 - Création du canal avec un port libre
socketClient = new DatagramSocket();
} catch (SocketException e) {
System.err.println("The client socket cannot be created : " + e);
return;
}
// 2 - Création du client avec l'envoi du pseudo pour l'établissement de la // 2 - Création du client avec l'envoi du pseudo pour l'établissement de la
// connexion avec le server // connexion
Client client = createAndConfigureClient(socketClient); Client client = createAndConfigureClient(socketClient);
// // 3 - Recevoir // 3 - Recevoir la réponse initiale du serveur
DatagramPacket receivedPacket = Server.receivedPacket(socketClient); DatagramPacket receivedPacket = Server.receivedPacket(socketClient);
String reponse = Server.packetToMessage(receivedPacket); String reponse = Server.packetToMessage(receivedPacket);
if (reponse.startsWith("PORT:")) { if (reponse.startsWith("PORT:")) {
int newClientHandlerPort = Integer.parseInt(reponse.substring(5)); int handlerPort = Integer.parseInt(reponse.substring(5));
System.out.println("Connected on handler port: " + handlerPort);
System.out.println("Connected on port:" + newClientHandlerPort); client.setClientSocket(socketClient);
client.setHandlerPort(handlerPort);
// 4 - Communiquer sur le nouveau port // 4 - Lancer uniquement le thread de réception des messages
for (int i = 0; i < 3; i++) { MessageReceiver receiver = new MessageReceiver(socketClient);
String messagePort = "Test"; Thread receiverThread = new Thread(receiver);
byte[] envoyeesPort = messagePort.getBytes(); receiverThread.setDaemon(true);
DatagramPacket paquetPort = new DatagramPacket( receiverThread.start();
envoyeesPort,
envoyeesPort.length,
client.getServerAddress(),
newClientHandlerPort);
socketClient.send(paquetPort);
System.out.println("Test sent");
try { // 5 - Éxecution du client, bloquant tant que le client est connecté.
// Définir un timeout court pour que la boucle continue client.run();
socketClient.setSoTimeout(1000);
byte[] reponseData = new byte[1024]; // 6 - Arrêter le thread de réception
DatagramPacket reponsePacket = new DatagramPacket( receiver.stop();
reponseData, try {
reponseData.length); receiverThread.join(1000);
socketClient.receive(reponsePacket); } catch (InterruptedException e) {
Thread.currentThread().interrupt();
String messageServeur = new String(
reponsePacket.getData(),
0,
reponsePacket.getLength());
System.out.println("Serveur répond: \n" + messageServeur);
} catch (java.net.SocketTimeoutException e) {
// Ignorer le timeout et continuer
System.out.println("Pas de réponse du serveur cette fois-ci");
}
TimeUnit.SECONDS.sleep(1);
} }
deconnexionClient(socketClient, client, newClientHandlerPort); } else {
System.err.println("Unexpected response from the server: " + reponse);
} }
// 5 - Libérer le canal // 8 - Libérer le canal
socketClient.close(); socketClient.close();
System.out.println("Client closed");
} catch (Exception e) { } catch (Exception e) {
System.err.println(e); System.err.println(e);
} }

View File

@@ -15,13 +15,15 @@ public class ClientHandler implements Runnable {
this.client = client; this.client = client;
} }
private void sendConnectedClients() { private void sendConnectedClients(String message) {
Server.sendMessage(clientHandlerSocket, Server.getPseudos(), client.getAddress(), client.getPort()); if (message.equals("/list")) {
Server.sendMessage(clientHandlerSocket, Server.getPseudos(), client.getAddress(), client.getPort());
}
} }
private void handleDeconnexion(String message) { private void handleDeconnexion(String message) {
if (message.equals("DISCONNECT")) { if (message.equals("DISCONNECT")) {
System.out.println("connexion du client : " + client.getPseudo()); System.out.println("Client deconnection : " + prettyPrint(client));
Server.removeClientHandler(client.getPseudo()); Server.removeClientHandler(client.getPseudo());
stop(); stop();
} }
@@ -34,13 +36,22 @@ public class ClientHandler implements Runnable {
} }
} }
public String prettyPrint(Client client) {
StringBuilder str = new StringBuilder();
str.append(client.getPseudo() +
" (" +
client.getAddress() +
":" +
client.getPort() +
")");
return str.toString();
}
@Override @Override
public void run() { public void run() {
System.out.println( System.out.println(
"Started handler for client " + "Started handler for client " +
client.getAddress() + prettyPrint(client));
":" +
client.getPort());
while (running && !clientHandlerSocket.isClosed()) { while (running && !clientHandlerSocket.isClosed()) {
DatagramPacket packet = Server.receivedPacket(clientHandlerSocket); DatagramPacket packet = Server.receivedPacket(clientHandlerSocket);
@@ -52,21 +63,16 @@ public class ClientHandler implements Runnable {
0, 0,
packet.getLength()); packet.getLength());
System.out.println( System.out.println(
"Received from " + prettyPrint(client) + " : " +
client.getAddress() +
":" +
client.getPort() +
": " +
message); message);
sendConnectedClients(); // Possibilies
handleDeconnexion(message); sendConnectedClients(message); // /list command
handleDeconnexion(message); // exit command or DISCONNECT
} }
System.out.println( System.out.println(
"Client handler terminated for " + "Client handler terminated for " +
client.getAddress() + prettyPrint(client));
":" +
client.getPort());
} }
} }