diff --git a/code/simplechat1/ClientConsole.java b/code/simplechat1/ClientConsole.java index c9bb4e9..0c48f4c 100644 --- a/code/simplechat1/ClientConsole.java +++ b/code/simplechat1/ClientConsole.java @@ -41,11 +41,11 @@ public class ClientConsole implements ChatIF * @param host The host to connect to. * @param port The port to connect on. */ - public ClientConsole(String host, int port) + public ClientConsole(String loginID, String host, int port) { try { - client= new ChatClient(host, port, this); + client= new ChatClient(loginID, host, port, this); } catch(IOException exception) { @@ -104,18 +104,30 @@ public void display(String message) */ public static void main(String[] args) { + String loginID = ""; String host = ""; int port = 0; //The port number - try - { - host = args[0]; - } - catch(ArrayIndexOutOfBoundsException e) - { - host = "localhost"; + try { + loginID = args[0]; + host = args[1]; + port = Integer.parseInt(args[2]); + } catch(ArrayIndexOutOfBoundsException e1) { + port = DEFAULT_PORT; + try { + loginID = args[0]; + host = args[1]; + } catch (ArrayIndexOutOfBoundsException e2) { + host = "localhost"; + try { + loginID = args[0]; + } catch (ArrayIndexOutOfBoundsException e3) { + System.out.println("ERROR - No login ID specified. Connection aborted."); + System.exit(1); + } + } } - ClientConsole chat= new ClientConsole(host, DEFAULT_PORT); + ClientConsole chat= new ClientConsole(loginID, host, port); // MUST implement loginID to main chat.accept(); //Wait for console data } } diff --git a/code/simplechat1/EchoServer.java b/code/simplechat1/EchoServer.java index d4f3a1a..930b3fa 100644 --- a/code/simplechat1/EchoServer.java +++ b/code/simplechat1/EchoServer.java @@ -48,9 +48,89 @@ public EchoServer(int port) public void handleMessageFromClient (Object msg, ConnectionToClient client) { - System.out.println("Message received: " + msg + " from " + client); - this.sendToAllClients(msg); + if (msg.toString().startsWith("#login")) { + String[] message = msg.toString().split(" "); + if (message.length != 0) { + if (client.getInfo("loginID") == null) { + client.setInfo("loginID", message[1]); + this.sendToAllClients(client.getInfo("loginID") + " has logged on."); + } else { + try { + client.sendToClient("Username cannot be changed."); + } catch (IOException e) { + // + } + } + } + } else { + if (client.getInfo("loginID") == null) { + try { + client.sendToClient("Error: No username provided."); + client.close(); + } catch (IOException e) { + // + } + } else { + System.out.println("Message received: " + msg + " from " + client.getInfo("loginID")); + this.sendToAllClients(client.getInfo("loginID") + "> " + msg); + } + } } + + public void handleMessageFromServerUI(String message) + { + if (message.startsWith("#")) { + String[] cmd = message.split(" "); + switch (cmd[0]) { + case "#quit": + try { + close(); + System.out.println("Connection terminated."); + } catch (IOException e) { + System.out.println("Unable to terminate connection."); + } + break; + case "#stop": + this.sendToAllClients("Server has stopped listening for new clients."); + this.stopListening(); + break; + case "#close": + this.sendToAllClients("Server has stopped listening for new clients."); + this.stopListening(); + try { + close(); + System.out.println("Connection terminated."); + } catch (IOException e) { + System.out.println("Unable to terminate connection."); + } + break; + case "#setport": + if (!isListening()) { + this.setPort(Integer.parseInt(cmd[1])); + System.out.println("New port has been set."); + } else { + System.out.println("Server must be closed."); + } + break; + case "#start": + if (!isListening()) { + try { + listen(); + } catch (IOException e) { + System.out.println("Unable to listen for new clients."); + } + } else { + System.out.println("Server must be stopped."); + } + break; + case "#getport": + System.out.println("Current port: " + this.getPort()); + break; + } + } else { + this.sendToAllClients("SERVER MSG> " + message); + } + } /** * This method overrides the one in the superclass. Called @@ -72,6 +152,43 @@ protected void serverStopped() ("Server has stopped listening for connections."); } +/** + * Hook method called each time a new client connection is + * accepted. The default implementation does nothing. + * @param client the connection connected to the client. + */ + protected void clientConnected(ConnectionToClient client) { + if (client.getInfo("loginID") != null) { + System.out.println(client.getInfo("loginID") + " has connected to the server."); + } else { + System.out.println("A client has connected to the server."); + } + } + + /** + * Hook method called each time a client disconnects. + * The default implementation does nothing. The method + * may be overridden by subclasses but should remains synchronized. + * + * @param client the connection with the client. + */ + synchronized protected void clientDisconnected(ConnectionToClient client) { + this.sendToAllClients(client.getInfo("loginID") + " has disconnected from the server."); + } + + /** + * Hook method called each time an exception is thrown in a + * ConnectionToClient thread. + * The method may be overridden by subclasses but should remains + * synchronized. + * + * @param client the client that raised the exception. + * @param Throwable the exception thrown. + */ + synchronized protected void clientException(ConnectionToClient client, Throwable exception) { + clientDisconnected(client); + } + //Class methods *************************************************** /** diff --git a/code/simplechat1/ServerConsole.java b/code/simplechat1/ServerConsole.java new file mode 100644 index 0000000..e5ab573 --- /dev/null +++ b/code/simplechat1/ServerConsole.java @@ -0,0 +1,116 @@ +// This file contains material supporting section 3.7 of the textbook: +// "Object Oriented Software Engineering" and is issued under the open-source +// license found at www.lloseng.com + +import java.io.*; +// import client.*; +import common.*; + +/** + * This class constructs the UI for a chat client. It implements the + * chat interface in order to activate the display() method. + * Warning: Some of the code here is cloned in ServerConsole + * + * @author François Bélanger + * @author Dr Timothy C. Lethbridge + * @author Dr Robert Laganière + * @version July 2000 + */ +public class ServerConsole implements ChatIF +{ + //Class variables ************************************************* + + /** + * The default port to connect on. + */ + final public static int DEFAULT_PORT = 5555; + + //Instance variables ********************************************** + + EchoServer server; + + //Constructors **************************************************** + + /** + * Constructs an instance of the ClientConsole UI. + * + * @param host The host to connect to. + * @param port The port to connect on. + */ + public ServerConsole(int port) + { + server = new EchoServer(port); + try + { + server.listen(); + } + catch(IOException exception) + { + System.out.println("Error: Can't setup connection!" + + " Terminating client."); + System.exit(1); + } + } + + + //Instance methods ************************************************ + + /** + * This method waits for input from the console. Once it is + * received, it sends it to the client's message handler. + */ + public void accept() + { + try + { + BufferedReader fromConsole = + new BufferedReader(new InputStreamReader(System.in)); + String message; + + while (true) + { + message = fromConsole.readLine(); + server.handleMessageFromServerUI(message); + } + } + catch (Exception ex) + { + System.out.println + ("Unexpected error while reading from console!"); + } + } + + /** + * This method overrides the method in the ChatIF interface. It + * displays a message onto the screen. + * + * @param message The string to be displayed. + */ + public void display(String message) + { + System.out.println("SERVER MSG> " + message); + } + + + //Class methods *************************************************** + + /** + * This method is responsible for the creation of the Client UI. + * + * @param args[0] The host to connect to. + */ + public static void main(String[] args) + { + int port = 0; + + try { + port = Integer.parseInt(args[0]); + } catch(ArrayIndexOutOfBoundsException e) { + port = DEFAULT_PORT; + } + ServerConsole chat = new ServerConsole(port); + chat.accept(); + } + +} +//End of ServerConsole class diff --git a/code/simplechat1/client/ChatClient.java b/code/simplechat1/client/ChatClient.java index fe1401e..c6a99cf 100644 --- a/code/simplechat1/client/ChatClient.java +++ b/code/simplechat1/client/ChatClient.java @@ -26,7 +26,7 @@ public class ChatClient extends AbstractClient * the display method in the client. */ ChatIF clientUI; - + String loginID; //Constructors **************************************************** @@ -38,12 +38,15 @@ public class ChatClient extends AbstractClient * @param clientUI The interface type variable. */ - public ChatClient(String host, int port, ChatIF clientUI) + public ChatClient(String loginID, String host, int port, ChatIF clientUI) throws IOException { super(host, port); //Call the superclass constructor + this.loginID = loginID; this.clientUI = clientUI; openConnection(); + + this.sendToServer("#login " + loginID); } @@ -66,18 +69,84 @@ public void handleMessageFromServer(Object msg) */ public void handleMessageFromClientUI(String message) { - try - { - sendToServer(message); + if (message.startsWith("#")) { + String[] cmd = message.split(" "); + switch (cmd[0]) { + case "#quit": + clientUI.display("Terminating connection."); + quit(); + break; + case "#logoff": + try { + clientUI.display("Logging off."); + closeConnection(); + } catch(IOException e) { + System.out.println("Error logging off."); + } + case "#sethost": + if (!isConnected()) { + this.setHost(cmd[1]); + clientUI.display("New host has been set."); + } else { + System.out.println("Must be logged off."); + } + break; + case "#setport": + if (!isConnected()) { + this.setPort(Integer.parseInt(cmd[1])); + clientUI.display("New port has been set."); + } else { + System.out.println("Must be logged off."); + } + break; + case "#login": + try { + openConnection(); + } catch (IOException e) { + System.out.println("Must not be connected."); + } + break; + case "#gethost": + clientUI.display("Currect host: " + this.getHost()); + break; + case "#getport": + clientUI.display("Current port: " + this.getPort()); + break; + } + } else { + try { + sendToServer(message); + } catch (IOException e) { + clientUI.display + ("Could not send message to server. Terminating client."); + quit(); + } + } } - catch(IOException e) - { - clientUI.display - ("Could not send message to server. Terminating client."); + + /** + * Hook method called after the connection has been closed. The default + * implementation does nothing. The method may be overriden by subclasses to + * perform special processing such as cleaning up and terminating, or + * attempting to reconnect. + */ + public void connectionClosed() { + clientUI.display("The connection with the server has ended."); + } + + /** + * Hook method called each time an exception is thrown by the client's + * thread that is waiting for messages from the server. The method may be + * overridden by subclasses. + * + * @param exception + * the exception raised. + */ + public void connectionException(Exception exception) { + clientUI.display("Server has stopped communicating. Shutting down."); quit(); - } - } - + } + /** * This method terminates the client. */