📄 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 + -