📄 proxyserver.java
字号:
import java.io.*;import java.net.*;import java.util.*;/** * This class uses the Server class to provide a multi-threaded server * framework for a relatively simple proxy service. The main() method * starts up the server. The nested Proxy class implements the * Server.Service interface and provides the proxy service. **/public class ProxyServer { /** * Create a Server object, and add Proxy service objects to it to provide * proxy service as specified by the command-line arguments. **/ public static void main(String[] args) { try { // Check number of args. Must be a multiple of 3 and > 0. if ((args.length == 0) || (args.length % 3 != 0)) throw new IllegalArgumentException("Wrong number of args"); // Create the Server object Server s = new Server(null, 12); // log stream, max connections // Loop through the arguments parsing (host, remoteport, localport) // tuples. For each, create a Proxy, and add it to the server. int i = 0; while(i < args.length) { String host = args[i++]; int remoteport = Integer.parseInt(args[i++]); int localport = Integer.parseInt(args[i++]); s.addService(new Proxy(host, remoteport), localport); } } catch (Exception e) { // Print an error message if anything goes wrong System.err.println(e); System.err.println("Usage: java ProxyServer " + "<host> <remoteport> <localport> ..."); System.exit(1); } } /** * This is the class that implements the proxy service. The serve() method * will be called when the client has connected. At that point, it must * establish a connection to the server, and then transfer bytes back and * forth between client and server. For symmetry, this class implements * two very similar threads as anonymous classes. One thread copies bytes * from client to server, and the other copies them from server to client. * The thread that invoke the serve() method creates and starts these * threads, then just sits and waits for them to exit. **/ public static class Proxy implements Server.Service { String host; int port; /** Remember the host and port we are a proxy for */ public Proxy(String host, int port) { this.host = host; this.port = port; } /** The server invokes this method when a client connects. */ public void serve(InputStream in, OutputStream out) { // These are some sockets we'll use. They are final so they can // be used by the anonymous classes defined below. final InputStream from_client = in; final OutputStream to_client = out; final InputStream from_server; final OutputStream to_server; // Try to establish a connection to the specified server and port // and get sockets to talk to it. Tell our client if we fail. final Socket server; try { server = new Socket(host, port); from_server = server.getInputStream(); to_server = server.getOutputStream(); } catch (Exception e) { PrintWriter pw = new PrintWriter(new OutputStreamWriter(out)); pw.print("Proxy server could not connect to " + host + ":" + port + "\n"); pw.flush(); pw.close(); try { in.close(); } catch (IOException ex) {} return; } // Create an array to hold two Threads. It is declared final so // that it can be used by the anonymous classes below. We use an // array instead of two variables because given the structure of // this program two variables would not work if declared final. final Thread[] threads = new Thread[2]; // Define and create a thread to copy bytes from client to server Thread c2s = new Thread() { public void run() { // Copy bytes 'till EOF from client byte[] buffer = new byte[2048]; int bytes_read; try { while((bytes_read=from_client.read(buffer))!=-1) { to_server.write(buffer, 0, bytes_read); to_server.flush(); } } catch (IOException e) {} finally { // When the thread is done try { server.close(); // close the server socket to_client.close(); // and the client streams from_client.close(); } catch (IOException e) {} } } }; // Define and create a thread to copy bytes from server to client. // This thread works just like the one above. Thread s2c = new Thread() { public void run() { byte[] buffer = new byte[2048]; int bytes_read; try { while((bytes_read=from_server.read(buffer))!=-1) { to_client.write(buffer, 0, bytes_read); to_client.flush(); } } catch (IOException e) {} finally { try { server.close(); // close down to_client.close(); from_client.close(); } catch (IOException e) {} } } }; // Store the threads into the final threads[] array, so that the // anonymous classes can refer to each other. threads[0] = c2s; threads[1] = s2c; // start the threads c2s.start(); s2c.start(); // Wait for them to exit try { c2s.join(); s2c.join(); } catch (InterruptedException e) {} } }}/** * 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. **/class Server { // 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.stop = true; // Set the stop flag this.interrupt(); // Stop blocking 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 until 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 from 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 refused; " + "the server is busy; please try again later.\n"); 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 (IOException e) {log(e);} } else { // Otherwise, if the limit has not been reached // Create a Connection thread to handle this connection Connection c = new Connection(s, service); // Add it to the list of current connections connections.add(c); // Log this new connection log("Connected to " + s.getInetAddress().getHostAddress() + ":" + s.getPort() + " on port " + s.getLocalPort() +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -