Compare commits
5 Commits
55a7499339
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ee3291465 | |||
| b0e9f1bb4e | |||
| b8bc715868 | |||
| 13800486ba | |||
| bd0cf3c601 |
@@ -1,4 +1,4 @@
|
||||
arguments=--init-script /home/xeon0x/.var/app/dev.zed.Zed/data/zed/extensions/work/java/jdtls/jdt-language-server-1.46.0-202503271314/configuration/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle
|
||||
arguments=--init-script /home/xeon0x/.var/app/dev.zed.Zed/data/zed/extensions/work/java/jdtls/jdt-language-server-1.46.1-202504011455/configuration/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||
|
||||
94
README.md
94
README.md
@@ -1,5 +1,24 @@
|
||||
# ClientServer
|
||||
|
||||
A Java-based ☕️↔️ UDP client-server chat 💬 system with automatic 🔄 text-to-emoji 😊 conversion using Mistral API.
|
||||
|
||||
## Features
|
||||
|
||||
### Available Commands
|
||||
|
||||
- `/list` : Display connected clients list
|
||||
- `/msg <nickname> <message>` : Send private message to specific client
|
||||
- `/help` : Display help with all available commands
|
||||
- `/disconnect` : Clean server disconnection
|
||||
- `<message>` : Broadcast message translated to emojis to all connected clients
|
||||
|
||||
### Technical Features
|
||||
|
||||
- UDP Communication
|
||||
- Multi-threading for client handling
|
||||
- Mistral API for text-to-emoji conversion
|
||||
- Disconnection handling
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
@@ -11,33 +30,76 @@
|
||||
### Client
|
||||
|
||||
```bash
|
||||
./gradlew run --args='client'
|
||||
./gradlew runClient
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
./gradlew runServer
|
||||
```
|
||||
The client will ask for a pseudo, an IP (type `localhost` if server and clients are on the same machine), and a port (type `6666` by default).
|
||||
|
||||
### Server
|
||||
|
||||
```bash
|
||||
./gradlew run --args='server'
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
./gradlew runServer
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a `config.properties` file at project root (`ClientServer/app/config.properties`) with:
|
||||
|
||||
```properties
|
||||
mistral.api.key=your_api_key
|
||||
```
|
||||
|
||||
Replace `your_api_key` with your actual Mistral API key. Create one for free at https://console.mistral.ai/api-keys. If no API key is provided, the server will not be able to convert broadcasted messages to emojis and empty broadcasted messages will be sent.
|
||||
|
||||
## Documentation
|
||||
|
||||
### Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
Note over Client, Serveur: DatagramSocket("localhost", 66666)
|
||||
Client->>+Serveur: DatagramPacket("Salve !", 7, "localhost", 6666)
|
||||
Note over Serveur, Client: DatagramSocket("localhost", 66666)
|
||||
Serveur->>+Client: DatagramPacket("Accusé de réception", 19, "localhost", 6666)
|
||||
participant MainServer
|
||||
|
||||
participant Alice
|
||||
Note over Alice: Creates DatagramSocket
|
||||
Alice->>+MainServer: Sends username "Alice"
|
||||
Note over MainServer: Creates new socket<br/>for AliceHandler
|
||||
create participant AliceHandler
|
||||
MainServer->>AliceHandler: Create new handler thread
|
||||
MainServer->>-Alice: PORT:{AlicePort}
|
||||
|
||||
participant Bob
|
||||
Bob->>+MainServer: Sends username "Bob"
|
||||
Note over MainServer: Creates new socket<br/>for BobHandler
|
||||
create participant BobHandler
|
||||
MainServer->>BobHandler: Create new handler thread
|
||||
MainServer->>-Bob: PORT:{BobPort}
|
||||
|
||||
rect rgb(200, 200, 255)
|
||||
Note over Alice,Bob: Chat loop
|
||||
Alice->>+AliceHandler: "Hi everyone!"
|
||||
Note over AliceHandler: Converts to emojis
|
||||
AliceHandler->>+BobHandler: Broadcast message
|
||||
BobHandler->>-Bob: "Alice: 👋👩👩🌍🎉"
|
||||
AliceHandler->>-Alice: Message sent
|
||||
|
||||
Bob->>+BobHandler: "Hello Alice, how are you?"
|
||||
Note over BobHandler: Converts to emojis
|
||||
BobHandler->>+AliceHandler: Broadcast message
|
||||
AliceHandler->>-Alice: "Bob: 👋🏻👩🏼🦰🐰🤔👩🏼🦰🐇💬👩🏼🦰🐰👍🏻😊👩🏼🦰🐇👋🏻"
|
||||
BobHandler->>-Bob: Message sent
|
||||
end
|
||||
|
||||
Alice->>+AliceHandler: /disconnect
|
||||
Note over AliceHandler: Removes Alice<br/>from map
|
||||
AliceHandler->>-Alice: Disconnection confirmation
|
||||
AliceHandler->>MainServer: Thread terminated
|
||||
MainServer-->>AliceHandler: destroy
|
||||
|
||||
Bob->>+BobHandler: /disconnect
|
||||
Note over BobHandler: Removes Bob<br/>from map
|
||||
BobHandler->>-Bob: Disconnection confirmation
|
||||
BobHandler->>MainServer: Thread terminated
|
||||
MainServer-->>BobHandler: destroy
|
||||
|
||||
Note over Alice,Bob: Close sockets
|
||||
```
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package clientserver.client;
|
||||
|
||||
import clientserver.server.Server;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
@@ -8,8 +9,6 @@ import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Scanner;
|
||||
|
||||
import clientserver.server.Server;
|
||||
|
||||
public class Client {
|
||||
|
||||
InetAddress clientAddress;
|
||||
@@ -27,6 +26,7 @@ public class Client {
|
||||
}
|
||||
|
||||
private static class MessageReceiver implements Runnable {
|
||||
|
||||
private final DatagramSocket socket;
|
||||
private volatile boolean running = true;
|
||||
|
||||
@@ -43,21 +43,27 @@ public class Client {
|
||||
while (running && !socket.isClosed()) {
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
DatagramPacket packet = new DatagramPacket(
|
||||
buffer,
|
||||
buffer.length
|
||||
);
|
||||
|
||||
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
|
||||
System.out.println("\n>>> Message from server: \n" + message);
|
||||
System.out.print("Enter your message (or 'exit' to quit): ");
|
||||
} catch (SocketTimeoutException e) {
|
||||
System.out.println(message);
|
||||
} catch (SocketTimeoutException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
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");
|
||||
@@ -128,16 +134,23 @@ public class Client {
|
||||
System.out.println("Enter your Pseudo :");
|
||||
|
||||
// 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));
|
||||
String userPseudo = client.getScanner().next();
|
||||
client.setPseudo(userPseudo);
|
||||
|
||||
System.out.println("Enter the IP address of the server :");
|
||||
try {
|
||||
client.serverAddress = InetAddress.getByName(client.getScanner().next());
|
||||
client.serverAddress = InetAddress.getByName(
|
||||
client.getScanner().next()
|
||||
);
|
||||
} 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:");
|
||||
@@ -146,26 +159,40 @@ public class Client {
|
||||
// (morph (/127.0.0.1:39056) : ...)
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private static void deconnexionClient(DatagramSocket socketClient, Client client, int handlerPort) {
|
||||
private static void deconnexionClient(
|
||||
DatagramSocket socketClient,
|
||||
Client client,
|
||||
int handlerPort
|
||||
) {
|
||||
try {
|
||||
String disconnectMsg = "DISCONNECT";
|
||||
String disconnectMsg = "/disconnect";
|
||||
byte[] disconnectData = disconnectMsg.getBytes();
|
||||
DatagramPacket disconnectPacket = new DatagramPacket(
|
||||
disconnectData,
|
||||
disconnectData.length,
|
||||
client.getServerAddress(),
|
||||
handlerPort);
|
||||
handlerPort
|
||||
);
|
||||
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) {
|
||||
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;
|
||||
|
||||
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 '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) {
|
||||
System.out.print("Enter your message: ");
|
||||
String message = this.getScanner().nextLine();
|
||||
|
||||
if ("exit".equalsIgnoreCase(message)) {
|
||||
if ("/disconnect".equalsIgnoreCase(message)) {
|
||||
running = false;
|
||||
} 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();
|
||||
}
|
||||
} else {
|
||||
System.err.println("Unexpected response from the server: " + reponse);
|
||||
System.err.println(
|
||||
"Unexpected response from the server: " + reponse
|
||||
);
|
||||
}
|
||||
|
||||
// 8 - Libérer le canal
|
||||
|
||||
@@ -10,10 +10,9 @@ import java.util.Properties;
|
||||
|
||||
public class MistralDirectAPI {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String apiKey;
|
||||
private static String apiKey;
|
||||
|
||||
// Load API key from properties file
|
||||
static {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
FileInputStream input = new FileInputStream("config.properties");
|
||||
@@ -22,18 +21,22 @@ public class MistralDirectAPI {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not load API key: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String payload =
|
||||
public static String translateToEmojis(String message) {
|
||||
String payload = String.format(
|
||||
"""
|
||||
{
|
||||
"model": "mistral-medium",
|
||||
"messages": [
|
||||
{ "role": "user", "content": "Reverse turing test." }
|
||||
{ "role": "system", "content": "Please translate the following message to emojis only. Do not include any comments. End the emoji message with ;" },
|
||||
{ "role": "user", "content": "%s" }
|
||||
]
|
||||
}
|
||||
""";
|
||||
""",
|
||||
message
|
||||
);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://api.mistral.ai/v1/chat/completions"))
|
||||
@@ -49,10 +52,26 @@ public class MistralDirectAPI {
|
||||
request,
|
||||
HttpResponse.BodyHandlers.ofString()
|
||||
);
|
||||
System.out.println("Response Code: " + response.statusCode());
|
||||
System.out.println("Response Body: " + response.body());
|
||||
String responseBody = response.body();
|
||||
return extractContent(responseBody);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
System.err.println("Error occurred: " + e.getMessage());
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
private static String extractContent(String jsonResponse) {
|
||||
// Find the content field in the response
|
||||
int contentIndex = jsonResponse.indexOf("\"content\":\"");
|
||||
if (contentIndex != -1) {
|
||||
int startIndex = contentIndex + "\"content\":\"".length();
|
||||
|
||||
int endIndex = jsonResponse.indexOf(";", startIndex);
|
||||
|
||||
if (endIndex != -1) {
|
||||
return jsonResponse.substring(startIndex, endIndex);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,24 +9,20 @@ public class ClientHandler implements Runnable {
|
||||
private final DatagramSocket clientHandlerSocket;
|
||||
private final Client client;
|
||||
private boolean running = true;
|
||||
private final MessageProcessor messageProcessor;
|
||||
|
||||
public ClientHandler(DatagramSocket socket, Client client) {
|
||||
this.clientHandlerSocket = socket;
|
||||
this.client = client;
|
||||
this.messageProcessor = new MessageProcessor(
|
||||
client,
|
||||
clientHandlerSocket,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
private void sendConnectedClients(String message) {
|
||||
if (message.equals("/list")) {
|
||||
Server.sendMessage(clientHandlerSocket, Server.getPseudos(), client.getAddress(), client.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeconnexion(String message) {
|
||||
if (message.equals("DISCONNECT")) {
|
||||
System.out.println("Client deconnection : " + prettyPrint(client));
|
||||
Server.removeClientHandler(client.getPseudo());
|
||||
stop();
|
||||
}
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -36,43 +32,39 @@ public class ClientHandler implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public String prettyPrint(Client client) {
|
||||
public static String prettyPrint(Client client) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append(client.getPseudo() +
|
||||
str.append(
|
||||
client.getPseudo() +
|
||||
" (" +
|
||||
client.getAddress() +
|
||||
":" +
|
||||
client.getPort() +
|
||||
")");
|
||||
")"
|
||||
);
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println(
|
||||
"Started handler for client " +
|
||||
prettyPrint(client));
|
||||
System.out.println("Started handler for client " + prettyPrint(client));
|
||||
|
||||
while (running && !clientHandlerSocket.isClosed()) {
|
||||
DatagramPacket packet = Server.receivedPacket(clientHandlerSocket);
|
||||
if (packet == null)
|
||||
continue;
|
||||
if (packet == null) continue;
|
||||
|
||||
String message = new String(
|
||||
packet.getData(),
|
||||
0,
|
||||
packet.getLength());
|
||||
System.out.println(
|
||||
prettyPrint(client) + " : " +
|
||||
message);
|
||||
packet.getLength()
|
||||
);
|
||||
System.out.println(prettyPrint(client) + " : " + message);
|
||||
|
||||
// Possibilies
|
||||
sendConnectedClients(message); // /list command
|
||||
handleDeconnexion(message); // exit command or DISCONNECT
|
||||
messageProcessor.processMessage(message);
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
"Client handler terminated for " +
|
||||
prettyPrint(client));
|
||||
"Client handler terminated for " + prettyPrint(client)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
170
app/src/main/java/clientserver/server/MessageProcessor.java
Normal file
170
app/src/main/java/clientserver/server/MessageProcessor.java
Normal file
@@ -0,0 +1,170 @@
|
||||
package clientserver.server;
|
||||
|
||||
import clientserver.client.Client;
|
||||
import clientserver.common.MistralDirectAPI;
|
||||
import java.net.DatagramSocket;
|
||||
|
||||
public class MessageProcessor {
|
||||
|
||||
private final Client client;
|
||||
private final DatagramSocket clientHandlerSocket;
|
||||
private final ClientHandler clientHandler;
|
||||
|
||||
public MessageProcessor(
|
||||
Client client,
|
||||
DatagramSocket clientHandlerSocket,
|
||||
ClientHandler clientHandler
|
||||
) {
|
||||
this.client = client;
|
||||
this.clientHandlerSocket = clientHandlerSocket;
|
||||
this.clientHandler = clientHandler;
|
||||
}
|
||||
|
||||
public void processMessage(String message) {
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.startsWith("/msg ")) {
|
||||
handlePrivateMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case "/list":
|
||||
handleListCommand();
|
||||
break;
|
||||
case "/help":
|
||||
handleHelpCommand();
|
||||
break;
|
||||
case "/msg":
|
||||
handleMsgCommand();
|
||||
break;
|
||||
case "/disconnect":
|
||||
handleDisconnect();
|
||||
break;
|
||||
default:
|
||||
// Broadcast message to all clients
|
||||
broadcastMessageEmoji(message);
|
||||
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 broadcastMessage(String message) {
|
||||
String formattedMessage = "> " + client.getPseudo() + ": " + message;
|
||||
|
||||
for (ClientHandler handler : Server.getAllClientHandlers()) {
|
||||
Client targetClient = handler.getClient();
|
||||
Server.sendMessage(
|
||||
clientHandlerSocket,
|
||||
formattedMessage,
|
||||
targetClient.getAddress(),
|
||||
targetClient.getPort()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastMessageEmoji(String message) {
|
||||
// Translate message to emojis
|
||||
String emojiMessage = MistralDirectAPI.translateToEmojis(message);
|
||||
String formattedMessage =
|
||||
"> " + client.getPseudo() + ": " + emojiMessage;
|
||||
|
||||
for (ClientHandler handler : Server.getAllClientHandlers()) {
|
||||
Client targetClient = handler.getClient();
|
||||
Server.sendMessage(
|
||||
clientHandlerSocket,
|
||||
formattedMessage,
|
||||
targetClient.getAddress(),
|
||||
targetClient.getPort()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMsgCommand() {
|
||||
Server.sendMessage(
|
||||
clientHandlerSocket,
|
||||
"> /msg <target> <message> : send a message to a client",
|
||||
client.getAddress(),
|
||||
client.getPort()
|
||||
);
|
||||
}
|
||||
|
||||
private void handleListCommand() {
|
||||
Server.sendMessage(
|
||||
clientHandlerSocket,
|
||||
Server.getPseudos(),
|
||||
client.getAddress(),
|
||||
client.getPort()
|
||||
);
|
||||
}
|
||||
|
||||
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() {
|
||||
System.out.println(
|
||||
"Client deconnection : " + ClientHandler.prettyPrint(client)
|
||||
);
|
||||
Server.removeClientHandler(client.getPseudo());
|
||||
clientHandler.stop();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -13,7 +14,10 @@ public class Server {
|
||||
private int mainServerPort;
|
||||
private DatagramSocket mainServerSocket;
|
||||
private boolean isRunning;
|
||||
private static Map<String, ClientHandler> mapPseudosConnectedClientsHandlers = new ConcurrentHashMap<>();
|
||||
private static Map<
|
||||
String,
|
||||
ClientHandler
|
||||
> mapPseudosConnectedClientsHandlers = new ConcurrentHashMap<>();
|
||||
|
||||
public Server(int port) {
|
||||
this.mainServerPort = port;
|
||||
@@ -45,7 +49,8 @@ public class Server {
|
||||
byte[] receivedData = new byte[1024];
|
||||
DatagramPacket receivedPacket = new DatagramPacket(
|
||||
receivedData,
|
||||
receivedData.length);
|
||||
receivedData.length
|
||||
);
|
||||
|
||||
try {
|
||||
socket.receive(receivedPacket); // Blocking call
|
||||
@@ -57,81 +62,84 @@ public class Server {
|
||||
}
|
||||
|
||||
public static String packetToMessage(DatagramPacket packet) {
|
||||
return new String(
|
||||
packet.getData(),
|
||||
0,
|
||||
packet.getLength());
|
||||
return new String(packet.getData(), 0, packet.getLength());
|
||||
}
|
||||
|
||||
public static void sendMessage(
|
||||
DatagramSocket socket,
|
||||
String message,
|
||||
java.net.InetAddress address,
|
||||
int port) {
|
||||
int port
|
||||
) {
|
||||
try {
|
||||
byte[] sendData = message.getBytes();
|
||||
DatagramPacket packetToSend = new DatagramPacket(
|
||||
sendData,
|
||||
sendData.length,
|
||||
address,
|
||||
port);
|
||||
port
|
||||
);
|
||||
socket.send(packetToSend);
|
||||
} catch (IOException e) {
|
||||
System.err.println(
|
||||
"Failed to send message to " + address + ":" + port);
|
||||
"Failed to send message to " + address + ":" + port
|
||||
);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNewConnection() {
|
||||
DatagramPacket packet = receivedPacket(mainServerSocket);
|
||||
if (packet == null)
|
||||
return;
|
||||
if (packet == null) return;
|
||||
|
||||
int originalClientPort = packet.getPort();
|
||||
InetAddress clientAddress = packet.getAddress();
|
||||
;
|
||||
|
||||
// Log the initial connection request
|
||||
System.out.println(
|
||||
"New connection request from " +
|
||||
clientAddress +
|
||||
":" +
|
||||
originalClientPort);
|
||||
originalClientPort
|
||||
);
|
||||
|
||||
// Process the received message
|
||||
String pseudoMessage = new String(packet.getData(), 0, packet.getLength());
|
||||
String pseudoMessage = new String(
|
||||
packet.getData(),
|
||||
0,
|
||||
packet.getLength()
|
||||
);
|
||||
|
||||
System.out.println(
|
||||
"Received message from " +
|
||||
clientAddress +
|
||||
":" +
|
||||
originalClientPort +
|
||||
": " +
|
||||
pseudoMessage);
|
||||
|
||||
pseudoMessage
|
||||
);
|
||||
// Create a new socket for this client
|
||||
DatagramSocket clientHandlerSocket = createNewSocket();
|
||||
if (clientHandlerSocket == null)
|
||||
return;
|
||||
|
||||
if (clientHandlerSocket == null) return;
|
||||
int clientHandlerLocalPort = clientHandlerSocket.getLocalPort();
|
||||
|
||||
Client client = new Client(clientAddress, originalClientPort);
|
||||
client.setPseudo(pseudoMessage);
|
||||
|
||||
client.setPseudo(pseudoMessage);
|
||||
// Send new port information to client
|
||||
String response = "PORT:" + clientHandlerLocalPort;
|
||||
|
||||
sendMessage(
|
||||
mainServerSocket,
|
||||
response,
|
||||
clientAddress,
|
||||
packet.getPort());
|
||||
|
||||
packet.getPort()
|
||||
);
|
||||
// Create and start a ClientHandler for this connection
|
||||
ClientHandler handler = new ClientHandler(clientHandlerSocket, client);
|
||||
|
||||
Thread thread = new Thread(handler);
|
||||
thread.start();
|
||||
|
||||
// Add client handler to mapPseudosConnectedClients
|
||||
addConnectedClientHandler(pseudoMessage, handler);
|
||||
}
|
||||
@@ -143,7 +151,8 @@ public class Server {
|
||||
handleNewConnection();
|
||||
} catch (Exception e) {
|
||||
System.err.println(
|
||||
"Error handling connection: " + e.getMessage());
|
||||
"Error handling connection: " + e.getMessage()
|
||||
);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -151,12 +160,34 @@ public class Server {
|
||||
}
|
||||
|
||||
public static String getPseudos() {
|
||||
StringBuilder pseudos = new StringBuilder("All connected clients:\n");
|
||||
mapPseudosConnectedClientsHandlers.forEach((k, v) -> pseudos.append("Pseudo: ").append(k).append("\n"));
|
||||
StringBuilder pseudos = new StringBuilder();
|
||||
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();
|
||||
}
|
||||
|
||||
public void addConnectedClientHandler(String pseudo, ClientHandler clientHandler) {
|
||||
public static ClientHandler getClientHandler(String pseudo) {
|
||||
return mapPseudosConnectedClientsHandlers.get(pseudo);
|
||||
}
|
||||
|
||||
public static Collection<ClientHandler> getAllClientHandlers() {
|
||||
return mapPseudosConnectedClientsHandlers.values();
|
||||
}
|
||||
|
||||
public void addConnectedClientHandler(
|
||||
String pseudo,
|
||||
ClientHandler clientHandler
|
||||
) {
|
||||
mapPseudosConnectedClientsHandlers.putIfAbsent(pseudo, clientHandler);
|
||||
}
|
||||
|
||||
|
||||
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@@ -0,0 +1 @@
|
||||
org.gradle.console=plain
|
||||
Reference in New Issue
Block a user