feat: add private message

This commit is contained in:
2025-04-05 00:06:46 +02:00
parent bd0cf3c601
commit 13800486ba
5 changed files with 232 additions and 79 deletions

View File

@@ -1,5 +1,6 @@
package clientserver.client; package clientserver.client;
import clientserver.server.Server;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
@@ -8,8 +9,6 @@ import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Scanner; import java.util.Scanner;
import clientserver.server.Server;
public class Client { public class Client {
InetAddress clientAddress; InetAddress clientAddress;
@@ -27,6 +26,7 @@ public class Client {
} }
private static class MessageReceiver implements Runnable { private static class MessageReceiver implements Runnable {
private final DatagramSocket socket; private final DatagramSocket socket;
private volatile boolean running = true; private volatile boolean running = true;
@@ -38,26 +38,32 @@ public class Client {
public void run() { public void run() {
try { try {
socket.setSoTimeout(500); // Petit timeout pour pouvoir vérifier régulièrement si on doit s'arrêter, 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 // evite thread zombie car receive() est bloquant
while (running && !socket.isClosed()) { while (running && !socket.isClosed()) {
try { try {
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); DatagramPacket packet = new DatagramPacket(
buffer,
buffer.length
);
socket.receive(packet); socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength()); String message = new String(
packet.getData(),
0,
packet.getLength()
);
// Afficher le message reçu avec une mise en forme claire // Afficher le message reçu avec une mise en forme claire
System.out.println("\n>>> Message from server: \n" + message); System.out.println(message);
System.out.print("Enter your message (or 'exit' to quit): "); } catch (SocketTimeoutException e) {}
} catch (SocketTimeoutException e) {
}
} }
} catch (IOException e) { } catch (IOException e) {
if (!socket.isClosed()) { if (!socket.isClosed()) {
System.err.println("Error in the reception of messages: " + e.getMessage()); System.err.println(
"Error in the reception of messages: " + e.getMessage()
);
} }
} }
System.out.println("Message receiver stopped"); System.out.println("Message receiver stopped");
@@ -128,16 +134,23 @@ public class Client {
System.out.println("Enter your Pseudo :"); System.out.println("Enter your Pseudo :");
// 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.setScanner(new Scanner(System.in)); client.setScanner(new Scanner(System.in));
String userPseudo = client.getScanner().next(); String userPseudo = client.getScanner().next();
client.setPseudo(userPseudo); 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(client.getScanner().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:");
@@ -146,26 +159,40 @@ public class Client {
// (morph (/127.0.0.1:39056) : ...) // (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()
);
System.out.println("New connection request sent"); System.out.println("New connection request sent");
} }
return client; return client;
} }
private static void deconnexionClient(DatagramSocket socketClient, Client client, int handlerPort) { private static void deconnexionClient(
DatagramSocket socketClient,
Client client,
int handlerPort
) {
try { try {
String disconnectMsg = "DISCONNECT"; String disconnectMsg = "/disconnect";
byte[] disconnectData = disconnectMsg.getBytes(); byte[] disconnectData = disconnectMsg.getBytes();
DatagramPacket disconnectPacket = new DatagramPacket( DatagramPacket disconnectPacket = new DatagramPacket(
disconnectData, disconnectData,
disconnectData.length, disconnectData.length,
client.getServerAddress(), client.getServerAddress(),
handlerPort); handlerPort
);
socketClient.send(disconnectPacket); socketClient.send(disconnectPacket);
System.out.println("Deconnection sended on the server port: " + handlerPort); System.out.println(
"Deconnection sended on the server port: " + handlerPort
);
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error during sending the deconnection message: " + e); System.err.println(
"Error during sending the deconnection message: " + e
);
} }
} }
@@ -174,18 +201,28 @@ public class Client {
boolean running = true; boolean running = true;
System.out.println("\n=== Chat client ready ==="); 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 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 /list to see all the clients connected.");
System.out.println("Type 'exit' to quit."); System.out.println(
"Type '/msg <target> <message>' to send a private message."
);
System.out.println("Type '/help' to see all the commands.");
System.out.println("Type '/disconnect' to quit.\n");
while (running) { while (running) {
System.out.print("Enter your message: ");
String message = this.getScanner().nextLine(); String message = this.getScanner().nextLine();
if ("exit".equalsIgnoreCase(message)) { if ("/disconnect".equalsIgnoreCase(message)) {
running = false; running = false;
} else { } else {
Server.sendMessage(this.clientSocket, message, this.getServerAddress(), this.getHandlerPort()); Server.sendMessage(
this.clientSocket,
message,
this.getServerAddress(),
this.getHandlerPort()
);
} }
} }
@@ -231,7 +268,9 @@ public class Client {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
} else { } else {
System.err.println("Unexpected response from the server: " + reponse); System.err.println(
"Unexpected response from the server: " + reponse
);
} }
// 8 - Libérer le canal // 8 - Libérer le canal

View File

@@ -21,6 +21,10 @@ public class ClientHandler implements Runnable {
); );
} }
public Client getClient() {
return client;
}
public void stop() { public void stop() {
running = false; running = false;
if (clientHandlerSocket != null && !clientHandlerSocket.isClosed()) { if (clientHandlerSocket != null && !clientHandlerSocket.isClosed()) {

View File

@@ -1,7 +1,6 @@
package clientserver.server; package clientserver.server;
import clientserver.client.Client; import clientserver.client.Client;
import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
public class MessageProcessor { public class MessageProcessor {
@@ -25,11 +24,84 @@ public class MessageProcessor {
return; return;
} }
if (message.equals("/list")) { if (message.startsWith("/msg ")) {
handleListCommand(); handlePrivateMessage(message);
} else if (message.equals("DISCONNECT")) { return;
handleDisconnect();
} }
switch (message) {
case "/list":
handleListCommand();
break;
case "/help":
handleHelpCommand();
break;
case "/msg":
handleMsgCommand();
break;
case "/disconnect":
handleDisconnect();
break;
}
}
private void handlePrivateMessage(String message) {
String[] parts = message.split(" ", 3);
if (parts.length < 3) {
handleMsgCommand();
return;
}
String targetPseudo = parts[1];
String messageContent = parts[2];
ClientHandler targetHandler = Server.getClientHandler(targetPseudo);
if (targetHandler == null) {
Server.sendMessage(
clientHandlerSocket,
"> User " + targetPseudo + " not found",
client.getAddress(),
client.getPort()
);
return;
}
// Send private message to target
Client targetClient = targetHandler.getClient();
Server.sendMessage(
clientHandlerSocket,
"> " +
client.getPseudo() +
" -> " +
targetClient.getPseudo() +
": " +
messageContent,
targetClient.getAddress(),
targetClient.getPort()
);
// Send confirmation to sender
Server.sendMessage(
clientHandlerSocket,
"> " +
client.getPseudo() +
" -> " +
targetClient.getPseudo() +
": " +
messageContent,
client.getAddress(),
client.getPort()
);
}
private void handleMsgCommand() {
Server.sendMessage(
clientHandlerSocket,
"> /msg <target> <message> : send a message to a client",
client.getAddress(),
client.getPort()
);
} }
private void handleListCommand() { private void handleListCommand() {
@@ -41,6 +113,17 @@ public class MessageProcessor {
); );
} }
private void handleHelpCommand() {
Server.sendMessage(
clientHandlerSocket,
"> /list : list all connected clients" +
"\n> /msg <target> <message> : send a message to a client" +
"\n> /disconnect : disconnect from the server",
client.getAddress(),
client.getPort()
);
}
private void handleDisconnect() { private void handleDisconnect() {
System.out.println( System.out.println(
"Client deconnection : " + ClientHandler.prettyPrint(client) "Client deconnection : " + ClientHandler.prettyPrint(client)

View File

@@ -13,7 +13,10 @@ public class Server {
private int mainServerPort; private int mainServerPort;
private DatagramSocket mainServerSocket; private DatagramSocket mainServerSocket;
private boolean isRunning; private boolean isRunning;
private static Map<String, ClientHandler> mapPseudosConnectedClientsHandlers = new ConcurrentHashMap<>(); private static Map<
String,
ClientHandler
> mapPseudosConnectedClientsHandlers = new ConcurrentHashMap<>();
public Server(int port) { public Server(int port) {
this.mainServerPort = port; this.mainServerPort = port;
@@ -44,8 +47,9 @@ public class Server {
public static DatagramPacket receivedPacket(DatagramSocket socket) { public static DatagramPacket receivedPacket(DatagramSocket socket) {
byte[] receivedData = new byte[1024]; byte[] receivedData = new byte[1024];
DatagramPacket receivedPacket = new DatagramPacket( DatagramPacket receivedPacket = new DatagramPacket(
receivedData, receivedData,
receivedData.length); receivedData.length
);
try { try {
socket.receive(receivedPacket); // Blocking call socket.receive(receivedPacket); // Blocking call
@@ -57,81 +61,84 @@ public class Server {
} }
public static String packetToMessage(DatagramPacket packet) { public static String packetToMessage(DatagramPacket packet) {
return new String( return new String(packet.getData(), 0, packet.getLength());
packet.getData(),
0,
packet.getLength());
} }
public static void sendMessage( public static void sendMessage(
DatagramSocket socket, DatagramSocket socket,
String message, String message,
java.net.InetAddress address, java.net.InetAddress address,
int port) { int port
) {
try { try {
byte[] sendData = message.getBytes(); byte[] sendData = message.getBytes();
DatagramPacket packetToSend = new DatagramPacket( DatagramPacket packetToSend = new DatagramPacket(
sendData, sendData,
sendData.length, sendData.length,
address, address,
port); port
);
socket.send(packetToSend); socket.send(packetToSend);
} catch (IOException e) { } catch (IOException e) {
System.err.println( System.err.println(
"Failed to send message to " + address + ":" + port); "Failed to send message to " + address + ":" + port
);
e.printStackTrace(); e.printStackTrace();
} }
} }
private void handleNewConnection() { private void handleNewConnection() {
DatagramPacket packet = receivedPacket(mainServerSocket); DatagramPacket packet = receivedPacket(mainServerSocket);
if (packet == null) if (packet == null) return;
return;
int originalClientPort = packet.getPort(); int originalClientPort = packet.getPort();
InetAddress clientAddress = packet.getAddress(); InetAddress clientAddress = packet.getAddress();
;
// Log the initial connection request // Log the initial connection request
System.out.println( System.out.println(
"New connection request from " + "New connection request from " +
clientAddress + clientAddress +
":" + ":" +
originalClientPort); originalClientPort
);
// Process the received message // Process the received message
String pseudoMessage = new String(packet.getData(), 0, packet.getLength()); String pseudoMessage = new String(
System.out.println( packet.getData(),
"Received message from " + 0,
clientAddress + packet.getLength()
":" + );
originalClientPort +
": " +
pseudoMessage);
System.out.println(
"Received message from " +
clientAddress +
":" +
originalClientPort +
": " +
pseudoMessage
);
// Create a new socket for this client // Create a new socket for this client
DatagramSocket clientHandlerSocket = createNewSocket(); DatagramSocket clientHandlerSocket = createNewSocket();
if (clientHandlerSocket == null)
return;
if (clientHandlerSocket == null) return;
int clientHandlerLocalPort = clientHandlerSocket.getLocalPort(); int clientHandlerLocalPort = clientHandlerSocket.getLocalPort();
Client client = new Client(clientAddress, originalClientPort); Client client = new Client(clientAddress, originalClientPort);
client.setPseudo(pseudoMessage);
client.setPseudo(pseudoMessage);
// Send new port information to client // Send new port information to client
String response = "PORT:" + clientHandlerLocalPort; String response = "PORT:" + clientHandlerLocalPort;
sendMessage(
mainServerSocket,
response,
clientAddress,
packet.getPort());
sendMessage(
mainServerSocket,
response,
clientAddress,
packet.getPort()
);
// Create and start a ClientHandler for this connection // Create and start a ClientHandler for this connection
ClientHandler handler = new ClientHandler(clientHandlerSocket, client); ClientHandler handler = new ClientHandler(clientHandlerSocket, client);
Thread thread = new Thread(handler); Thread thread = new Thread(handler);
thread.start(); thread.start();
// Add client handler to mapPseudosConnectedClients // Add client handler to mapPseudosConnectedClients
addConnectedClientHandler(pseudoMessage, handler); addConnectedClientHandler(pseudoMessage, handler);
} }
@@ -143,7 +150,8 @@ public class Server {
handleNewConnection(); handleNewConnection();
} catch (Exception e) { } catch (Exception e) {
System.err.println( System.err.println(
"Error handling connection: " + e.getMessage()); "Error handling connection: " + e.getMessage()
);
e.printStackTrace(); e.printStackTrace();
} }
} }
@@ -151,12 +159,30 @@ public class Server {
} }
public static String getPseudos() { public static String getPseudos() {
StringBuilder pseudos = new StringBuilder("All connected clients:\n"); StringBuilder pseudos = new StringBuilder();
mapPseudosConnectedClientsHandlers.forEach((k, v) -> pseudos.append("Pseudo: ").append(k).append("\n")); int size = mapPseudosConnectedClientsHandlers.size();
int count = 0;
for (Map.Entry<
String,
ClientHandler
> entry : mapPseudosConnectedClientsHandlers.entrySet()) {
count++;
pseudos.append("> ").append(entry.getKey());
if (count < size) {
pseudos.append("\n");
}
}
return pseudos.toString(); return pseudos.toString();
} }
public void addConnectedClientHandler(String pseudo, ClientHandler clientHandler) { public static ClientHandler getClientHandler(String pseudo) {
return mapPseudosConnectedClientsHandlers.get(pseudo);
}
public void addConnectedClientHandler(
String pseudo,
ClientHandler clientHandler
) {
mapPseudosConnectedClientsHandlers.putIfAbsent(pseudo, clientHandler); mapPseudosConnectedClientsHandlers.putIfAbsent(pseudo, clientHandler);
} }

1
gradle.properties Normal file
View File

@@ -0,0 +1 @@
org.gradle.console=plain