From c480fca72a146fcf316f4434a9002687b74e28ee Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 13 Jul 2017 22:55:03 +1200 Subject: [PATCH] WIP: Worked on new server thread class that would create and store multiple THREADS of connections. Researched Authorative server structures tags: #story[1047] --- .../seng302/gameServer/GameServerThread.java | 67 ++++++----- .../gameServerWithThreading/ServerThread.java | 105 ++++++++++++++++++ .../ServerThreadHandler.java | 67 +++++++++++ 3 files changed, 204 insertions(+), 35 deletions(-) create mode 100644 src/main/java/seng302/gameServerWithThreading/ServerThread.java create mode 100644 src/main/java/seng302/gameServerWithThreading/ServerThreadHandler.java diff --git a/src/main/java/seng302/gameServer/GameServerThread.java b/src/main/java/seng302/gameServer/GameServerThread.java index d4e1f5a3..a7dbb3cc 100644 --- a/src/main/java/seng302/gameServer/GameServerThread.java +++ b/src/main/java/seng302/gameServer/GameServerThread.java @@ -3,14 +3,10 @@ package seng302.gameServer; import seng302.models.Player; import seng302.models.Yacht; import seng302.server.messages.*; -import seng302.server.simulator.Boat; -import seng302.server.simulator.Simulator; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; -import java.net.SocketOption; -import java.net.SocketOptions; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.*; @@ -280,7 +276,7 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel startTime = System.currentTimeMillis() + TIME_TILL_RACE_START; - } + } } // /** @@ -360,41 +356,42 @@ public class GameServerThread implements Runnable, Observer, ClientConnectionDel @Override @SuppressWarnings("unchecked") public void update(Observable o, Object arg) { - /* Only send if server started - // TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17 - if(server == null || !server.isStarted()){ - return; - } - - int numOfBoatsFinished = 0; - for (Boat boat : (List) arg){ - try { - if (boat.isFinished()) { - numOfBoatsFinished ++; - if (!boatsFinished.get(boat.getSourceID())) { - boatsFinished.put(boat.getSourceID(), true); - } - } - Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(), - boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(), - ((long) boat.getSpeed())); - broadcast(m); - } catch (IOException e) { - serverLog("Couldn't send a boat status message", 3); - return; - } - catch (NullPointerException e){ - e.printStackTrace(); - }*/ - } - - +// /* Only send if server started*/ +//// // TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17 +// if (server == null || !server.isStarted()) { +// return; +// } +// +// int numOfBoatsFinished = 0; +// for (Boat boat : (List) arg) { +// try { +// if (boat.isFinished()) { +// numOfBoatsFinished++; +// if (!boatsFinished.get(boat.getSourceID())) { +// boatsFinished.put(boat.getSourceID(), true); +// } +// } +// Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(), +// boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(), +// ((long) boat.getSpeed())); +// broadcast(m); +// } catch (IOException e) { +// serverLog("Couldn't send a boat status message", 3); +// return; +// } catch (NullPointerException e) { +// e.printStackTrace(); +// } +// } // if (numOfBoatsFinished == ((List) arg).size()) { // startSendingRaceFinishedBoatPositions(); // } - //} + } + + + + public void terminateGame() { try { diff --git a/src/main/java/seng302/gameServerWithThreading/ServerThread.java b/src/main/java/seng302/gameServerWithThreading/ServerThread.java new file mode 100644 index 00000000..cca96035 --- /dev/null +++ b/src/main/java/seng302/gameServerWithThreading/ServerThread.java @@ -0,0 +1,105 @@ +package seng302.gameServerWithThreading; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * A class describing a single connection to a Client for the purposes of sending and receiving on its own thread. + * All server threads created and owned by the server thread handler which can trigger client updates on its threads + * Created by wmu16 on 13/07/17. + */ +public class ServerThread extends Thread { + + private static final Integer MAX_ID_ATTEMPTS = 10; + + private InputStream is; + private OutputStream os; + private Socket socket; + + private Boolean userIdentified = false; + private Boolean connected = true; + private Boolean updateClient = true; + + public ServerThread(Socket socket) { + this.socket = socket; + } + + public void run() { + try { + is = socket.getInputStream(); + os = socket.getOutputStream(); + } catch (IOException e) { + System.out.println("IO error in server thread upon grabbing streams"); + } + + threeWayHandshake(); + + // TODO: 13/07/17 wmu16 - Some way of knowing if the client is still connected. perhaps when we read disconnect message switch this bool? + while (connected) { + + //Perform a read and update game state + try { + Integer userInput = is.read(); + } catch (IOException e) { + System.out.println("IO error in server thread upon reading input stream"); + } + + + //Perform a write if it is time to as delegated by the ServerThreadHandler + if (updateClient) { + // TODO: 13/07/17 wmu16 - Write out game state - some function that would write all appropriate messages to this output stream +// try { +// GameState.outputState(os); +// } catch (IOException e) { +// System.out.println("IO error in server thread upon writing to output stream"); +// } + updateClient = false; + } + } + + closeSocket(); + + } + + public void updateClient() { + updateClient = true; + } + + + /** + * Tries to confirm the connection just accepted. + * Sends ID, expects that ID echoed for confirmation, + * if so, sends a confirmation packet back to that connection + * Creates a player instance with that ID and this thread and adds it to the GameState + * If not, close the socket and end the threads execution + */ + private void threeWayHandshake() { + // TODO: 13/07/17 Finish using AC35 +// Integer playerID = GameState.getUniquePlayerID(); +// Integer confirmationID = null; +// Integer identificationAttempt = 0 +// while (!userIdentified) { +// os.write(playerID); //Send out new ID looking for echo +// confirmationID = is.read(); +// if (playerID == idConfirmation) { //ID is echoed back. Connection is a client +// os.write( some determined confirmation message ); //Confirm to client +// GameState.addPlayer(new Player(playerID, this)); //Create a player in game state for client +// userIdentified = true; +// } else if (identificationAttempt > MAX_ID_ATTEMPTS) { //No response. not a client. tidy up and go home. +// closeSocket(); +// return; +// } +// identificationAttempt++; +// } + } + + public void closeSocket() { + try { + socket.close(); + } catch (IOException e) { + System.out.println("IO error in server thread upon trying to close socket"); + } + } +} diff --git a/src/main/java/seng302/gameServerWithThreading/ServerThreadHandler.java b/src/main/java/seng302/gameServerWithThreading/ServerThreadHandler.java new file mode 100644 index 00000000..dffedd60 --- /dev/null +++ b/src/main/java/seng302/gameServerWithThreading/ServerThreadHandler.java @@ -0,0 +1,67 @@ +package seng302.gameServerWithThreading; + +import seng302.gameServer.GameStages; +import seng302.gameServer.GameState; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; + +/** + * A class describing the overall server, which creates and collects server threads for each client + * Created by wmu16 on 13/07/17. + */ +public class ServerThreadHandler extends Thread { + + private static final int PORT = 4950; + private static final Integer MAX_NUM_PLAYERS = 10; + + private ServerSocket serverSocket = null; + private Socket socket; + private ArrayList serverThreads = new ArrayList<>(); + + public ServerThreadHandler() { + try { + serverSocket = new ServerSocket(PORT); + } catch (IOException e) { + System.out.println("IO error in server thread handler upon trying to make new server socket"); + } + } + + + public void run() { + //You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app. + while (!isInterrupted()) { + try { + Thread.sleep(1000 / 60); //60 times per second we should calculate the game state + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (GameState.getCurrentStage() == GameStages.LOBBYING && GameState.getPlayers().size() < MAX_NUM_PLAYERS) { + try { + socket = serverSocket.accept(); + } catch (IOException e) { + System.out.println("IO error in server thread handler upon trying to accept connection"); + } + ServerThread thread = new ServerThread(socket); + serverThreads.add(thread); + thread.start(); + } + + updateClients(); + } + + try { + serverSocket.close(); + } catch (IOException e) { + System.out.println("IO error in server thread handler upon closing socket"); + } + } + + public void updateClients() { + for (ServerThread serverThread : serverThreads) { + serverThread.updateClient(); + } + } +}