• alex3712
  • Java
  • 416KB
  • rar
  • 0
  • 1 积分
  • 11
  • 2005-08-10 07:42
/* * Copyright (c) 2000 David Flanagan. All rights reserved. * This code is from the book Java Examples in a Nutshell, 2nd Edition. * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied. * You may study, use, and modify it for any non-commercial purpose. * You may distribute it non-commercially as long as you retain this notice. * For a commercial use license, or to purchase the book (recommended), * visit http://www.davidflanagan.com/javaexamples2. */ package com.davidflanagan.examples.net; import java.io.*; import java.net.*; import java.util.*; /** * This class is a generic framework for a flexible, multi-threaded server. * It listens on any number of specified ports, and, when it receives a * connection on a port, passes input and output streams to a specified Service * object which provides the actual service. It can limit the number of * concurrent connections, and logs activity to a specified stream. **/ public class Server { /** * A main() method for running the server as a standalone program. The * command-line arguments to the program should be pairs of servicenames * and port numbers. For each pair, the program will dynamically load the * named Service class, instantiate it, and tell the server to provide * that Service on the specified port. The special -control argument * should be followed by a password and port, and will start special * server control service running on the specified port, protected by the * specified password. **/ public static void main(String[] args) { try { if (args.length < 2) // Check number of arguments throw new IllegalArgumentException("Must specify a service"); // Create a Server object that uses standard out as its log and // has a limit of ten concurrent connections at once. Server s = new Server(System.out, 10); // Parse the argument list int i = 0; while(i < args.length) { if (args[i].equals("-control")) { // Handle the -control arg i++; String password = args[i++]; int port = Integer.parseInt(args[i++]); // add control service s.addService(new Control(s, password), port); } else { // Otherwise start a named service on the specified port. // Dynamically load and instantiate a Service class String serviceName = args[i++]; Class serviceClass = Class.forName(serviceName); Service service = (Service)serviceClass.newInstance(); int port = Integer.parseInt(args[i++]); s.addService(service, port); } } } catch (Exception e) { // Display a message if anything goes wrong System.err.println("Server: " + e); System.err.println("Usage: java Server " + "[-control <password> <port>] " + "[<servicename> <port> ... ]"); System.exit(1); } } // This is the state for the server Map services; // Hashtable mapping ports to Listeners Set connections; // The set of current connections int maxConnections; // The concurrent connection limit ThreadGroup threadGroup; // The threadgroup for all our threads PrintWriter logStream; // Where we send our logging output to /** * This is the Server() constructor. It must be passed a stream * to send log output to (may be null), and the limit on the number of * concurrent connections. **/ public Server(OutputStream logStream, int maxConnections) { setLogStream(logStream); log("Starting server"); threadGroup = new ThreadGroup(Server.class.getName()); this.maxConnections = maxConnections; services = new HashMap(); connections = new HashSet(maxConnections); } /** * A public method to set the current logging stream. Pass null * to turn logging off **/ public synchronized void setLogStream(OutputStream out) { if (out != null) logStream = new PrintWriter(out); else logStream = null; } /** Write the specified string to the log */ protected synchronized void log(String s) { if (logStream != null) { logStream.println("[" + new Date() + "] " + s); logStream.flush(); } } /** Write the specified object to the log */ protected void log(Object o) { log(o.toString()); } /** * This method makes the server start providing a new service. * It runs the specified Service object on the specified port. **/ public synchronized void addService(Service service, int port) throws IOException { Integer key = new Integer(port); // the hashtable key // Check whether a service is already on that port if (services.get(key) != null) throw new IllegalArgumentException("Port " + port + " already in use."); // Create a Listener object to listen for connections on the port Listener listener = new Listener(threadGroup, port, service); // Store it in the hashtable services.put(key, listener); // Log it log("Starting service " + service.getClass().getName() + " on port " + port); // Start the listener running. listener.start(); } /** * This method makes the server stop providing a service on a port. * It does not terminate any pending connections to that service, merely * causes the server to stop accepting new connections **/ public synchronized void removeService(int port) { Integer key = new Integer(port); // hashtable key // Look up the Listener object for the port in the hashtable final Listener listener = (Listener) services.get(key); if (listener == null) return; // Ask the listener to stop listener.pleaseStop(); // Remove it from the hashtable services.remove(key); // And log it. log("Stopping service " + listener.service.getClass().getName() + " on port " + port); } /** * This nested Thread subclass is a "listener". It listens for * connections on a specified port (using a ServerSocket) and when it gets * a connection request, it calls the servers addConnection() method to * accept (or reject) the connection. There is one Listener for each * Service being provided by the Server. **/ public class Listener extends Thread { ServerSocket listen_socket; // The socket to listen for connections int port; // The port we're listening on Service service; // The service to provide on that port volatile boolean stop = false; // Whether we've been asked to stop /** * The Listener constructor creates a thread for itself in the * threadgroup. It creates a ServerSocket to listen for connections * on the specified port. It arranges for the ServerSocket to be * interruptible, so that services can be removed from the server. **/ public Listener(ThreadGroup group, int port, Service service) throws IOException { super(group, "Listener:" + port); listen_socket = new ServerSocket(port); // give it a non-zero timeout so accept() can be interrupted listen_socket.setSoTimeout(600000); this.port = port; this.service = service; } /** * This is the polite way to get a Listener to stop accepting * connections ***/ public void pleaseStop() { this.st