spacer

Webref WebRef   Sitemap · Experts · Tools · Services · Newsletters · About i.com

home / web / dev / proxy

A Multi-threaded Server

Developer News
Eclipse Helios Update Brings New PHP Tools
Internet Explorer 9 Ups Standards Support
JBoss Portal 5 Release Easier to Use

In the UNIX world, many multi-user network servers create additional copies of themselves to handle incoming requests from clients. If you look at the list of running programs on a Web server that runs Apache, you will see 10 copies of "httpd" running. Since Java does not have a way to create new processes, the multi-threading mechanism is used instead. Each thread handles a single client. When it is finished, it is returned to the thread pool to accept another. The default design can handle five "users" simultaneously, though it is really limited by the host server's memory and performance.

The proxy server is built on top of a generic multi-threaded server class. The name of the game, again, is creating code we can use elsewhere, and a networked server is a common enough design element that it doesn't hurt to do a little extra work. The multi-threaded server has a private Vector called threadPool that stores a collection of service threads, miniature runnable pieces of code that can be dispatched to handle clients. Since the class needs to work with any possible service thread and listen to any port, its primary constructor takes a thread class and a port as its arguments. This class must extend ServiceThread or the server will throw an exception when it starts.

    public MultiThreadServer(Class serviceThreadClass, int servicePort);

You start the server by calling the init() method. This method creates threadPoolSize (the default number is 5) threads for handling clients and starts them running. Note, as mentioned above, the check to make sure the serviceThreadClass is an instance of ServiceThread.

    public void init() throws IOException, InstantiationException, IllegalAccessException { threadPool = new Vector(); ss = new ServerSocket(servicePort, backlogSize); // check to make sure we've been handed a ServiceThread; a // service thread class must extend ServiceThread if (!(serviceThreadClass.newInstance() instanceof ServiceThread)) { throw new InstantiationException("a service thread class must extend ServiceThread"}; int i; for (i = 0; i < threadPoolSize; i++) { ServiceThread serviceThread =(ServiceThread)serviceThreadClass.newInstance(); threadPool.addElement(serviceThreadClass.newInstance()); serviceThread.setServer(this); serviceThread.start(); } }

Once each ServiceThread starts running , it loops continuously, attempting to accept() a connection from a client. When it does, it prints out a message on the console that it got a client, and then immediately passes the buck to the method service(). The HttpProxyServiceThread class simply defines service() to talk to the client according to the protocol we have already outlined. It loops, looking for "PROXY" commands and responds in kind. It cuts off the connection with the client by ending the service() call when it sees QUIT.

The meat of the proxy server is in bold below. It attempts to open the URL specified in the PROXY statement. If the connection is made properly, it copies the entire remote file into a buffer in memory. It tells the client the length of the buffer with Content-length: and then prints out the entire set of data. This is the middle tier that connects the applet to the outside world, and does the actual proxying.

    public void service(InputStream in, OutputStream out) throws Exception {
    	PrintStream os = new PrintStream(out);
    	DataInputStream is = new DataInputStream(in);
    
    	os.println("Applet HTTP Proxy Server 1.0, (C) WebConcepts, LLC, 1997");
    	while (true) {
    		String commandLine = is.readLine();
    		if (commandLine.regionMatches(true, 0, "PROXY ", 0, 6)) {
    			String proxyUrlString = commandLine.substring(6);
    			System.out.println("proxying to " + proxyUrlString);
    
    			// fetch the file specified in the URL and redirect it to the client
    			URL proxyURL = new URL(proxyUrlString);
    			InputStream urlStream = proxyURL.openStream();
    
    			// read the stream into buffer
    			byte[] buffer = new byte[0];
    			byte[] chunk = new byte[4096];
    			int count;
    			while ((count = urlStream.read(chunk)) >= 0) {
    				byte [] t = new byte[buffer.length + count];
    				System.arraycopy(buffer, 0, t, 0, buffer.length);
    				System.arraycopy(chunk, 0, t, buffer.length, count);
    				buffer = t;
    			}
    
    			// write the buffer to the output stream
    			os.println("+OK URL fetched; data to follow");
    			os.println("Content-length: " + buffer.length);
    			os.write(buffer);
    
    		}
    		else if (commandLine.regionMatches(true, 0, "QUIT", 0, 4)) {
    			os.println("+OK logging out of proxy server");
    			return;
    		}
    		else {
    			os.println("+ERR unknown directive");
    			continue;
    		}
    	}
    }
    

Comments are welcome

webref The latest from WebReference.com Browse >
Flashmaps' DynamicLocator: Interactive Maps for Small Areas · Flashmaps' AreaSelector: Interactive Maps for Wide Areas · The DB Mapper: Interactive Street-level Maps of U.S. and Canada
Sitemap · Experts · Tools · Services · Email a Colleague · Contact FREE Newsletters 
 The latest from internet.com
MS Access and MySQL · Cisco AutoQoS: VoIP QoS for Mere Mortals · While VoIP Adoption Explodes in Enterprise, Carrier Spending Lags

Created: Oct. 27, 1997
Revised: Oct. 30, 1997

URL: http://webreference.com/dev/proxy/multi.html