package simpleNetworking; import java.io.IOException; import java.net.BindException; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import javax.swing.JOptionPane; /** *
 * A simple Client that you can use in your programs.
 * 
 * It has methods for:
 *   -- reading from the Server
 *   -- writing to the Server
 *   -- closing its resources
 * 
 * It does a minimal-security validation protocol with the Server
 * (who must use the Server's side of that protocol).
 * 
* * @author David Mutchler, based on the Java Tutorials on networking. May, 2009. */ public class Client { private Socket socket; private ReaderWriter readerWriter; /** * Connects to the given Server, getting a Socket, * and prepares for reading/writer to the Server based on that Socket. * Also does a minimal-security validation. * * @param serverHostName Name (e.g. "mutchler-3.rose-hulman.edu") * or IP address (e.g. "137.112.250.33") * of the Server to which you want to connect. * @throws SocketException */ public Client(String serverHostName) throws SocketException { try { this.socket = new Socket(serverHostName, MultiServer.PORT); this.readerWriter = new ReaderWriter(this.socket); if (! this.validate()) { this.finishUpFailedConstruction(null, serverHostName, "Validation of this Client failed!\n" + "Is the Server using the matching validation protocol?\n\n"); } } catch (UnknownHostException exception) { this.finishUpFailedConstruction(exception, serverHostName, "Client could not connect to " + serverHostName + " because that is an unknown host.\n\n" + "The Server may still running; fix that by running a Client\n" + "(without starting a Server).\n\n"); } catch (ConnectException exception) { this.finishUpFailedConstruction(exception, serverHostName, "Client could not connect to " + serverHostName + " either because:\n" + " -- the Server is not yet started, or \n" + " -- the host does not accept connections on port " + MultiServer.PORT + ".\n\n" + "The Server may still running; fix that by running a Client\n" + "(without starting a Server).\n\n"); } catch (NoRouteToHostException exception) { this.finishUpFailedConstruction(exception, serverHostName, "Client could not connect to " + serverHostName + " perhaps because\n" + "the hostname " + serverHostName + " does not allow connections on port " + MultiServer.PORT + ".\n\n" + "The Server may still running; fix that by running a Client\n" + "(without starting a Server).\n\n"); } catch (BindException exception) { this.finishUpFailedConstruction(exception, serverHostName, "Client could not connect to " + serverHostName + " perhaps because\n" + "port " + MultiServer.PORT + " is in use.\n" + "The Server is probably still running; stop it by getting a Client to run.\n\n" + "The Server may still running; fix that by running a Client\n" + "(without starting a Server).\n\n"); } catch (SocketException exception) { this.finishUpFailedConstruction(exception, serverHostName, "Client ended, either because it could not start\n" + "or (more likely) because the user closed the Server.\n\n"); } catch (IOException exception) { this.finishUpFailedConstruction(exception, serverHostName, "Client ended, probably because the user closed the Server.\n\n"); } catch (Throwable exception) { exception.printStackTrace(); this.finishUpFailedConstruction(exception, serverHostName, "Client ended abnormally. See stack trace.\n\n"); } } /** * Returns the next line available from the Server, stripping the terminating newline. * Blocks (waits) if no line is available yet. * * @return the next line available from the Server, * but with the terminating newline stripped. * @throws IOException if an IO error occurs while reading. */ public String readLine() throws IOException { // Blocks until a line is sent, the buffer is filled, or an IO exception occurs. return this.readerWriter.readLine(); } /** * Sends the given String, appending a newline to it, to the Server. * * @param stringToWrite String to send (with a newline appended) to the Server. * @throws IOException if an error occurs while writing. */ public void writeLine(String stringToWrite) throws IOException { this.readerWriter.writeLine(stringToWrite); } /** * Closes the resources associated with this Client. */ public void close() { JOptionPane.showMessageDialog(null, "Closing the Client's resources."); this.readerWriter.close(); } /** * To validate a proposed Client: * -- This Client sends the (announced) password to the Server. * -- The Server should receive the password and check whether it is OK. * -- If it is not OK, the Server should not accept the attempted connection. * -- The Server should echo back the sent password. * This method is the Client side of the above. * * Subclasses can override this, simply returning 'true' for no validation * or doing their own validation protocol with their Server. * * @return true if the validation succeeded */ protected boolean validate() { try { this.readerWriter.writeLine(Server.PASSWORD); String passwordSentBack = this.readerWriter.readLine(); return passwordSentBack.equals(Server.PASSWORD); } catch (IOException exception) { exception.printStackTrace(); return false; } } /** * Something went wrong in constructing this Client. * Close the Client, print an error message, and throw a SocketException. * * @param exception that was thrown by the failed construction of this Client. * @param errorMessage to display in a message dialog. * @param serverHostName to which this Client tried to connect. * * @throws SocketException so that the caller knows that construction failed. */ void finishUpFailedConstruction(Throwable exception, String serverHostName, String errorMessage) throws SocketException { this.close(); if (exception != null) { System.out.println(exception); } JOptionPane.showMessageDialog(null, errorMessage); throw new SocketException( "Could not connect this Client to " + serverHostName + "\n" + "using port " + MultiServer.PORT + ".\n\n"); } }