From b75cd6de1ae0402687b074f13978f2d21719fabc Mon Sep 17 00:00:00 2001 From: Yohan Simard Date: Wed, 2 Dec 2020 19:05:53 +0100 Subject: [PATCH] Add ConnectionListener + improve network classes + first work on username change impl --- .idea/clavardator.iml | 2 + .idea/codeStyles/Project.xml | 15 +--- .idea/compiler.xml | 2 +- .idea/misc.xml | 2 +- .../java/fr/insa/clavardator/MainApp.java | 10 ++- .../fr/insa/clavardator/chat/Message.java | 9 ++- .../network/ConnectionListener.java | 67 ++++++++++++++++ .../clavardator/network/NetDiscoverer.java | 78 ++++++++++++------- .../clavardator/network/PeerConnection.java | 44 +++++++---- .../fr/insa/clavardator/users/ActiveUser.java | 50 ++++++++++-- .../insa/clavardator/users/CurrentUser.java | 11 --- .../java/fr/insa/clavardator/users/User.java | 9 ++- .../fr/insa/clavardator/users/UserList.java | 15 +++- .../clavardator/users/UsernameChange.java | 21 +++++ 14 files changed, 247 insertions(+), 88 deletions(-) create mode 100644 .idea/clavardator.iml create mode 100644 src/main/java/fr/insa/clavardator/network/ConnectionListener.java create mode 100644 src/main/java/fr/insa/clavardator/users/UsernameChange.java diff --git a/.idea/clavardator.iml b/.idea/clavardator.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/.idea/clavardator.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 645f4a0..3198fd2 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,18 +3,9 @@ - diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..ac216bc 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1cd02f1..c66ef85 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -9,7 +9,7 @@ - + \ No newline at end of file diff --git a/src/main/java/fr/insa/clavardator/MainApp.java b/src/main/java/fr/insa/clavardator/MainApp.java index de8e789..fb1807d 100644 --- a/src/main/java/fr/insa/clavardator/MainApp.java +++ b/src/main/java/fr/insa/clavardator/MainApp.java @@ -12,6 +12,8 @@ import javafx.stage.Stage; public class MainApp extends Application { + NetDiscoverer netDiscoverer; + public static void main(String[] args) { launch(args); } @@ -34,16 +36,18 @@ public class MainApp extends Application { stage.setMaximized(true); stage.show(); + netDiscoverer = new NetDiscoverer(); + // Network discovery test - NetDiscoverer.discoverActiveUsers("Broadcast", + netDiscoverer.discoverActiveUsers("Broadcast", (ipAddr, data) -> System.out.println("User detected at address : " + ipAddr.toString()), Throwable::printStackTrace); - NetDiscoverer.startDiscoveryListening("Yohan", null, Throwable::printStackTrace); + netDiscoverer.startDiscoveryListening("Yohan", null, Throwable::printStackTrace); } @Override public void stop() throws Exception { - NetDiscoverer.stopDiscovery(); + netDiscoverer.stopDiscovery(); super.stop(); } } \ No newline at end of file diff --git a/src/main/java/fr/insa/clavardator/chat/Message.java b/src/main/java/fr/insa/clavardator/chat/Message.java index 54ecaa9..62fce4c 100644 --- a/src/main/java/fr/insa/clavardator/chat/Message.java +++ b/src/main/java/fr/insa/clavardator/chat/Message.java @@ -1,14 +1,13 @@ package fr.insa.clavardator.chat; -import fr.insa.clavardator.users.CurrentUser; import fr.insa.clavardator.users.User; import java.io.Serializable; public class Message implements Serializable { - private String text; - private final User recipient; + private final String text; private final User sender; + private final User recipient; public Message(User sender, User recipient) { this(sender, recipient, ""); @@ -27,4 +26,8 @@ public class Message implements Serializable { public User getSender() { return sender; } + + public User getRecipient() { + return recipient; + } } diff --git a/src/main/java/fr/insa/clavardator/network/ConnectionListener.java b/src/main/java/fr/insa/clavardator/network/ConnectionListener.java new file mode 100644 index 0000000..81d3d6f --- /dev/null +++ b/src/main/java/fr/insa/clavardator/network/ConnectionListener.java @@ -0,0 +1,67 @@ +package fr.insa.clavardator.network; + +import fr.insa.clavardator.users.ActiveUser; +import fr.insa.clavardator.util.ErrorCallback; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public class ConnectionListener { + Acceptor acceptor = null; + + ConnectionListener() { + } + + public void acceptConnection(NewConnectionCallback callback, ErrorCallback errorCallback) { + if (acceptor != null) { + acceptor.stopAccepting(); + } + acceptor = new Acceptor(callback, errorCallback); + } + + public void stopAccepting() { + acceptor.stopAccepting(); + } + + + private static class Acceptor extends Thread { + private boolean shouldStop = false; + NewConnectionCallback callback; + ErrorCallback errorCallback; + ServerSocket server; + + public Acceptor(NewConnectionCallback callback, ErrorCallback errorCallback) { + this.callback = callback; + this.errorCallback = errorCallback; + } + + @Override + public void run() { + try { + server = new ServerSocket(PeerConnection.TCP_PORT); + while (!shouldStop) { + Socket clientSocket = server.accept(); + ActiveUser newUser = new ActiveUser(clientSocket); + callback.onNewConnection(newUser); + } + } catch (IOException e) { + errorCallback.onError(e); + } + + } + + public void stopAccepting() { + shouldStop = true; + try { + server.close(); + } catch (IOException ignored) { + } + } + } + + + interface NewConnectionCallback { + void onNewConnection(ActiveUser user); + } +} diff --git a/src/main/java/fr/insa/clavardator/network/NetDiscoverer.java b/src/main/java/fr/insa/clavardator/network/NetDiscoverer.java index c54e89e..22c8a11 100644 --- a/src/main/java/fr/insa/clavardator/network/NetDiscoverer.java +++ b/src/main/java/fr/insa/clavardator/network/NetDiscoverer.java @@ -16,11 +16,12 @@ public class NetDiscoverer { private static final int BROADCAST_BUFFER_SIZE = 50; private static final int RESPONSE_BUFFER_SIZE = 50; - private static DatagramSocket broadcastListener; - private static DatagramSocket responseListener; - private static boolean shouldStop = false; + private BroadcastSender broadcastSender; + private BroadcastListener socket; + private ResponseSender responseSender; + private ResponseListener responseListener; - private NetDiscoverer() { + public NetDiscoverer() { } /** @@ -30,11 +31,13 @@ public class NetDiscoverer { * @param callback function to call when a new user is discovered * @param errorCallback function to call on error */ - public static void discoverActiveUsers(String broadcastMessage, ResponseReceivedCallback callback, ErrorCallback errorCallback) { - ResponseListener receiver = new ResponseListener(callback, errorCallback); - BroadcastSender sender = new BroadcastSender(broadcastMessage, errorCallback); - receiver.start(); - sender.start(); + public void discoverActiveUsers(String broadcastMessage, ResponseReceivedCallback callback, ErrorCallback errorCallback) { + if (responseListener != null) + responseListener.stopListening(); + responseListener = new ResponseListener(callback, errorCallback); + broadcastSender = new BroadcastSender(broadcastMessage, errorCallback); + responseListener.start(); + broadcastSender.start(); } /** @@ -44,29 +47,31 @@ public class NetDiscoverer { * @param onBroadcastReceived The function to call on success * @param errorCallback The function to call on error */ - public static void startDiscoveryListening(String responseMessage, @Nullable BroadcastReceivedCallback onBroadcastReceived, @Nullable ErrorCallback errorCallback) { - BroadcastListener listener = new BroadcastListener((ipAddr, data) -> { + public void startDiscoveryListening(String responseMessage, @Nullable BroadcastReceivedCallback onBroadcastReceived, @Nullable ErrorCallback errorCallback) { + if (socket != null) + socket.stopListening(); + + socket = new BroadcastListener((ipAddr, data) -> { if (onBroadcastReceived != null) onBroadcastReceived.onBroadcastReceived(ipAddr, data); - ResponseSender sender = new ResponseSender(ipAddr, responseMessage, errorCallback); - sender.start(); + responseSender = new ResponseSender(ipAddr, responseMessage, errorCallback); + responseSender.start(); }, e -> { if (errorCallback != null) errorCallback.onError(e); }); - listener.start(); + socket.start(); } /** * Stop network discovery and listening */ - public static void stopDiscovery() { - shouldStop = true; - if (broadcastListener != null) - broadcastListener.close(); + public void stopDiscovery() { + if (socket != null) + socket.stopListening(); if (responseListener != null) - responseListener.close(); + responseListener.stopListening(); } @@ -105,6 +110,8 @@ public class NetDiscoverer { private static class BroadcastListener extends Thread { private final BroadcastReceivedCallback callback; private final ErrorCallback errorCallback; + private DatagramSocket socket; + private boolean shouldStop = false; /** * Constructs a thread that sends a broadcast over the network, using all available interfaces @@ -121,15 +128,15 @@ public class NetDiscoverer { public void run() { try { while (!shouldStop) { - broadcastListener = new DatagramSocket(null); - broadcastListener.setOption(StandardSocketOptions.SO_REUSEPORT, true); - broadcastListener.setOption(StandardSocketOptions.SO_REUSEADDR, true); - broadcastListener.bind(new InetSocketAddress((InetAddress) null, DISCOVERY_PORT)); + socket = new DatagramSocket(null); + socket.setOption(StandardSocketOptions.SO_REUSEPORT, true); + socket.setOption(StandardSocketOptions.SO_REUSEADDR, true); + socket.bind(new InetSocketAddress((InetAddress) null, DISCOVERY_PORT)); byte[] buffer = new byte[BROADCAST_BUFFER_SIZE]; DatagramPacket receivedPacket = new DatagramPacket(buffer, BROADCAST_BUFFER_SIZE); - broadcastListener.receive(receivedPacket); + socket.receive(receivedPacket); // System.out.println("broadcast received from " + receivedPacket.getAddress().toString()); callback.onBroadcastReceived(receivedPacket.getAddress(), new String(receivedPacket.getData())); } @@ -139,6 +146,11 @@ public class NetDiscoverer { } } } + + public void stopListening() { + shouldStop = true; + socket.close(); + } } private static class ResponseSender extends Thread { @@ -175,6 +187,8 @@ public class NetDiscoverer { private static class ResponseListener extends Thread { private final ResponseReceivedCallback callback; private final ErrorCallback errorCallback; + private DatagramSocket socket; + private boolean shouldStop = false; /** * Constructs a thread that receives all UDP responses, until stopDiscovery() is called @@ -191,14 +205,14 @@ public class NetDiscoverer { public void run() { try { while (!shouldStop) { - responseListener = new DatagramSocket(null); - responseListener.setOption(StandardSocketOptions.SO_REUSEPORT, true); - responseListener.setOption(StandardSocketOptions.SO_REUSEADDR, true); - responseListener.bind(new InetSocketAddress((InetAddress) null, RESPONSE_PORT)); + socket = new DatagramSocket(null); + socket.setOption(StandardSocketOptions.SO_REUSEPORT, true); + socket.setOption(StandardSocketOptions.SO_REUSEADDR, true); + socket.bind(new InetSocketAddress((InetAddress) null, RESPONSE_PORT)); byte[] buffer = new byte[RESPONSE_BUFFER_SIZE]; DatagramPacket receivedPacket = new DatagramPacket(buffer, RESPONSE_BUFFER_SIZE); - responseListener.receive(receivedPacket); + socket.receive(receivedPacket); // System.out.println("response received from " + receivedPacket.getAddress().toString()); callback.onResponseReceived(receivedPacket.getAddress(), new String(receivedPacket.getData())); } @@ -208,6 +222,12 @@ public class NetDiscoverer { } } } + + public void stopListening() { + shouldStop = true; + socket.close(); + } + } diff --git a/src/main/java/fr/insa/clavardator/network/PeerConnection.java b/src/main/java/fr/insa/clavardator/network/PeerConnection.java index ca60fa5..5cc9f75 100644 --- a/src/main/java/fr/insa/clavardator/network/PeerConnection.java +++ b/src/main/java/fr/insa/clavardator/network/PeerConnection.java @@ -1,6 +1,5 @@ package fr.insa.clavardator.network; -import fr.insa.clavardator.chat.Message; import fr.insa.clavardator.util.ErrorCallback; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,13 +12,21 @@ import java.net.InetAddress; import java.net.Socket; public class PeerConnection { - public static final short PORT = 31598; + public static final short TCP_PORT = 31598; + Socket socket; private final ObjectOutputStream outputStream; private final ObjectInputStream inputStream; + private boolean shouldStop = false; public PeerConnection(InetAddress ipAddr) throws IOException { - Socket socket = new Socket(ipAddr, PORT); + socket = new Socket(ipAddr, TCP_PORT); + outputStream = new ObjectOutputStream(socket.getOutputStream()); + inputStream = new ObjectInputStream(socket.getInputStream()); + } + + public PeerConnection(Socket socket) throws IOException { + this.socket = socket; outputStream = new ObjectOutputStream(socket.getOutputStream()); inputStream = new ObjectInputStream(socket.getInputStream()); } @@ -31,22 +38,30 @@ public class PeerConnection { * @param callback The function to call on success * @param errorCallback The function to call on error */ - public void sendMessage(Message message, MessageSentCallback callback, ErrorCallback errorCallback) { + public void send(Serializable message, MessageSentCallback callback, ErrorCallback errorCallback) { Sender sender = new Sender(message, callback, errorCallback); sender.start(); } /** - * Receives a message from the peer + * Subscibe to all incoming messages from the peer * - * @param callback The function to call on success + * @param callback The function to call when a message is received * @param errorCallback The function to call on error */ - public void receiveMessage(MessageReceivedCallback callback, ErrorCallback errorCallback) { + public void receive(MessageReceivedCallback callback, ErrorCallback errorCallback) { Receiver receiver = new Receiver(callback, errorCallback); receiver.start(); } + public void close() { + shouldStop = true; + try { + socket.close(); + } catch (IOException ignored) { + } + } + private class Sender extends Thread { private final Serializable message; @@ -73,7 +88,7 @@ public class PeerConnection { if (callback != null) callback.onMessageSent(); } catch (IOException e) { - if (errorCallback != null) + if (errorCallback != null && !shouldStop) errorCallback.onError(e); } } @@ -83,6 +98,7 @@ public class PeerConnection { private final MessageReceivedCallback callback; private final ErrorCallback errorCallback; + /** * Constructs a thread that receives a message using the socket of the outer class * @@ -97,15 +113,11 @@ public class PeerConnection { @Override public void run() { try { - Object msg = inputStream.readObject(); - if (msg.getClass().isInstance(Message.class)) { - callback.onMessageReceived((Message) msg); - } else { - if (errorCallback != null) - errorCallback.onError(new ClassCastException("The message received is not a valid Message object")); + while(!shouldStop) { + callback.onMessageReceived(inputStream.readObject()); } } catch (IOException | ClassNotFoundException e) { - if (errorCallback != null) + if (errorCallback != null && !shouldStop) errorCallback.onError(e); } } @@ -113,7 +125,7 @@ public class PeerConnection { public interface MessageReceivedCallback { - void onMessageReceived(Message msg); + void onMessageReceived(Object msg); } public interface MessageSentCallback { diff --git a/src/main/java/fr/insa/clavardator/users/ActiveUser.java b/src/main/java/fr/insa/clavardator/users/ActiveUser.java index d08b28e..4c8a406 100644 --- a/src/main/java/fr/insa/clavardator/users/ActiveUser.java +++ b/src/main/java/fr/insa/clavardator/users/ActiveUser.java @@ -2,33 +2,67 @@ package fr.insa.clavardator.users; import fr.insa.clavardator.chat.Message; import fr.insa.clavardator.network.PeerConnection; +import fr.insa.clavardator.util.ErrorCallback; import java.io.IOException; import java.net.InetAddress; +import java.net.Socket; public class ActiveUser extends PeerUser { - private transient PeerConnection connection; + private final transient PeerConnection connection; + /** + * Creates an active user and connects to him/her + * @param username The username of the new user + * @param ipAddr The IP address of the new user + * @throws IOException when an error occurs during the connection + */ public ActiveUser(String username, InetAddress ipAddr) throws IOException { super(username); connection = new PeerConnection(ipAddr); + subscibeToMessages(); + connection.send(new UsernameChange("", "currentUser.username"), null, null); // TODO: get currentUser + error handling + } + + public ActiveUser(Socket socket) throws IOException { + super(""); + connection = new PeerConnection(socket); + subscibeToMessages(); + } + + private void subscibeToMessages() { + connection.receive( + msg -> { + if (msg.getClass().isInstance(UsernameChange.class)) { + setUsername(((UsernameChange)msg).getNewUsername()); + + } else if (msg.getClass().isInstance(Message.class)) { + // TODO + } + }, + error -> { + // TODO + }); } /** - * @param message + * Sends a message to this peer + * @param message The message to send + * @param callback The function to call when the message is sent + * @param errorCallback The function to call on error */ - public void sendMessage(Message message) { -// connection.sendMessage(message); + public void sendMessage(Message message, PeerConnection.MessageSentCallback callback, ErrorCallback errorCallback) { + connection.send(message, callback, errorCallback); } - /** - * @param message + * Closes the connection with the user. + * Must be called before exiting the app. */ - public void onMessageSent(Message message) { + public void destroy() { + connection.close(); } - } diff --git a/src/main/java/fr/insa/clavardator/users/CurrentUser.java b/src/main/java/fr/insa/clavardator/users/CurrentUser.java index 945c0d5..d3034b6 100644 --- a/src/main/java/fr/insa/clavardator/users/CurrentUser.java +++ b/src/main/java/fr/insa/clavardator/users/CurrentUser.java @@ -1,18 +1,7 @@ package fr.insa.clavardator.users; public class CurrentUser extends User { - public CurrentUser(String username, UserList userList) { super(username); } - - /** - * @param username - */ - public void changeUsername(String username) { - pcs.firePropertyChange("username", this.username, username); - this.username = username; - } - - } diff --git a/src/main/java/fr/insa/clavardator/users/User.java b/src/main/java/fr/insa/clavardator/users/User.java index 81edee6..adc668c 100644 --- a/src/main/java/fr/insa/clavardator/users/User.java +++ b/src/main/java/fr/insa/clavardator/users/User.java @@ -22,13 +22,16 @@ public class User implements Serializable, Comparable { this.username = username; } - /** - * @return the current value of username - */ + public String getUsername() { return username; } + protected void setUsername(String newUsername) { + pcs.firePropertyChange("username", this.username, newUsername); + this.username = newUsername; + } + @Override public int compareTo(@NotNull User o) { diff --git a/src/main/java/fr/insa/clavardator/users/UserList.java b/src/main/java/fr/insa/clavardator/users/UserList.java index c46c8d3..b485877 100644 --- a/src/main/java/fr/insa/clavardator/users/UserList.java +++ b/src/main/java/fr/insa/clavardator/users/UserList.java @@ -15,6 +15,7 @@ public class UserList { private ArrayList activeUsers; private ArrayList inactiveUsers; private final DatabaseController db = new DatabaseController(); + private final NetDiscoverer netDiscoverer = new NetDiscoverer(); // Make this class observable private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); @@ -28,6 +29,18 @@ public class UserList { public UserList() { } + /** + * Close all running threads, sockets and db connection + * Must be called before exiting the app + */ + public void destroy() { + netDiscoverer.stopDiscovery(); +// db.close(); + for (ActiveUser user : activeUsers) { + user.destroy(); + } + } + /** * @param username * @return boolean @@ -50,7 +63,7 @@ public class UserList { * */ public void discoverActiveUsers(ErrorCallback errorCallback) { - NetDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> { + netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> { ActiveUser newUser = null; try { newUser = new ActiveUser("", ipAddr); // TODO find username diff --git a/src/main/java/fr/insa/clavardator/users/UsernameChange.java b/src/main/java/fr/insa/clavardator/users/UsernameChange.java new file mode 100644 index 0000000..2985854 --- /dev/null +++ b/src/main/java/fr/insa/clavardator/users/UsernameChange.java @@ -0,0 +1,21 @@ +package fr.insa.clavardator.users; + +import java.io.Serializable; + +public class UsernameChange implements Serializable { + private final String prevUsername; // Not sure if this is useful + private final String newUsername; + + public UsernameChange(String previousUsername, String newUsername) { + this.prevUsername = previousUsername; + this.newUsername = newUsername; + } + + public String getPreviousUsername() { + return prevUsername; + } + + public String getNewUsername() { + return newUsername; + } +}