From eb9d8fe6e7bab515c1ad1c79680a2dfa50936a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luc=C3=A0s?= Date: Mon, 5 Dec 2022 17:54:49 +0100 Subject: [PATCH] FEAT: Interface --- .idea/workspace.xml | 119 ++++++++++--------- README.md | 2 +- src/client/Client.java | 81 +++++++++---- src/client/ListenThread.java | 17 +++ src/{serveur => server}/Server.java | 30 ++++- src/{serveur => server}/ThreadServer.java | 21 +++- src/ui/FenetreClient.form | 132 ++++++++++++++++++++++ src/ui/FenetreClient.java | 123 ++++++++++++++++++++ src/ui/FenetreErreur.java | 15 +++ src/ui/FenetreServeur.form | 52 +++++++++ src/ui/FenetreServeur.java | 49 ++++++++ 11 files changed, 560 insertions(+), 81 deletions(-) rename src/{serveur => server}/Server.java (52%) rename src/{serveur => server}/ThreadServer.java (75%) create mode 100644 src/ui/FenetreClient.form create mode 100644 src/ui/FenetreClient.java create mode 100644 src/ui/FenetreErreur.java create mode 100644 src/ui/FenetreServeur.form create mode 100644 src/ui/FenetreServeur.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 99ea954..13c4130 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,18 +2,17 @@ - - - - + + + + + - - + - - - - + + + - { + "keyToString": { + "Downloaded.Files.Path.Enabled": "false", + "Repository.Attach.Annotations": "false", + "Repository.Attach.JavaDocs": "false", + "Repository.Attach.Sources": "false", + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "WebServerToolWindowFactoryState": "false", + "codeWithMe.voiceChat.enabledByDefault": "false", + "com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrary": "JUnit5", + "com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrarySuperClass.JUnit5": "", + "create.test.in.the.same.root": "true", + "last_opened_file_path": "/Users/lucas/Documents/GitHub/projet-mini-chat/src/serveur", + "project.structure.last.edited": "Modules", + "project.structure.proportion": "0.15", + "project.structure.side.proportion": "0.2", + "settings.editor.selected.configurable": "preferences.lookFeel" } -}]]> +} + @@ -77,20 +77,7 @@ - - - + - - @@ -171,7 +188,7 @@ - + diff --git a/README.md b/README.md index 2abbebd..4be1df6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Avant la communication Utiliser le RSA, avant toute communication. -Le client et le serveur génèrent 2 clés (une publique et une privées) +Le client et le server génèrent 2 clés (une publique et une privées) Ils s'échangent leurs clé publiques. ![img](https://upload.wikimedia.org/wikipedia/commons/thumb/0/01/Asymmetric_cryptography_-_step_1.svg/800px-Asymmetric_cryptography_-_step_1.svg.png) diff --git a/src/client/Client.java b/src/client/Client.java index 818f298..6be4bf1 100644 --- a/src/client/Client.java +++ b/src/client/Client.java @@ -1,8 +1,11 @@ package client; +import ui.FenetreClient; +import ui.FenetreErreur; import utils.RSA; import utils.ResolutionDeNom; +import javax.swing.*; import java.io.EOFException; import java.io.IOException; import java.io.ObjectInputStream; @@ -11,56 +14,90 @@ import java.net.ConnectException; import java.net.Socket; import java.security.KeyPair; import java.security.PublicKey; +import java.util.ArrayList; import java.util.Objects; import java.util.Scanner; -public class Client { +public class Client extends Thread{ private final String SERVER_IP; private final int SERVER_PORT; + private final FenetreClient fenetre; + private String pseudo; + private final Scanner scanner; - private ObjectInputStream inputStream; - private ObjectOutputStream outputStream; private KeyPair clientKeys; private PublicKey serverKey; - private String pseudo; + private ObjectInputStream inputStream; + private ObjectOutputStream outputStream; - public Client(String ip, int port) { + private ArrayList fileAttenteMessage; + + public Client(String ip, int port, String pseudo) { this.SERVER_IP = ResolutionDeNom.getIPAddress(ip); this.SERVER_PORT = port; + this.pseudo = pseudo; + this.fenetre = null; + this.scanner = new Scanner(System.in); this.clientKeys = RSA.genererCle(); + + this.fileAttenteMessage = new ArrayList<>(); } - public void start() { - System.out.print("Saisir un pseudo: "); - pseudo = scanner.nextLine(); + public Client(String ip, int port, String pseudo, FenetreClient fenetre) { + this.SERVER_IP = ResolutionDeNom.getIPAddress(ip); + this.SERVER_PORT = port; + this.pseudo = pseudo; + this.fenetre = fenetre; + + this.scanner = new Scanner(System.in); + this.clientKeys = RSA.genererCle(); + + this.fileAttenteMessage = new ArrayList<>(); + } + + @Override + public void run() { + if (pseudo == null) { + System.out.print("Pseudo: "); + pseudo = scanner.nextLine(); + } try (Socket socket = new Socket(SERVER_IP, SERVER_PORT)) { inputStream = new ObjectInputStream(socket.getInputStream()); outputStream = new ObjectOutputStream(socket.getOutputStream()); exchangeKeys(); - sendMessage(pseudo); // Ecoute du serveur - ListenThread threadClient = new ListenThread(this); + ListenThread threadClient = new ListenThread(this, fenetre); threadClient.start(); // Ecoute de l'entrée du clavier - System.out.println("Tappez 'bye' pour quitter\n"); - String message; + System.out.println("Tapez 'bye' pour quitter\n"); + String message = ""; do { - message = scanner.nextLine(); - sendMessage(message); + if (fileAttenteMessage.size() > 0) { + message = fileAttenteMessage.remove(0); + sendMessage(message); + } } while (!Objects.equals(message, "bye")); } catch (ConnectException e) { - System.err.println("Serveur non trouvé"); - }catch (EOFException e) { - System.err.println("Connexion perdue"); + System.err.println("Serveur non trouvé"); // TODO throw error + if (fenetre != null) { + new FenetreErreur("Serveur non trouvé", fenetre); + fenetre.deconnexion(); + } + } catch (EOFException e) { + System.err.println("Connexion perdue"); // TODO throw error + if (fenetre != null) { + new FenetreErreur("Connexion perdue", fenetre); + fenetre.deconnexion(); + } } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } @@ -84,9 +121,11 @@ public class Client { return RSA.decrypter(messageCrypte, clientKeys.getPrivate()); } - - public static void main(String[] args) { - Client client = new Client("localhost", 4444); - client.start(); + /** + * Ajoute un message à la file d'attente + * @param message + */ + public void addMessage(String message) { + this.fileAttenteMessage.add(message); } } diff --git a/src/client/ListenThread.java b/src/client/ListenThread.java index 2f2500d..434bb92 100644 --- a/src/client/ListenThread.java +++ b/src/client/ListenThread.java @@ -1,5 +1,8 @@ package client; +import ui.FenetreClient; +import ui.FenetreErreur; + import java.io.EOFException; import java.io.IOException; import java.net.SocketException; @@ -10,9 +13,16 @@ import java.net.SocketException; public class ListenThread extends Thread { private final Client client; + private final FenetreClient fenetre; public ListenThread(Client client) { this.client = client; + this.fenetre = null; + } + + public ListenThread(Client client, FenetreClient fenetre) { + this.client = client; + this.fenetre = fenetre; } @Override @@ -23,11 +33,18 @@ public class ListenThread extends Thread { while (true) { message = client.getMessage(); System.out.println(message); + if (fenetre != null) { + fenetre.displayNewMessage(message); + } } } catch (SocketException ignored) { System.out.println("Vous avez quitté le salon"); } catch (EOFException ignored) { System.err.println("Connexion perdue"); + if (fenetre != null) { + new FenetreErreur("Connexion perdue", fenetre); + fenetre.deconnexion(); + } }catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } diff --git a/src/serveur/Server.java b/src/server/Server.java similarity index 52% rename from src/serveur/Server.java rename to src/server/Server.java index e8c8516..e95d53f 100644 --- a/src/serveur/Server.java +++ b/src/server/Server.java @@ -1,27 +1,45 @@ -package serveur; +package server; + +import ui.FenetreServeur; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; -public class Server { +public class Server extends Thread{ private final int PORT; private final ArrayList threads; + private final FenetreServeur fenetre; public Server(int port) { this.PORT = port; this.threads = new ArrayList<>(); + + this.fenetre = null; } - public void start() { + public Server(int port, FenetreServeur fenetre) { + this.PORT = port; + this.fenetre = fenetre; + this.threads = new ArrayList<>(); + } + + @Override + public void run() { try (ServerSocket serversocket = new ServerSocket(PORT)) { - System.out.println("Server is started..."); + String msg = "Serveur démarré !"; + System.out.println(msg); + fenetre.displayNewMessage(msg); + while (true) { Socket socket = serversocket.accept(); - System.out.printf("Nouvelle connexion : %s\n", socket); - ThreadServer thread = new ThreadServer(socket, threads); + String connexionMessage = String.format("Nouvelle connexion : %s\n", socket); + System.out.printf(connexionMessage); + fenetre.displayNewMessage(connexionMessage); + + ThreadServer thread = new ThreadServer(socket, threads, fenetre); threads.add(thread); thread.start(); diff --git a/src/serveur/ThreadServer.java b/src/server/ThreadServer.java similarity index 75% rename from src/serveur/ThreadServer.java rename to src/server/ThreadServer.java index 18d7cb2..0edb559 100644 --- a/src/serveur/ThreadServer.java +++ b/src/server/ThreadServer.java @@ -1,5 +1,6 @@ -package serveur; +package server; +import ui.FenetreServeur; import utils.RSA; import java.io.*; @@ -16,11 +17,22 @@ public class ThreadServer extends Thread { private final ObjectInputStream inputStream; private final ObjectOutputStream outputStream; private final KeyPair serverKeys; + private final FenetreServeur fenetre; private PublicKey clientKey; private String clientPseudo; public ThreadServer(Socket socket, ArrayList threads) throws IOException { this.threads = threads; + this.fenetre = null; + this.outputStream = new ObjectOutputStream(socket.getOutputStream()); + this.inputStream = new ObjectInputStream(socket.getInputStream()); + + this.serverKeys = RSA.genererCle(); + } + + public ThreadServer(Socket socket, ArrayList threads, FenetreServeur fenetre) throws IOException { + this.threads = threads; + this.fenetre = fenetre; this.outputStream = new ObjectOutputStream(socket.getOutputStream()); this.inputStream = new ObjectInputStream(socket.getInputStream()); @@ -43,6 +55,8 @@ public class ThreadServer extends Thread { sendMessageToEveryone(reply, true); } while (!Objects.equals(message, "bye")); + } catch (EOFException e) { + System.out.printf("%s c'est déconnecté\n", clientPseudo); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { @@ -53,6 +67,7 @@ public class ThreadServer extends Thread { e.printStackTrace(); } } + } public void exchangeKeys() throws IOException, ClassNotFoundException { @@ -63,7 +78,7 @@ public class ThreadServer extends Thread { outputStream.writeObject(serverKeys.getPublic()); } - public String getMessage() throws IOException, ClassNotFoundException { + public String getMessage() throws EOFException, IOException, ClassNotFoundException { byte[] messageCrypte = (byte[]) inputStream.readObject(); return RSA.decrypter(messageCrypte, serverKeys.getPrivate()); } @@ -72,6 +87,7 @@ public class ThreadServer extends Thread { byte[] messageCrypte = RSA.encrypter(message, clientKey); outputStream.writeObject(messageCrypte); if (log) System.out.println(message); + if (log && fenetre != null) {fenetre.displayNewMessage(message);} } public void sendMessageToEveryone(String message, boolean log) throws IOException { @@ -79,5 +95,6 @@ public class ThreadServer extends Thread { thread.sendMessage(message, false); } if (log) System.out.println(message); + if (log && fenetre != null) {fenetre.displayNewMessage(message);} } } diff --git a/src/ui/FenetreClient.form b/src/ui/FenetreClient.form new file mode 100644 index 0000000..6200eed --- /dev/null +++ b/src/ui/FenetreClient.form @@ -0,0 +1,132 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/ui/FenetreClient.java b/src/ui/FenetreClient.java new file mode 100644 index 0000000..1232987 --- /dev/null +++ b/src/ui/FenetreClient.java @@ -0,0 +1,123 @@ +package ui; + +import client.Client; + +import javax.swing.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.Objects; + +public class FenetreClient extends JFrame { + private JTextField addressInput, portInput, pseudoInput, messageInput; + private JButton connexionButton, envoyerButton, deconnexionButton; + private JPanel mainPanel; + private JTextArea chatArea; + private JScrollPane scrollPane; + + private Client client; + + public FenetreClient() { + super("Fenêtre client"); + add(mainPanel); + + // On ne peut pas saisir dans la boîte de dialogue directement + chatArea.setEnabled(false); + + // Au lancement, on ne peut pas envoyer de message sans être connecté au serveur + messageInput.setEnabled(false); + envoyerButton.setEnabled(false); + + // Action des boutons + messageInput.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) envoyerMessage(); + } + }); + connexionButton.addActionListener(e -> connexion()); + envoyerButton.addActionListener(e -> envoyerMessage()); + deconnexionButton.addActionListener(e -> deconnexion()); + + // Quand on clique sur fermer la fenêtre, on ferme la connexion + this.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {deconnexion();} + }); + + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setSize(400, 250); + this.setVisible(true); + } + + private void envoyerMessage() { + { + String message = this.messageInput.getText(); + + try { + if (Objects.equals(message, "bye")) deconnexion(); + else this.client.sendMessage(message); + + this.messageInput.setText(""); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } + + public void connexion() { + String address = addressInput.getText(); + int port = Integer.parseInt(portInput.getText()); + String pseudo = pseudoInput.getText(); + + if (Objects.equals(pseudo.strip(), "")) { + new FenetreErreur("Pseudo vide !", this); + this.pseudoInput.setText(""); + return; + } + + this.client = new Client(address, port, pseudo, this); + this.client.start(); + + addressInput.setEnabled(false); + portInput.setEnabled(false); + pseudoInput.setEnabled(false); + + messageInput.setEnabled(true); + envoyerButton.setEnabled(true); + } + + public void deconnexion() { + if (client == null) return; + + // Déconnecte le client + this.client.addMessage("bye"); + this.client = null; + + // Vide le chat + this.chatArea.setText(""); + + // Active les boutons pour changer de serveur + addressInput.setEnabled(true); + portInput.setEnabled(true); + pseudoInput.setEnabled(true); + + // Désactive les commandes de chats + messageInput.setEnabled(false); + envoyerButton.setEnabled(false); + } + + public void displayNewMessage(String message) { + // Ajoute le message + chatArea.append(message + "\n"); + + // Va en bas de la fenêtre + JScrollBar vertical = scrollPane.getVerticalScrollBar(); + vertical.setValue(vertical.getMaximum()); + } + + public static void main(String[] args) { + new FenetreClient(); + } +} diff --git a/src/ui/FenetreErreur.java b/src/ui/FenetreErreur.java new file mode 100644 index 0000000..7e39d3e --- /dev/null +++ b/src/ui/FenetreErreur.java @@ -0,0 +1,15 @@ +package ui; + +import javax.swing.*; + +public class FenetreErreur { + + public FenetreErreur(String message, JFrame parent) { + final String TITLE = "Erreur"; + JOptionPane.showMessageDialog(parent, message, TITLE, JOptionPane.ERROR_MESSAGE); + } + + public static void main(String[] args) { + new FenetreErreur("Message", null); + } +} \ No newline at end of file diff --git a/src/ui/FenetreServeur.form b/src/ui/FenetreServeur.form new file mode 100644 index 0000000..0f7e00e --- /dev/null +++ b/src/ui/FenetreServeur.form @@ -0,0 +1,52 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/ui/FenetreServeur.java b/src/ui/FenetreServeur.java new file mode 100644 index 0000000..e280413 --- /dev/null +++ b/src/ui/FenetreServeur.java @@ -0,0 +1,49 @@ +package ui; + +import server.Server; + +import javax.swing.*; + +public class FenetreServeur extends JFrame { + private JTextArea chatArea; + private JPanel mainPanel; + private JScrollPane scrollPane; + private JTextField portInput; + private JButton demarrerButton; + private Server server; + + public FenetreServeur() { + super("Fenetre Serveur"); + add(mainPanel); + + chatArea.setEnabled(false); + + demarrerButton.addActionListener(e -> demarrerServeur()); + + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setSize(400, 250); + this.setVisible(true); + } + + public void demarrerServeur() { + int port = Integer.parseInt(portInput.getText()); + server = new Server(port, this); + server.start(); + + portInput.setEnabled(false); + demarrerButton.setEnabled(false); + } + + public void displayNewMessage(String message) { + // Ajoute le message + chatArea.append(message + "\n"); + + // Va en bas de la fenêtre + JScrollBar vertical = scrollPane.getVerticalScrollBar(); + vertical.setValue(vertical.getMaximum()); + } + + public static void main(String[] args) { + new FenetreServeur(); + } +}