diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java index de54a56..009c604 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java @@ -6,7 +6,6 @@ import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ClientPinger; import java.net.Socket; import java.io.*; -import java.net.SocketException; import java.net.UnknownHostException; import java.util.Objects; import java.util.Scanner; @@ -88,11 +87,12 @@ public class Client { try { chatMsg = in.readLine(); //todo: maybe if if (chatMsg != null) { - parse(chatMsg); //todo: i think this trows an error BC chatMsg is null if client disconnects - } + parse(chatMsg); + } else { System.out.println("chatMsg is null");} } catch (IOException e) { //e.printStackTrace(); LOGGER.debug("Exception while trying to read message"); + disconnectFromServer(); } } @@ -102,7 +102,8 @@ public class Client { } /** - * Sends a message to the server, as is. + * Sends a message to the server, as is. The message has to already be protocol-formatted. ALL + * communication with the server has to happen via this method! * * @param msg the message sent. Should already be protocol-formatted. */ @@ -114,20 +115,20 @@ public class Client { } catch (IOException e) { //e.printStackTrace(); LOGGER.debug("unable to send msg: " + msg); + disconnectFromServer(); } } /** * parses a received message according to the client protocol. - * * @param msg the message to be parsed. */ public void parse(String msg) { JClientProtocolParser.parse(msg, this); } - public void closeEverything() { + public void disconnectFromServer() { try { if (in != null) { in.close(); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java index a04faf5..0c23a97 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java @@ -1,7 +1,6 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.client; import ch.unibas.dmi.dbis.cs108.BudaLogConfig; -import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -41,7 +40,7 @@ public class JClientProtocolParser { System.out.println(msg.substring(6)); break; case "QUITC": - c.closeEverything(); + c.disconnectFromServer(); break; default: System.out.println("Received unknown command"); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java index f08eec7..d7195ff 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java @@ -3,6 +3,7 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.server; import ch.unibas.dmi.dbis.cs108.BudaLogConfig; import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ServerPinger; import java.io.*; +import java.net.InetAddress; import java.net.Socket; import java.util.HashSet; import java.util.Scanner; @@ -17,20 +18,23 @@ public class ClientHandler implements Runnable { private BufferedWriter out; private BufferedReader in; private Socket socket; + private InetAddress ip; private Scanner sc; public ServerPinger serverPinger; public static HashSet connectedClients = new HashSet<>(); + public static HashSet disconnectedClients = new HashSet<>(); //todo: implement re-connection public static HashSet lobby = new HashSet<>(); public static HashSet ghostClients = new HashSet<>(); /** * Implements the login logic in client-server architecture. - * + * @param ip the ip of the client, used for re-connection. * @param socket the socket on which to make the connection. */ - public ClientHandler(Socket socket) { + public ClientHandler(Socket socket, InetAddress ip) { try { + this.ip = ip; this.socket = socket; this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); this.in = new BufferedReader(new InputStreamReader((socket.getInputStream()))); @@ -87,6 +91,7 @@ public class ClientHandler implements Runnable { } catch (IOException e) { //e.printStackTrace(); LOGGER.debug("Exception while trying to read message"); + removeClientOnConnectionLoss(); break; } } @@ -146,9 +151,10 @@ public class ClientHandler implements Runnable { } } - /** Sends a given message to client + /** Sends a given message to client. The message has to already be protocol-formatted. ALL + * communication with the client has to happen via this method! * todo: check for exception if out is closed. - * @param msg the given message + * @param msg the given message. Should already be protocol-formatted. */ public void sendMsgToClient(String msg) { try { @@ -159,17 +165,39 @@ public class ClientHandler implements Runnable { } catch (IOException e) { //e.printStackTrace(); LOGGER.debug("unable to send msg: " + msg); + removeClientOnConnectionLoss(); } } + /** + * Removes & disconnects the client. To be used if a severe connection loss is detected (i.e. if trying to + * send / receive a message throws an exception, not just if ping-pong detects a connection loss). + * This is very similar to removeClientOnLogout(), however removeClientOnLogout() should only be used for + * regular quitting, since removeClientOnLogout() cannot handle a client with connection loss. + */ + public void removeClientOnConnectionLoss() { + connectedClients.remove(this); + disconnectClient(); + broadcastAnnouncement(getClientUserName() + " has left the server due to a connection loss."); + disconnectedClients.add(this); + } /** - * Does exactly what it says on the tin, closes all connections of Client to Server. + * Does exactly what it says on the tin, closes all connections of Client to Server and removes + * the client. To be used if the client requests logout or in other "regular" situations. Use + * removeClientOnConnectionLoss() if the client has to be removed due to a connection loss. */ - public void disconnectClient() { + public void removeClientOnLogout() { broadcastAnnouncement(getClientUserName() + " has left the server."); sendMsgToClient("QUITC"); connectedClients.remove(this); + disconnectClient(); + } + + /** + * Closes the client's socket, in, and out. + */ + public void disconnectClient() { socket = this.getSocket(); in = this.getIn(); out = this.getOut(); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java index 91f99c7..e651911 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java @@ -56,7 +56,7 @@ public class JServerProtocolParser { break; case "QUITS": //safely disconnects the user - h.disconnectClient(); + h.removeClientOnLogout(); break; default: System.out.println("Received unknown command"); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java index 0b157da..012a4b7 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Server.java @@ -27,10 +27,10 @@ public class Server { */ public void startServer() { try { - System.out.println("Port 42069 is open on " + this.serverSocket.getInetAddress()); //TODO: this is always 0.0.0.0 + System.out.println("Port 42069 is open."); while (!serverSocket.isClosed()) { Socket socket = serverSocket.accept(); - ClientHandler nextClient = new ClientHandler(socket); + ClientHandler nextClient = new ClientHandler(socket, socket.getInetAddress()); Thread th = new Thread(nextClient); connectedClients.add(nextClient); th.start();