diff --git a/.project b/.project new file mode 100644 index 0000000..9f05120 --- /dev/null +++ b/.project @@ -0,0 +1,28 @@ + + + ClientServer + Project ClientServer created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1743445961452 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..d230334 --- /dev/null +++ b/.settings/org.eclipse.buildship.core.prefs @@ -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.0-202503271314/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 diff --git a/README.md b/README.md index c940f86..2d82657 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,39 @@ +# ClientServer + +## Build + +```bash +./gradlew build +``` + +## Run + +### Client + +```bash +./gradlew run --args='client' +``` + +or + +```bash +./gradlew runServer +``` + +### Server + +```bash +./gradlew run --args='server' +``` + +or + +```bash +./gradlew runServer +``` + +## Documentation + ```mermaid sequenceDiagram Note over Client, Serveur: DatagramSocket("localhost", 66666) diff --git a/app/.classpath b/app/.classpath new file mode 100644 index 0000000..282ada5 --- /dev/null +++ b/app/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/.project b/app/.project new file mode 100644 index 0000000..ecb5e8f --- /dev/null +++ b/app/.project @@ -0,0 +1,34 @@ + + + app + Project app created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + + + 1743445961456 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/app/.settings/org.eclipse.buildship.core.prefs b/app/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..b1886ad --- /dev/null +++ b/app/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir=.. +eclipse.preferences.version=1 diff --git a/app/build.gradle b/app/build.gradle index 990d74d..fc08b4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,3 +40,19 @@ tasks.named('test') { // Use JUnit Platform for unit tests. 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'] +} diff --git a/app/src/main/java/clientserver/App.java b/app/src/main/java/clientserver/App.java index 755f153..658a06e 100644 --- a/app/src/main/java/clientserver/App.java +++ b/app/src/main/java/clientserver/App.java @@ -1,5 +1,8 @@ package clientserver; +import clientserver.client.Client; +import clientserver.server.Server; + public class App { public String getGreeting() { @@ -7,8 +10,49 @@ public class App { } 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"); } } diff --git a/app/src/main/java/clientserver/Client.java b/app/src/main/java/clientserver/Client.java deleted file mode 100644 index 98bd287..0000000 --- a/app/src/main/java/clientserver/Client.java +++ /dev/null @@ -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); - } - } -} diff --git a/app/src/main/java/clientserver/Server.java b/app/src/main/java/clientserver/Server.java deleted file mode 100644 index 71ca9db..0000000 --- a/app/src/main/java/clientserver/Server.java +++ /dev/null @@ -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); - } - } -} diff --git a/app/src/main/java/clientserver/client/Client.java b/app/src/main/java/clientserver/client/Client.java new file mode 100644 index 0000000..05f65d2 --- /dev/null +++ b/app/src/main/java/clientserver/client/Client.java @@ -0,0 +1,89 @@ +package clientserver.client; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.concurrent.TimeUnit; + +public class Client { + + InetAddress clientAddress; + int clientPort; + + public void setPort(int port) { + this.clientPort = port; + } + + public int getPort() { + return this.clientPort; + } + + public InetAddress getAddress() { + return this.clientAddress; + } + + public Client(InetAddress address, int port) { + this.clientAddress = address; + this.clientPort = port; + } + + public static void main(String[] args) { + try { + // 1 - Création du canal avec un port libre + DatagramSocket socketClient = new DatagramSocket(); + InetAddress serverAddress = InetAddress.getByName("localhost"); + int serverPort = 6666; + + // 2 - Envoyer un message au serveur + String message = "Connection request"; + byte[] sendData = message.getBytes(); + DatagramPacket sendPacket = new DatagramPacket( + sendData, + sendData.length, + serverAddress, + serverPort + ); + socketClient.send(sendPacket); + System.out.println("New connection request sent"); + + // 3 - Recevoir + byte[] receivedData = new byte[1024]; // tampon de réception + DatagramPacket receivedPacket = new DatagramPacket( + receivedData, + receivedData.length + ); + socketClient.receive(receivedPacket); + String reponse = new String( + receivedPacket.getData(), + 0, + receivedPacket.getLength() + ); + + if (reponse.startsWith("PORT:")) { + int newPort = Integer.parseInt(reponse.substring(5)); + + System.out.println("Connected on port:" + newPort); + + // 4 - Communiquer sur le nouveau port + for (int i = 0; i < 20; i++) { + String messagePort = "Test"; + byte[] envoyeesPort = messagePort.getBytes(); + DatagramPacket paquetPort = new DatagramPacket( + envoyeesPort, + envoyeesPort.length, + serverAddress, + newPort + ); + socketClient.send(paquetPort); + System.out.println("Test sent"); + TimeUnit.SECONDS.sleep(1); + } + } + + // 5 - Libérer le canal + socketClient.close(); + } catch (Exception e) { + System.err.println(e); + } + } +} diff --git a/app/src/main/java/clientserver/MistralDirectAPI.java b/app/src/main/java/clientserver/common/MistralDirectAPI.java similarity index 94% rename from app/src/main/java/clientserver/MistralDirectAPI.java rename to app/src/main/java/clientserver/common/MistralDirectAPI.java index c1615e6..65bc0bc 100644 --- a/app/src/main/java/clientserver/MistralDirectAPI.java +++ b/app/src/main/java/clientserver/common/MistralDirectAPI.java @@ -1,11 +1,11 @@ -package clientserver; +package clientserver.common; +import java.io.FileInputStream; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.io.FileInputStream; import java.util.Properties; public class MistralDirectAPI { @@ -30,7 +30,7 @@ public class MistralDirectAPI { { "model": "mistral-medium", "messages": [ - { "role": "user", "content": "Explain recursion like I'm five." } + { "role": "user", "content": "Reverse turing test." } ] } """; diff --git a/app/src/main/java/clientserver/server/ClientHandler.java b/app/src/main/java/clientserver/server/ClientHandler.java new file mode 100644 index 0000000..7d959a1 --- /dev/null +++ b/app/src/main/java/clientserver/server/ClientHandler.java @@ -0,0 +1,74 @@ +package clientserver.server; + +import clientserver.client.Client; +import java.net.DatagramPacket; +import java.net.DatagramSocket; + +public class ClientHandler implements Runnable { + + private final DatagramSocket socket; + private final Client client; + private boolean running = true; + + public ClientHandler(DatagramSocket socket, Client client) { + this.socket = socket; + this.client = client; + } + + public void stop() { + running = false; + if (socket != null && !socket.isClosed()) { + socket.close(); + } + } + + @Override + public void run() { + System.out.println( + "Started handler for client " + + client.getAddress() + + ":" + + client.getPort() + ); + + // try { + // socket.setSoTimeout(30000); + // } catch (Exception e) { + // System.err.println("Could not set socket timeout"); + // } + + while (running && !socket.isClosed()) { + DatagramPacket packet = Server.receivedPacket(socket); + if (packet == null) continue; + + String message = new String( + packet.getData(), + 0, + packet.getLength() + ); + System.out.println( + "Received from " + + client.getAddress() + + ":" + + client.getPort() + + ": " + + message + ); + // Reply with echo + // String response = "ECHO: " + message; + // Server.sendMessage( + // socket, + // response, + // client.getAddress(), + // client.getPort() + // ); + } + + System.out.println( + "Client handler terminated for " + + client.getAddress() + + ":" + + client.getPort() + ); + } +} diff --git a/app/src/main/java/clientserver/server/Server.java b/app/src/main/java/clientserver/server/Server.java new file mode 100644 index 0000000..ef2f21d --- /dev/null +++ b/app/src/main/java/clientserver/server/Server.java @@ -0,0 +1,140 @@ +package clientserver.server; + +import clientserver.client.Client; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; + +public class Server { + + private int mainServerPort; + private DatagramSocket mainServerSocket; + private boolean isRunning; + + 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 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; + + Client client = new Client(packet.getAddress(), packet.getPort()); + + // Log the initial connection request + System.out.println( + "New connection request from " + + client.getAddress() + + ":" + + client.getPort() + ); + + // Process the received message + String message = new String(packet.getData(), 0, packet.getLength()); + System.out.println( + "Received message from " + + client.getAddress() + + ":" + + client.getPort() + + ": " + + message + ); + + // Create a new socket for this client + DatagramSocket clientSocket = createNewSocket(); + if (clientSocket == null) return; + + int newPort = clientSocket.getLocalPort(); + client.setPort(newPort); + + // Send new port information to client + String response = "PORT:" + newPort; + sendMessage( + mainServerSocket, + response, + client.getAddress(), + packet.getPort() + ); + + // Create and start a ClientHandler for this connection + ClientHandler handler = new ClientHandler(clientSocket, client); + Thread thread = new Thread(handler); + thread.start(); + } + + 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"); + } +}