package simpleNetworking; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.security.SignatureException; import javax.swing.JOptionPane; /** *
 * A MultiServer first constructs a Server.
 * 
 * Then the MultiServer, in its own Thread, repeatedly accepts connections
 * to Clients and sends them to the Server (who can accept or reject them).
 * 
 * After constructing a MultiServer, you can ask it to give you the Server
 * that the MultiServer constructs via:
 *   -- getServer(N): Returns the Server only after it has N validated Clients
 *   -- getServer():  Returns the Server with whatever Clients it currently has,
 *                    and the MultiServer continues to add Clients to the Server
 *   -- getServerAndQuit():  Returns the Server with whatever Clients
 *                    it currently has, but now the MultiServer stops (so
 *                    no new Clients are added to the Server by the MultiServer).
 * 
 * Or, you can stop the MultiServer at any point and receive the Server
 * that the 
 *
 * NOTE:
 *   1. The MultServer must be started before any Clients attempt to connect.
 *   2. Before running this program, you may need to tell your Firewall
 *      not to block the port that this program uses (4444, chosen arbitrarily).
 *      In Windows, do so by:
 *         Control Panel ~ Windows Firewall ~ Exceptions tab ~ Add Port
 *      and enter the port number (4444) with any name you like. 
 *
* * @author David Mutchler, using examples.example1_one_client from the Java Tutorials. May, 2009. */ public class MultiServer implements Runnable { /** * Port to use for establishing connections to this server. * Any open, unused port is fine, but ports less than 1024 are reserved. */ static final int PORT = 4444; private ServerSocket serverSocket; private Server server; private boolean isDone = false; /** *
	 * Constructs a ServerSocket that this MultiServer uses to accept
	 * multiple connections to Clients.  If that ServerSocket is constructed
	 * successfully:
	 * 
	 * 1. Construct a Server that runs in its own Thread.
	 * 
	 * 2. In a new Thread, repeatedly:
	 *    a. Accept a connection, getting a Socket for communicating with a Client.
	 *    b. Ask the Server to add that Client.
	 *       -- The Server can run a validation protocol on the proposed Client,
	 *          accepting or rejecting the proposed Client as it chooses.
	 * 
* * @throws SocketException if the MultiServer cannot construct a ServerSocket * that uses the default port to accept connections from Clients. */ public MultiServer() throws SocketException { try { // Construct a ServerSocket to accept connections to Clients. this.serverSocket = new ServerSocket(MultiServer.PORT); // Construct a Server. this.server = new Server(); JOptionPane.showMessageDialog(null, "The MultiServer has started and will wait for a connection.\n\n" + "Start the Client(s) AFTER pressing OK in this dialog.\n\n"); // Start a Thread for this MultiServer to accept connections to Clients. new Thread(this).start(); } catch (Throwable exception) { this.finishUpFailedConstruction(exception, "Could not construct a ServerSocket using port " + MultiServer.PORT + ".\n\n"); } } /** * Repeatedly: * 1. Accept a connection, getting a Socket to a proposed Client. * 2. Ask the Server to add that Client to the list of Clients * with which it communicates. * -- The Server can run a validation protocol on the proposed Client, * accepting or rejecting the proposed Client as it chooses. */ @Override public void run() { Socket socketToTalkToClient = null; while (! this.isDone) { try { socketToTalkToClient = this.serverSocket.accept(); this.server.addClient(socketToTalkToClient); } catch (SignatureException exception) { // The proposed Client did not pass the validation. // A break-in attempt? // Or the Clients are not doing the validation protocol correctly? // In any case, print a message and continue accepting clients. System.out.println("The client " + socketToTalkToClient + " did not pass the validation protocol."); } catch (SocketException exception) { // Presumably the ServerSocket was closed, if not print error if (! exception.getMessage().equals("socket closed")) { exception.printStackTrace(); } } catch (Exception exception) { exception.printStackTrace(); // Something went wrong, but keep trying to accept connections. } } try { this.serverSocket.close(); } catch (IOException exception) { // ServerSocket will usually be already closed, // so no error if we get here. Just continue. } } /** * Blocks (waits) until the Server created by this MultiServer * has validated connections to at least the given number of Clients; * then it returns that Server. * * @param requiredNumberOfClients Number of Clients required before this method returns. * @return the Server created by this MultiServer, after that Server * has validated connections to at least the given number of Clients. */ public Server getServer(int requiredNumberOfClients) { while (this.server.numberOfValidatedClients() < requiredNumberOfClients) { try { Thread.sleep(1000); } catch (InterruptedException exception) { // Ignore exception, keep waiting. } } return this.server; } /** * Stop this MultiThread (which is running in its own Thread) * and close all its resources. */ public void stop() { this.isDone = true; try { this.serverSocket.close(); // This interrupts the Thread too } catch (IOException exception) { exception.printStackTrace(); // Continue as best you can. } } /** * Closes the ServerSocket that this MultiServer opened. */ private void close() { try { this.serverSocket.close(); } catch (Exception exception) { // Ignore; we made our best effort at closing the resource. } } /** * Something went wrong in constructing this MultiServer. * Close the MultiServer, 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. * * @throws SocketException so that the caller knows that construction failed. */ void finishUpFailedConstruction(Throwable exception, String errorMessage) throws SocketException { this.close(); if (exception != null) { System.out.println(exception); } JOptionPane.showMessageDialog(null, errorMessage); throw new SocketException( "Could not construct this MultiServer using port " + MultiServer.PORT + ".\n\n"); } }