📄 server.java
字号:
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.Connection:" + client.getInetAddress().getHostAddress() + ":" + client.getPort()); this.client = client; this.service = service; } /** * 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 (IOException 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 typically 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 interface 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 many 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 are 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 implements 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 cannot 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) { if (connected) { out.print("ONLY ONE CONTROL CONNECTION ALLOWED.\n"); out.close(); return; } else connected = true; } // This is the main loop: read a command, parse it, and handle it for(;;) { // infinite loop out.print("> "); // Display a prompt 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 (command.equals("password")) { // Password command String p = t.nextToken(); // Get the next word if (p.equals(this.password)) { // 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 (command.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 limit 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 <password>\n" + "\tadd <service> <port>\n" + "\tremove <port>\n" + "\tmax <max-connections>\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, then output details of the exception. out.print("ERROR WHILE PARSING OR EXECUTING COMMAND:\n" + e + "\n"); } } // Finally, when the loop command loop ends, close the streams // and set our connected flag to false so that other clients can // now connect. connected = false; out.close(); in.close(); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -