Perfect Socket Server program

zhaozj2021-02-08  216

/ * * 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.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 IllegaArgumentException ("Must Specify a Service"); // Create a Server Object That Uses Standard Out As ITS LOG AND / / HAS A LIMIT OF Ten Concurrent Connections Atce. Server S = New Server (System.out, 10);

// PARSE THE ARGUMENT LIST INT I = 0; While (I ]" "[ .. .] "); 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 (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 (! services.get (key) = null) service is already on that port if 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 Hashta BLE Services.Put (Key, Listener); // log it log ("Starting Service" Service.getClass (). getName () "on port" port); // start the lisning. 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 listner.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 Series Vice service; // the service to provide on what port volatile boolean stop = false; // WHether Weider 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 interface.setsotimeout (600000); this.port = port; this.service = service;}

/ ** * this is the polite way to get a listener to stop accessing * connection *** / public void please; // set the stop flag this.interrupt (); // stop block block IN accept () try {listen_socket.close ();} // stop listening. catch (ioexception e) {}} / ** * a listener is a thread, and this is its body. * Wait for connection requests, Accept Them And pass the socket on * to the addconnection method of the server. ** / public void run () {while (! stop) {// loop unsteil we're asked to stop. try {socket client = listen_socket.accept ( ); addConnection (client, service);} catch (InterruptedIOException e) {} catch (IOException e) {log (e);}}}} / ** * This is the method that Listener objects call when they accept a * connection fly rom a client. It either creates a Connection object * for the connection and adds it to the list of current connections, * or, if the limit on connections has been reached, it closes the * connection. ** / protected synchronized void addConnection ( Socket s, Service service) {// If the connection limit has been reached if (connections.size ()> = maxConnections) {try {// Then tell the client it is being rejected. PrintWriter out = new PrintWriter (s.getOutputStream ()); OUT.PRINT ("Connection Refuse;" "The Server IS busy; please try, latter); out.flush (); // and close the connection to the rejected client. S.close (); // and log it, of course log ("

CONNECTION REFUSED TO " S.Getinetaddress (). GetHostAddress () ": s.getport () ": max connections reached.");} Catch (ooException e) {log (e);}} else { // otherwise, if the limit has not been reached // create a connections; // address (s, service); // address (s, service); // add it to the list of current connections connects.add (c); / / Log this new connection log ("Connected to" s.Getinetaddress (). GetHostaddress () ":" S.Getport () "on port" s.getlocalport () "for service" service. . getClass () getName ()); // And start the Connection thread to provide the service c.start ();.}} / ** * A Connection thread calls this method just before it exits It removes * the specified Connection from ** / protected synchronized void endconnection (connection c) {connections.remove (c); log ("Connection to" c.client.getinetaddress (). gethostaddress () ":" c.cli ENT.GETPORT () "closed.");

/ ** Change the current connection limit * / public synchronized void setMaxConnections (int max) {maxconnections = max;}

/ ** * This method displays status information about the server on the * specified stream. It can be used for debugging, and is used by the * Control service later in this example. ** / public synchronized void displayStatus (PrintWriter out) { // Display a list of all services That Are Being provided iterator keys = services.keyset (). Iterator (); while (keys.hasnext ()) {integer port = (integer) keys.next (); listener Listener = ( Listener); out.print ("service" listener.service.getClass (). GetName () "on port" port "/ n");} // Display THE CURRENT Connection Limit OUT.PRINT ("Max Connections:" MaxConnections "/ N"); // Display a list of all current connections iterator connS = connections.iterator (); while (connS.hasNext ()) {connection c = (Connection) ); out.print ("Connected to" c.client.getinetaddress (). getHostaddress () ": c.client.get () " on port " c.client.getlocalp Ort () "for service" C.Service.getClass (). getname () "/ n");}}

/ ** * This class is a subclass of Thread that handles an individual * connection between a client and a Service provided by this server. * Because each such connection has a thread of its own, each Service can * have multiple connections pending at once . Despite all the other * threads in use, this is the key feature that makes this a * multi-threaded server implementation ** / public class Connection extends Thread {Socket client;. // The socket to talk to the client through Service service ; // The service being provided to that client / ** * This constructor just saves some state and calls the superclass * constructor to create a thread to handle the connection Connection * objects are created by Listener threads These threads are part of *.. The Server's Threadgroup, So All Connection Threads Are Part of That * Group, Too. ** / Public Connection (SOCKET Client, Service Service) {Super ("Server.Connect) ION: " Client.GetinetDress (). getHostaddress () ": client.get (); this.client = client;

} / ** * This is the body of each and every Connection thread. * All it does is pass the client input and output streams to the * serve () method of the specified Service object. That method is * responsible for reading from and writing to those streams to * provide the actual service. Recall that the Service object has * been passed from the Server.addService () method to a Listener * object to the addConnection () method to this Connection object, and * is now finally being used to provide the service. Note that just * before this thread exits it always calls the endConnection () method * to remove itself from the set of connections ** / public void run () {try {InputStream in = client.getInputStream () OutputStream out = client.getOutputStream (); service.serve (in, out);} catch (ooException e) {log (e);} finally {endConnection (this);

}}} / ** * Here is the Service interface that we have seen so much of. It defines * only a single method which is invoked to provide the service. Serve () * will be passed an input stream and an output stream to the client. It * should do whatever it wants with them, and should close them before * returning. * * All connections through the same port to this service share a single * Service object. Thus, any state local to an individual connection must * be stored in local variables within the serve () method. State that * should be global to all connections on the same port should be stored * in instance variables of the Service class. If the same Service is * running on more than one port, THERE WILL TYPICLY BE DIFFERENT * Service Instances for Each Port. Data That Should Be Global To All * Connections On Any Port Should Be Stored In Static Variables. * * Note That Implementations of this Interfac e must have a no-argument * constructor if they are to be dynamically instantiated by the main () * method of the Server class ** / public interface Service. {public void serve (InputStream in, OutputStream out) throws IOException;}

/ ** * A very simple service. It displays the current time on the server * to the client, and closes the connection. ** / public static class Time implements Service {public void serve (InputStream i, OutputStream o) throws IOException { PrintWriter out = new printwriter (o); out.print (new Date () "/ n"); out.close (); i.close ();}} / ** * this is another example service. It reads lines of input from the * client, and sends them back, reversed. It also displays a welcome * message and instructions, and closes the connection when the user * enters a '.' on a line by itself. ** / public static class Reverse implements Service {public void serve (InputStream i, OutputStream o) throws IOException {BufferedReader in = new BufferedReader (new InputStreamReader (i)); PrintWriter out = new PrintWriter (new BufferedWriter (new OutputStreamWriter (o))) Out.Print ("Welcome to the line reversal server./n"); out.print ("Enter Lines. End with a '.' On a line by itself./n"); for (;;) {OUT .print (">"); out.flush (); string line = in.readline (); if ((line == null) || line.equals (".")) Break; for (int J = line .length () - 1; j> = 0; J - out.print (line.Charat (j)); out.print ("/ n");} out.close (); in.close () ;

}} / ** * This service is an HTTP mirror, just like the HttpMirror class * implemented earlier in this chapter. It echos back the client's * HTTP request ** / public static class HTTPMirror implements Service {public void serve (InputStream i, OutputStream o) throws IOException {BufferedReader in = new BufferedReader (new InputStreamReader (i)); PrintWriter out = new PrintWriter (o); out.print ( "HTTP / 1.0 200 / n"); out.print ( "Content-Type : text / plain / n / n "); string line; while ((line = in.readline ())! = null) {if (line.Length () == 0) Break; out.print (line " / n ");} out.close (); in.close ();}} / ** * This service demonstrates how to maintain state across connections by * saving it in instance variables and using synchronized access to those * variables It. Maintains a count of how much clients have connected and * tells each client what number it is ** / public static class UniqueID implements Service {public int id = 0; public synchronized int nextId () {return id ;} public void serve (InputStream i, OutputStream o) throws IOException { PrintWriter Out = New PrintWriter (O); OUT.PRINT ("You area Client #:" nextId () "/ n"); out.close (); i.close ();

}} / ** * This is a non-trivial service. It implements a command-based protocol * that gives password-protected runtime control over the operation of the * server. See the main () method of the Server class to see how . this * service is started * * The recognized commands are: * password: give password; authorization is required for most commands * add: dynamically add a named service on a specified port * remove: dynamically remove the service running on a specified port * max: change the current maximum connection limit * status:. display current services, connections, and connection limit * help: display a help message * quit: disconnect * * This service displays a prompt, and sends all of its output to the user * IN Capital Letters. Only One Client Is Allowed to Connect To this * Service at a time. ** / public static class control ustements service { Server server; // The server we control String password; // The password we require boolean connected = false; // Whether a client is already connected / ** * Create a new Control service It will control the specified Server * object,. and will require the specified password for authorization * Note that this Service does not have a no argument constructor, * which means that it can not be dynamically instantiated and added as * the other, generic services above can be. ** / public Control (Server Server, String Password) {this.server = server; this.password = password;}

/ ** * This is the serve method that provides the service. It reads a * line the client, and uses java.util.StringTokenizer to parse it * into commands and arguments. It does various things depending on * the command. ** / public void serve (InputStream i, OutputStream o) throws IOException {// Setup the streams BufferedReader in = new BufferedReader (new InputStreamReader (i)); PrintWriter out = new PrintWriter (o); String line; // For reading client input lines // has the user has given the password yet boolean authorized = false;? // If there is already a client connected to this service, display // a message to this client and close the connection We use a // synchronized block. To Prevent a Race Condition. Synchronized (this) {ified {Out.Print ("Only One Control Connection Allowed./N"); out.close (); Else connection = true;

// this is the main loop: read a command, parse it, and handle it for (;;) {// infinite loop out.print (">"); // Display a prospt out.flush (); // Make IT APPEAR Right Away Line = In.readLine (); // Get The user's INPUT IF (LINE == NULL) Break; // Quit IF We get Eof. Try {// use a stringtokenizer to Parse The User's Command StringTokenizer T = New StringTokenizer (LINE); if (! T.hasmoreToKens ()) Continue; // if INPUT WAS EMPTY / / GET First Word of the Input and Convert To Lower Case String Command = T.NEXTTOKEN (). TOLOWERCASE (); // Now Compare to Each of the Possible Commands, Doing The // Appropriate Thing for Each Command IF ("Password")) {// Password Command String P = T.NEXTTOKEN (); // get the next wordiff {// is it the password? Out.print ("ok / n"); // Say so authorized = true; // GRANT Authorization} else out.print ("invalid password / n"); // Otherwise fail} Else IF (Command.equals ("add")) {// add service command // check WHether Password Has Been Given if (! Authorized ) Out.print ("Password Required / N);

else {// Get the name of the service and try to // dynamically load and instantiate it // Exceptions will be handled below String serviceName = t.nextToken ();. Class serviceClass = Class.forName (serviceName); Service service; try {service = (Service) serviceClass.newInstance ();} catch (NoSuchMethodError e) {throw new IllegalArgumentException ( "Service must have a" "no-argument constructor");} int port = Integer.parseInt (t.nextToken ()); // if no exceptions occurred, add the service server.addservice (service, port); out.print ("service added / n"); // Acknowledge }} Else IF ("Remand.equals (" Remove ")) {// Remove Service if (! Authorized) Out.print (" Password Required / N "); Else {Int port = integer.parseint (T.NextToken () ); Server.removeService (port); // remove the service out.print ("service removed / n"); // Acknowledge}} else if (Command.equals ("max")) {// set connection limited IF (! Authorized) Out.print ("Password Required / N);

Else {int max = integer.parseint (t.nextToken ()); server.setmaxconnections (max); out.print ("Max Connections Changed / N");}}}} Else IF (Command.equals ("STATUS")) {// status display if (! Authorized) Out.print ("Password Required / N"); Else Server.DisplayStatus (OUT);} else if (Command.equals ("Help")) {// Help Command // Display Command Syntax. Password NOT Required Out.print ("Commands: / N" "/ TPassword / n" "/ tadd / n" "/ TREMOVE / n" "/ Tmax / n" "/ tstatus / n" "/ thelp / n" "/ tquit / n");} else if (Command.equals ("quit") Break; // quit command. Else out.print ("unrecognized command / n"); // error } Catch (Exception E) {// if an exception Occurred During the command, print an // error message, dam out.print ("Error While Parsing or Executing Command: / N" E " / N ");}}} // finally, when the loop command loop ends, close the streams // and setur connection flag to false so.. Connected = false;

转载请注明原文地址:https://www.9cbs.com/read-2142.html

New Post(0)