Compare commits
18 Commits
1f52c66270
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ee3291465 | |||
| b0e9f1bb4e | |||
| b8bc715868 | |||
| 13800486ba | |||
| bd0cf3c601 | |||
|
|
55a7499339 | ||
|
|
3f4ff22cea | ||
|
|
5b3abadfb6 | ||
|
|
4894c2555d | ||
| d0c9f59ad2 | |||
| 1837041996 | |||
| 2459dca82e | |||
| 8599c14979 | |||
| f0ea538c21 | |||
| d3b523048f | |||
| aaebae7850 | |||
|
|
e227ddc48c | ||
| 00ef197628 |
28
.project
Normal file
28
.project
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>ClientServer</name>
|
||||||
|
<comment>Project ClientServer created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1743445961452</id>
|
||||||
|
<name></name>
|
||||||
|
<type>30</type>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
|
</projectDescription>
|
||||||
13
.settings/org.eclipse.buildship.core.prefs
Normal file
13
.settings/org.eclipse.buildship.core.prefs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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)
|
||||||
|
connection.project.dir=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
gradle.user.home=
|
||||||
|
java.home=/usr/lib64/jvm/java-21-openjdk-21
|
||||||
|
jvm.arguments=
|
||||||
|
offline.mode=false
|
||||||
|
override.workspace.settings=true
|
||||||
|
show.console.view=true
|
||||||
|
show.executions.view=true
|
||||||
106
README.md
106
README.md
@@ -1,7 +1,105 @@
|
|||||||
|
# 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
|
||||||
|
./gradlew build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
### Client
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew runClient
|
||||||
|
```
|
||||||
|
|
||||||
|
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 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
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
Note over Client, Serveur: DatagramSocket("localhost", 66666)
|
participant MainServer
|
||||||
Client->>+Serveur: DatagramPacket("Salve !", 7, "localhost", 6666)
|
|
||||||
Note over Serveur, Client: DatagramSocket("localhost", 66666)
|
participant Alice
|
||||||
Serveur->>+Client: DatagramPacket("Accusé de réception", 19, "localhost", 6666)
|
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
|
||||||
```
|
```
|
||||||
|
|||||||
19
app/.classpath
Normal file
19
app/.classpath
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="gradle_scope" value="main"/>
|
||||||
|
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="gradle_scope" value="test"/>
|
||||||
|
<attribute name="gradle_used_by_scope" value="test"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21/"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||||
|
<classpathentry kind="output" path="bin/default"/>
|
||||||
|
</classpath>
|
||||||
34
app/.project
Normal file
34
app/.project
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>app</name>
|
||||||
|
<comment>Project app created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1743445961456</id>
|
||||||
|
<name></name>
|
||||||
|
<type>30</type>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
|
</projectDescription>
|
||||||
2
app/.settings/org.eclipse.buildship.core.prefs
Normal file
2
app/.settings/org.eclipse.buildship.core.prefs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
connection.project.dir=..
|
||||||
|
eclipse.preferences.version=1
|
||||||
@@ -40,3 +40,20 @@ tasks.named('test') {
|
|||||||
// Use JUnit Platform for unit tests.
|
// Use JUnit Platform for unit tests.
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task runServer(type: JavaExec) {
|
||||||
|
description = 'Runs the server'
|
||||||
|
group = 'application'
|
||||||
|
mainClass = application.mainClass
|
||||||
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
|
args = ['--server']
|
||||||
|
}
|
||||||
|
|
||||||
|
task runClient(type: JavaExec) {
|
||||||
|
description = 'Runs the client'
|
||||||
|
group = 'application'
|
||||||
|
mainClass = application.mainClass
|
||||||
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
|
args = ['--client']
|
||||||
|
standardInput = System.in
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package clientserver;
|
package clientserver;
|
||||||
|
|
||||||
|
import clientserver.client.Client;
|
||||||
|
import clientserver.server.Server;
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
public String getGreeting() {
|
public String getGreeting() {
|
||||||
@@ -7,8 +10,49 @@ public class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println(new App().getGreeting());
|
String mode = null;
|
||||||
|
|
||||||
MistralDirectAPI.main(new String[] {});
|
// Parse arguments
|
||||||
|
for (String arg : args) {
|
||||||
|
switch (arg.toLowerCase()) {
|
||||||
|
case "--client":
|
||||||
|
mode = "client";
|
||||||
|
break;
|
||||||
|
case "--server":
|
||||||
|
mode = "server";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Unknown argument: " + arg);
|
||||||
|
printUsage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if mode is specified
|
||||||
|
if (mode == null) {
|
||||||
|
System.out.println("No mode specified.");
|
||||||
|
printUsage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute based on mode
|
||||||
|
switch (mode) {
|
||||||
|
case "client":
|
||||||
|
System.out.println("Starting client...");
|
||||||
|
Client.main(new String[] {});
|
||||||
|
break;
|
||||||
|
case "server":
|
||||||
|
System.out.println("Starting server...");
|
||||||
|
Server server = new Server(6666);
|
||||||
|
server.run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printUsage() {
|
||||||
|
System.out.println("Usage: ./gradlew run --args='option'");
|
||||||
|
System.out.println("Options:");
|
||||||
|
System.out.println(" --client Run in client mode");
|
||||||
|
System.out.println(" --server Run in server mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package clientserver;
|
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
|
|
||||||
public class Client {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
try {
|
|
||||||
// 1 - Création du canal avec un port libre
|
|
||||||
DatagramSocket socketClient = new DatagramSocket();
|
|
||||||
InetAddress adresseClient = InetAddress.getByName("localhost");
|
|
||||||
byte[] envoyees; // tampon d'émission
|
|
||||||
byte[] recues = new byte[1024]; // tampon de réception
|
|
||||||
// 2 - Émettre
|
|
||||||
String message = "hello serveur RX302";
|
|
||||||
envoyees = message.getBytes();
|
|
||||||
DatagramPacket messageEnvoye = new DatagramPacket(envoyees, envoyees.length, adresseClient, 6666);
|
|
||||||
socketClient.send(messageEnvoye);
|
|
||||||
// 3 - Recevoir
|
|
||||||
DatagramPacket paquetRecu = new DatagramPacket(recues, recues.length);
|
|
||||||
socketClient.receive(paquetRecu);
|
|
||||||
String reponse = new String(paquetRecu.getData(), 0, paquetRecu.getLength());
|
|
||||||
System.out.println(reponse + " @" + paquetRecu.getAddress() + ":" + paquetRecu.getPort());
|
|
||||||
// 4 - Libérer le canal
|
|
||||||
socketClient.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package clientserver;
|
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
public class Server {
|
|
||||||
static void scannerUDP(int startPort, int endPort) {
|
|
||||||
try {
|
|
||||||
for (int i = startPort; i < endPort; i++) {
|
|
||||||
try (DatagramSocket socket = new DatagramSocket(i)) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("Port n°" + i + " déjà occupé");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
try {
|
|
||||||
// 1 - Création du canal
|
|
||||||
DatagramSocket socketServeur = new DatagramSocket(null);
|
|
||||||
// 2 - Réservation du port
|
|
||||||
InetSocketAddress adresse = new InetSocketAddress("localhost", 6666);
|
|
||||||
socketServeur.bind(adresse);
|
|
||||||
byte[] recues = new byte[1024]; // tampon d'émission
|
|
||||||
byte[] envoyees; // tampon de réception
|
|
||||||
// 3 - Recevoir
|
|
||||||
DatagramPacket paquetRecu = new DatagramPacket(recues, recues.length);
|
|
||||||
socketServeur.receive(paquetRecu);
|
|
||||||
InetAddress adrClient = paquetRecu.getAddress();
|
|
||||||
int prtClient = paquetRecu.getPort();
|
|
||||||
System.out.println("Nouveau client : @" + adrClient + ":" + prtClient);
|
|
||||||
// 4 - Émettre
|
|
||||||
String reponse = "Serveur RX302 ready";
|
|
||||||
envoyees = reponse.getBytes();
|
|
||||||
DatagramPacket paquetEnvoye = new DatagramPacket(envoyees, envoyees.length, adrClient, prtClient);
|
|
||||||
socketServeur.send(paquetEnvoye);
|
|
||||||
// 5 - Libérer le canal
|
|
||||||
socketServeur.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
283
app/src/main/java/clientserver/client/Client.java
Normal file
283
app/src/main/java/clientserver/client/Client.java
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
package clientserver.client;
|
||||||
|
|
||||||
|
import clientserver.server.Server;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Client {
|
||||||
|
|
||||||
|
InetAddress clientAddress;
|
||||||
|
int clientPort;
|
||||||
|
private InetAddress serverAddress;
|
||||||
|
private int serverPort;
|
||||||
|
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(message);
|
||||||
|
} 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) {
|
||||||
|
this.clientPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return this.clientPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetAddress getAddress() {
|
||||||
|
return this.clientAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetAddress getServerAddress() {
|
||||||
|
return this.serverAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServerPort() {
|
||||||
|
return this.serverPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerPort(int port) {
|
||||||
|
this.serverPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPseudo() {
|
||||||
|
return this.pseudo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPseudo(String pseudo) {
|
||||||
|
this.pseudo = pseudo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scanner getScanner() {
|
||||||
|
return this.scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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.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()
|
||||||
|
);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
System.err.println(
|
||||||
|
"The address you choose create an UnknownHostException: " + e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Enter the port of the server:");
|
||||||
|
client.setServerPort(client.getScanner().nextInt());
|
||||||
|
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) {
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
String disconnectMsg = "/disconnect";
|
||||||
|
byte[] disconnectData = disconnectMsg.getBytes();
|
||||||
|
DatagramPacket disconnectPacket = new DatagramPacket(
|
||||||
|
disconnectData,
|
||||||
|
disconnectData.length,
|
||||||
|
client.getServerAddress(),
|
||||||
|
handlerPort
|
||||||
|
);
|
||||||
|
socketClient.send(disconnectPacket);
|
||||||
|
System.out.println(
|
||||||
|
"Deconnection sended on the server port: " + handlerPort
|
||||||
|
);
|
||||||
|
} catch (Exception 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 '/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) {
|
||||||
|
String message = this.getScanner().nextLine();
|
||||||
|
|
||||||
|
if ("/disconnect".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) {
|
||||||
|
try {
|
||||||
|
// 1 - Création du canal avec un port libre
|
||||||
|
DatagramSocket socketClient = new DatagramSocket();
|
||||||
|
|
||||||
|
// 2 - Création du client avec l'envoi du pseudo pour l'établissement de la
|
||||||
|
// connexion
|
||||||
|
Client client = createAndConfigureClient(socketClient);
|
||||||
|
|
||||||
|
// 3 - Recevoir la réponse initiale du serveur
|
||||||
|
DatagramPacket receivedPacket = Server.receivedPacket(socketClient);
|
||||||
|
String reponse = Server.packetToMessage(receivedPacket);
|
||||||
|
|
||||||
|
if (reponse.startsWith("PORT:")) {
|
||||||
|
int handlerPort = Integer.parseInt(reponse.substring(5));
|
||||||
|
System.out.println("Connected on handler port: " + handlerPort);
|
||||||
|
|
||||||
|
client.setClientSocket(socketClient);
|
||||||
|
client.setHandlerPort(handlerPort);
|
||||||
|
|
||||||
|
// 4 - Lancer uniquement le thread de réception des messages
|
||||||
|
MessageReceiver receiver = new MessageReceiver(socketClient);
|
||||||
|
Thread receiverThread = new Thread(receiver);
|
||||||
|
receiverThread.setDaemon(true);
|
||||||
|
receiverThread.start();
|
||||||
|
|
||||||
|
// 5 - Éxecution du client, bloquant tant que le client est connecté.
|
||||||
|
client.run();
|
||||||
|
|
||||||
|
// 6 - Arrêter le thread de réception
|
||||||
|
receiver.stop();
|
||||||
|
try {
|
||||||
|
receiverThread.join(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println(
|
||||||
|
"Unexpected response from the server: " + reponse
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8 - Libérer le canal
|
||||||
|
socketClient.close();
|
||||||
|
System.out.println("Client closed");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
package clientserver;
|
package clientserver.common;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
public class MistralDirectAPI {
|
public class MistralDirectAPI {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
private static String apiKey;
|
||||||
String apiKey;
|
|
||||||
|
|
||||||
// Load API key from properties file
|
static {
|
||||||
try {
|
try {
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
FileInputStream input = new FileInputStream("config.properties");
|
FileInputStream input = new FileInputStream("config.properties");
|
||||||
@@ -22,18 +21,22 @@ public class MistralDirectAPI {
|
|||||||
input.close();
|
input.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("Could not load API key: " + e.getMessage());
|
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",
|
"model": "mistral-medium",
|
||||||
"messages": [
|
"messages": [
|
||||||
{ "role": "user", "content": "Explain recursion like I'm five." }
|
{ "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()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create("https://api.mistral.ai/v1/chat/completions"))
|
.uri(URI.create("https://api.mistral.ai/v1/chat/completions"))
|
||||||
@@ -49,10 +52,26 @@ public class MistralDirectAPI {
|
|||||||
request,
|
request,
|
||||||
HttpResponse.BodyHandlers.ofString()
|
HttpResponse.BodyHandlers.ofString()
|
||||||
);
|
);
|
||||||
System.out.println("Response Code: " + response.statusCode());
|
String responseBody = response.body();
|
||||||
System.out.println("Response Body: " + response.body());
|
return extractContent(responseBody);
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
System.err.println("Error occurred: " + e.getMessage());
|
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 "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
70
app/src/main/java/clientserver/server/ClientHandler.java
Normal file
70
app/src/main/java/clientserver/server/ClientHandler.java
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package clientserver.server;
|
||||||
|
|
||||||
|
import clientserver.client.Client;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
if (clientHandlerSocket != null && !clientHandlerSocket.isClosed()) {
|
||||||
|
clientHandlerSocket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String prettyPrint(Client client) {
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
str.append(
|
||||||
|
client.getPseudo() +
|
||||||
|
" (" +
|
||||||
|
client.getAddress() +
|
||||||
|
":" +
|
||||||
|
client.getPort() +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
System.out.println("Started handler for client " + prettyPrint(client));
|
||||||
|
|
||||||
|
while (running && !clientHandlerSocket.isClosed()) {
|
||||||
|
DatagramPacket packet = Server.receivedPacket(clientHandlerSocket);
|
||||||
|
if (packet == null) continue;
|
||||||
|
|
||||||
|
String message = new String(
|
||||||
|
packet.getData(),
|
||||||
|
0,
|
||||||
|
packet.getLength()
|
||||||
|
);
|
||||||
|
System.out.println(prettyPrint(client) + " : " + message);
|
||||||
|
|
||||||
|
messageProcessor.processMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(
|
||||||
|
"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();
|
||||||
|
}
|
||||||
|
}
|
||||||
197
app/src/main/java/clientserver/server/Server.java
Normal file
197
app/src/main/java/clientserver/server/Server.java
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
package clientserver.server;
|
||||||
|
|
||||||
|
import clientserver.client.Client;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class Server {
|
||||||
|
|
||||||
|
private int mainServerPort;
|
||||||
|
private DatagramSocket mainServerSocket;
|
||||||
|
private boolean isRunning;
|
||||||
|
private static Map<
|
||||||
|
String,
|
||||||
|
ClientHandler
|
||||||
|
> mapPseudosConnectedClientsHandlers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public Server(int port) {
|
||||||
|
this.mainServerPort = port;
|
||||||
|
mainServerSocket = createSocket(this.mainServerPort);
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DatagramSocket createSocket(int port) {
|
||||||
|
try {
|
||||||
|
return new DatagramSocket(port);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to bind server socket on port " + port);
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DatagramSocket createNewSocket() {
|
||||||
|
try {
|
||||||
|
return new DatagramSocket(); // Reserve a random port
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to create new server socket");
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DatagramPacket receivedPacket(DatagramSocket socket) {
|
||||||
|
byte[] receivedData = new byte[1024];
|
||||||
|
DatagramPacket receivedPacket = new DatagramPacket(
|
||||||
|
receivedData,
|
||||||
|
receivedData.length
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
socket.receive(receivedPacket); // Blocking call
|
||||||
|
return receivedPacket;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String packetToMessage(DatagramPacket packet) {
|
||||||
|
return new String(packet.getData(), 0, packet.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendMessage(
|
||||||
|
DatagramSocket socket,
|
||||||
|
String message,
|
||||||
|
java.net.InetAddress address,
|
||||||
|
int port
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
byte[] sendData = message.getBytes();
|
||||||
|
DatagramPacket packetToSend = new DatagramPacket(
|
||||||
|
sendData,
|
||||||
|
sendData.length,
|
||||||
|
address,
|
||||||
|
port
|
||||||
|
);
|
||||||
|
socket.send(packetToSend);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println(
|
||||||
|
"Failed to send message to " + address + ":" + port
|
||||||
|
);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNewConnection() {
|
||||||
|
DatagramPacket packet = receivedPacket(mainServerSocket);
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
// Process the received message
|
||||||
|
String pseudoMessage = new String(
|
||||||
|
packet.getData(),
|
||||||
|
0,
|
||||||
|
packet.getLength()
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println(
|
||||||
|
"Received message from " +
|
||||||
|
clientAddress +
|
||||||
|
":" +
|
||||||
|
originalClientPort +
|
||||||
|
": " +
|
||||||
|
pseudoMessage
|
||||||
|
);
|
||||||
|
// Create a new socket for this client
|
||||||
|
DatagramSocket clientHandlerSocket = createNewSocket();
|
||||||
|
|
||||||
|
if (clientHandlerSocket == null) return;
|
||||||
|
int clientHandlerLocalPort = clientHandlerSocket.getLocalPort();
|
||||||
|
|
||||||
|
Client client = new Client(clientAddress, originalClientPort);
|
||||||
|
|
||||||
|
client.setPseudo(pseudoMessage);
|
||||||
|
// Send new port information to client
|
||||||
|
String response = "PORT:" + clientHandlerLocalPort;
|
||||||
|
|
||||||
|
sendMessage(
|
||||||
|
mainServerSocket,
|
||||||
|
response,
|
||||||
|
clientAddress,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
System.out.println("Server started on port " + mainServerPort);
|
||||||
|
while (isRunning) {
|
||||||
|
try {
|
||||||
|
handleNewConnection();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(
|
||||||
|
"Error handling connection: " + e.getMessage()
|
||||||
|
);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("Server shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPseudos() {
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeClientHandler(String pseudo) {
|
||||||
|
mapPseudosConnectedClientsHandlers.remove(pseudo);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
org.gradle.console=plain
|
||||||
Reference in New Issue
Block a user