Fixed disconnection issues. Fix is only temporary until a consistent interface for disconnections exists.

#bug #implement
This commit is contained in:
Calum
2017-08-17 14:02:18 +12:00
parent ef2659a7b9
commit 0276911b88
8 changed files with 92 additions and 43 deletions
@@ -93,6 +93,9 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
// TODO: 14/07/17 wmu16 - Send out disconnect packet to clients // TODO: 14/07/17 wmu16 - Send out disconnect packet to clients
try { try {
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
serverToClientThread.terminate();
}
serverSocket.close(); serverSocket.close();
return; return;
} catch (IOException e) { } catch (IOException e) {
@@ -173,6 +176,7 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
thread.sendSetupMessages(); thread.sendSetupMessages();
} }
}); });
serverToClientThread.addDisconnectListener(this::clientDisconnected);
} }
/** /**
@@ -182,11 +186,11 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
*/ */
@Override @Override
public void clientDisconnected(Player player) { public void clientDisconnected(Player player) {
try { // try {
player.getSocket().close(); // player.getSocket().close();
} catch (Exception e) { // } catch (Exception e) {
serverLog("Cannot disconnect the socket for the disconnected player.", 0); // serverLog("Cannot disconnect the socket for the disconnected player.", 0);
} // }
serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0); serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0);
GameState.removeYacht(player.getYacht().getSourceId()); GameState.removeYacht(player.getYacht().getSourceId());
GameState.removePlayer(player); GameState.removePlayer(player);
@@ -194,11 +198,12 @@ public class MainServerThread implements Runnable, ClientConnectionDelegate {
for (ServerToClientThread serverToClientThread : serverToClientThreads) { for (ServerToClientThread serverToClientThread : serverToClientThreads) {
if (serverToClientThread.getSocket() == player.getSocket()) { if (serverToClientThread.getSocket() == player.getSocket()) {
closedConnection = serverToClientThread; closedConnection = serverToClientThread;
} else { } else if (GameState.getCurrentStage() != GameStages.RACING){
serverToClientThread.sendSetupMessages(); serverToClientThread.sendSetupMessages();
} }
} }
serverToClientThreads.remove(closedConnection); serverToClientThreads.remove(closedConnection);
closedConnection.terminate();
} }
public void startGame() { public void startGame() {
@@ -38,7 +38,7 @@ public class ServerListenThread implements Runnable {
} }
public void run(){ public void run(){
while (true){ while (serverSocket != null && !serverSocket.isClosed()){
acceptConnection(); acceptConnection();
} }
} }
@@ -19,26 +19,22 @@ import java.util.zip.CRC32;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RegistrationResponseMessage;
import seng302.gameServer.messages.RegistrationResponseStatus;
import seng302.gameServer.messages.XMLMessage;
import seng302.gameServer.messages.XMLMessageSubType;
import seng302.gameServer.messages.YachtEventCodeMessage; import seng302.gameServer.messages.YachtEventCodeMessage;
import seng302.model.Player; import seng302.model.Player;
import seng302.model.ServerYacht;
import seng302.model.stream.packets.PacketType; import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket; import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.xml.generator.Race; import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta; import seng302.model.stream.xml.generator.Regatta;
import seng302.utilities.XMLGenerator; import seng302.utilities.XMLGenerator;
import seng302.gameServer.messages.BoatAction;
import seng302.gameServer.messages.BoatLocationMessage;
import seng302.gameServer.messages.BoatSubMessage;
import seng302.gameServer.messages.ClientType;
import seng302.gameServer.messages.Message;
import seng302.gameServer.messages.RaceStatus;
import seng302.gameServer.messages.RaceStatusMessage;
import seng302.gameServer.messages.RaceType;
import seng302.gameServer.messages.RegistrationResponseMessage;
import seng302.gameServer.messages.RegistrationResponseStatus;
import seng302.gameServer.messages.XMLMessage;
import seng302.gameServer.messages.XMLMessageSubType;
import seng302.model.ServerYacht;
/** /**
* A class describing a single connection to a Client for the purposes of sending and receiving on * A class describing a single connection to a Client for the purposes of sending and receiving on
@@ -55,6 +51,12 @@ public class ServerToClientThread implements Runnable, Observer {
void notifyConnection (); void notifyConnection ();
} }
// TODO: 17/08/17 this is only temporary disconnects should be handled consistently
@FunctionalInterface
interface DisconnectListener {
void notifyDisconnect (Player player);
}
private Logger logger = LoggerFactory.getLogger(ServerToClientThread.class); private Logger logger = LoggerFactory.getLogger(ServerToClientThread.class);
private Thread thread; private Thread thread;
@@ -74,8 +76,10 @@ public class ServerToClientThread implements Runnable, Observer {
private XMLGenerator xml; private XMLGenerator xml;
private List<ConnectionListener> connectionListeners = new ArrayList<>(); private List<ConnectionListener> connectionListeners = new ArrayList<>();
private DisconnectListener disconnectListener;
private ServerYacht yacht; private ServerYacht yacht;
private Player player;
public ServerToClientThread(Socket socket) { public ServerToClientThread(Socket socket) {
this.socket = socket; this.socket = socket;
@@ -122,8 +126,9 @@ public class ServerToClientThread implements Runnable, Observer {
); );
yacht.addObserver(this); // TODO: yacht can notify mark rounding message hyi25 13/8/17 yacht.addObserver(this); // TODO: yacht can notify mark rounding message hyi25 13/8/17
player = new Player(socket, yacht);
GameState.addYacht(sourceId, yacht); GameState.addYacht(sourceId, yacht);
GameState.addPlayer(new Player(socket, yacht)); GameState.addPlayer(player);
} }
@Override @Override
@@ -169,8 +174,7 @@ public class ServerToClientThread implements Runnable, Observer {
int sync2; int sync2;
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop // TODO: 14/07/17 wmu16 - Work out how to fix this while loop
while (socket.isConnected()) { while (socket.isConnected() && !socket.isClosed()) {
try { try {
crcBuffer = new ByteArrayOutputStream(); crcBuffer = new ByteArrayOutputStream();
sync1 = readByte(); sync1 = readByte();
@@ -213,6 +217,7 @@ public class ServerToClientThread implements Runnable, Observer {
return; return;
} }
} }
logger.warn("Closed serverToClientThread" + thread, 1);
} }
public void sendSetupMessages() { public void sendSetupMessages() {
@@ -252,11 +257,12 @@ public class ServerToClientThread implements Runnable, Observer {
private int readByte() throws Exception { private int readByte() throws Exception {
int currentByte = -1; int currentByte = -1;
try { try {
// @TODO @FIX ConnectionReset Exception when a client disconnects before it is garbage collected
currentByte = is.read(); currentByte = is.read();
crcBuffer.write(currentByte); crcBuffer.write(currentByte);
} catch (SocketException se) {
disconnectListener.notifyDisconnect(this.player);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); disconnectListener.notifyDisconnect(this.player);
logger.warn("Socket read failed", 1); logger.warn("Socket read failed", 1);
} }
if (currentByte == -1) { if (currentByte == -1) {
@@ -335,4 +341,16 @@ public class ServerToClientThread implements Runnable, Observer {
public void removeConnectionListener(ConnectionListener listener) { public void removeConnectionListener(ConnectionListener listener) {
connectionListeners.remove(listener); connectionListeners.remove(listener);
} }
public void terminate () {
try {
socket.close();
} catch (IOException ioe) {
logger.warn("IOException attempting to terminate serverToClientThread " + this.thread);
}
}
public void addDisconnectListener(DisconnectListener disconnectListener) {
this.disconnectListener = disconnectListener;
}
} }
@@ -142,20 +142,22 @@ public class ClientToServerThread implements Runnable {
} }
} catch (ByteReadException e) { } catch (ByteReadException e) {
logger.warn("Byte read exception on ClientToServerThread", 1); logger.warn("Byte read exception on ClientToServerThread", 1);
closeSocket();
notifyDisconnectListeners("Connection to server was interrupted"); notifyDisconnectListeners("Connection to server was interrupted");
closeSocket();
} }
} }
logger.warn("Closed connection to server", 1); logger.warn("Closed connection to server", 1);
closeSocket();
notifyDisconnectListeners("Connection to server was terminated"); notifyDisconnectListeners("Connection to server was terminated");
closeSocket();
} }
private void notifyDisconnectListeners (String message) { private void notifyDisconnectListeners (String message) {
if (socketOpen) {
for (DisconnectedFromHostListener listener : disconnectionListeners) { for (DisconnectedFromHostListener listener : disconnectionListeners) {
listener.notifYDisconnection(message); listener.notifYDisconnection(message);
} }
} }
}
/** /**
* Sends a request to the server asking for a source ID * Sends a request to the server asking for a source ID
@@ -167,8 +169,8 @@ public class ClientToServerThread implements Runnable {
os.write(requestMessage.getBuffer()); os.write(requestMessage.getBuffer());
} catch (IOException e) { } catch (IOException e) {
logger.error("Could not send registration request. Exiting"); logger.error("Could not send registration request. Exiting");
closeSocket();
notifyDisconnectListeners("Failed to register with server"); notifyDisconnectListeners("Failed to register with server");
closeSocket();
} }
} }
@@ -197,8 +199,8 @@ public class ClientToServerThread implements Runnable {
else{ else{
alertErrorText = "Could not connect to server"; alertErrorText = "Could not connect to server";
} }
closeSocket();
notifyDisconnectListeners(alertErrorText); notifyDisconnectListeners(alertErrorText);
closeSocket();
} }
/** /**
@@ -208,7 +210,7 @@ public class ClientToServerThread implements Runnable {
* - MAINTAIN_HEADING = DOWNWIND and UPWIND packets stop being sent. * - MAINTAIN_HEADING = DOWNWIND and UPWIND packets stop being sent.
* @param actionType The boat action that will dictate packets sent. * @param actionType The boat action that will dictate packets sent.
*/ */
public void sendBoatActionMessage(BoatAction actionType) { public void sendBoatAction(BoatAction actionType) {
switch (actionType) { switch (actionType) {
case MAINTAIN_HEADING: case MAINTAIN_HEADING:
if (upwindTimerFlag) { if (upwindTimerFlag) {
@@ -272,9 +274,9 @@ public class ClientToServerThread implements Runnable {
try { try {
os.write(message.getBuffer()); os.write(message.getBuffer());
} catch (IOException e) { } catch (IOException e) {
logger.warn("IOException on attempting to sendBoatActionMessage from Client"); logger.warn("IOException on attempting to sendBoatAction from Client");
closeSocket();
notifyDisconnectListeners("Cannot communicate with server"); notifyDisconnectListeners("Cannot communicate with server");
closeSocket();
} }
} }
} }
@@ -282,6 +284,7 @@ public class ClientToServerThread implements Runnable {
private void closeSocket() { private void closeSocket() {
try { try {
socket.close(); socket.close();
socketOpen = false;
} catch (IOException e) { } catch (IOException e) {
logger.warn("IOException on attempting to close ClientToServerSocket"); logger.warn("IOException on attempting to close ClientToServerSocket");
} }
@@ -318,8 +321,8 @@ public class ClientToServerThread implements Runnable {
crcBuffer.write(currentByte); crcBuffer.write(currentByte);
} catch (IOException e) { } catch (IOException e) {
logger.warn("IOException on readByte Client side", 1); logger.warn("IOException on readByte Client side", 1);
closeSocket();
notifyDisconnectListeners("Cannot read from server."); notifyDisconnectListeners("Cannot read from server.");
closeSocket();
} }
if (currentByte == -1) { if (currentByte == -1) {
throw new ByteReadException("InputStream reach end of stream"); throw new ByteReadException("InputStream reach end of stream");
@@ -104,6 +104,7 @@ public class GameClient {
* @param portNumber Port to connect to. * @param portNumber Port to connect to.
*/ */
public void runAsHost(String ipAddress, Integer portNumber) { public void runAsHost(String ipAddress, Integer portNumber) {
server = new MainServerThread();
try { try {
startClientToServerThread(ipAddress, portNumber); startClientToServerThread(ipAddress, portNumber);
socketThread.addDisconnectionListener((cause) -> { socketThread.addDisconnectionListener((cause) -> {
@@ -126,6 +127,8 @@ public class GameClient {
lobbyController.disableReadyButton(); lobbyController.disableReadyButton();
server.startGame(); server.startGame();
} else if (exitCause == CloseStatus.LEAVE) { } else if (exitCause == CloseStatus.LEAVE) {
server.terminate();
server = null;
loadStartScreen(); loadStartScreen();
} }
}); });
@@ -370,13 +373,13 @@ public class GameClient {
private void keyPressed(KeyEvent e) { private void keyPressed(KeyEvent e) {
switch (e.getCode()) { switch (e.getCode()) {
case SPACE: // align with vmg case SPACE: // align with vmg
socketThread.sendBoatActionMessage(BoatAction.VMG); break; socketThread.sendBoatAction(BoatAction.VMG); break;
case PAGE_UP: // upwind case PAGE_UP: // upwind
socketThread.sendBoatActionMessage(BoatAction.UPWIND); break; socketThread.sendBoatAction(BoatAction.UPWIND); break;
case PAGE_DOWN: // downwind case PAGE_DOWN: // downwind
socketThread.sendBoatActionMessage(BoatAction.DOWNWIND); break; socketThread.sendBoatAction(BoatAction.DOWNWIND); break;
case ENTER: // tack/gybe case ENTER: // tack/gybe
socketThread.sendBoatActionMessage(BoatAction.TACK_GYBE); break; socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
} }
} }
@@ -385,12 +388,12 @@ public class GameClient {
switch (e.getCode()) { switch (e.getCode()) {
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet) //TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
case SHIFT: // sails in/sails out case SHIFT: // sails in/sails out
socketThread.sendBoatActionMessage(BoatAction.SAILS_IN); socketThread.sendBoatAction(BoatAction.SAILS_IN);
allBoatsMap.get(socketThread.getClientId()).toggleSail(); allBoatsMap.get(socketThread.getClientId()).toggleSail();
break; break;
case PAGE_UP: case PAGE_UP:
case PAGE_DOWN: case PAGE_DOWN:
socketThread.sendBoatActionMessage(BoatAction.MAINTAIN_HEADING); break; socketThread.sendBoatAction(BoatAction.MAINTAIN_HEADING); break;
} }
} }
@@ -0,0 +1,20 @@
package seng302.visualiser.ClientToServerTests;
import org.junit.Assert;
import org.junit.Test;
import seng302.gameServer.MainServerThread;
import seng302.visualiser.ClientToServerThread;
/**
* Created by cir27 on 17/08/17.
*/
public class DisconnectionTest {
@Test
public void testServerDisconnection () throws Exception {
MainServerThread serverThread = new MainServerThread();
ClientToServerThread clientThread = new ClientToServerThread("localhost", 4942);
Thread.sleep(1000);
clientThread.addDisconnectionListener(message -> Assert.assertTrue(message != null));
serverThread.terminate();
}
}
@@ -61,7 +61,7 @@ public class RegularPacketsTest {
// SleepThreadMaxDelay(); // SleepThreadMaxDelay();
// ServerYacht yacht = new ArrayList<>(GameState.getYachts().values()).get(0); // ServerYacht yacht = new ArrayList<>(GameState.getYachts().values()).get(0);
// boolean startState = yacht.getSailIn(); // boolean startState = yacht.getSailIn();
// clientThread.sendBoatActionMessage(BoatAction.SAILS_IN); // clientThread.sendBoatAction(BoatAction.SAILS_IN);
// SleepThreadMaxDelay(); // SleepThreadMaxDelay();
// Assert.assertEquals(startState, !yacht.getSailIn()); // Assert.assertEquals(startState, !yacht.getSailIn());
// } // }
+1 -1
View File
@@ -36,7 +36,7 @@ public class ToggleSailSteps {
public void the_user_has_pressed(String arg1) throws Throwable { public void the_user_has_pressed(String arg1) throws Throwable {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
if (arg1 == "shift") { if (arg1 == "shift") {
client.sendBoatActionMessage(BoatAction.SAILS_IN); client.sendBoatAction(BoatAction.SAILS_IN);
} }
} }