一个使用ThreadPoolExecutor线程池实现的简单的HTTP服务器

阅读更多
这里使用java socket和concurrent包里的ThreadPoolExecutor实现了一个小型的HTTP服务器,管理入站请求,代码如下:

package jHttpNew;

import java.net.*;
import java.io.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class JHTTP extends Thread 
{
	//文件所在目录
	private File documentRootDirectory;
	//默认的文件名
	private String indexFileName = "index.html";
	private ServerSocket server;
    
	public JHTTP(File documentRootDirectory, int port, String indexFileName) throws IOException
	{
		if (!documentRootDirectory.isDirectory())
		{
			throw new IOException(documentRootDirectory + " does not exist as a directory"); 
		}
		this.documentRootDirectory = documentRootDirectory;
		this.indexFileName = indexFileName;
		this.server = new ServerSocket(port);
	}

	public JHTTP(File documentRootDirectory, int port) throws IOException
	{
		this(documentRootDirectory, port, "index.html");
	}

	public JHTTP(File documentRootDirectory) throws IOException 
	{
		this(documentRootDirectory, 80, "index.html");
	}

	public void run()
	{
		//建立一个Http Request线程池
		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,TimeUnit.SECONDS, 
				new ArrayBlockingQueue(3),new ThreadPoolExecutor.AbortPolicy());
	
		System.out.println("Accepting connections on port " + server.getLocalPort());
		System.out.println("Document Root: " + documentRootDirectory);
		while (true)
		{
			try
			{
				//使用服务器端socket,接受http请求
				Socket request = server.accept();
				//用线程池处理http请求
				threadPool.execute(new ThreadPoolTask(documentRootDirectory, indexFileName, request));
			}
			catch (IOException ex) 
			{ 
			}   
		}  
	}
  
	public static void main(String[] args)
	{
	    // get the Document root
	    File docroot;
	    try 
	    {
	    	docroot = new File("C:/jhttp_test");
	    }
	    catch (ArrayIndexOutOfBoundsException ex) 
	    {
	    	System.out.println("Usage: java JHTTP docroot port indexfile");
	    	return;
	    }
	    
	    // set the port to listen on
	    int port;
	    try 
	    {
	    	port = Integer.parseInt(args[1]);
	    	if (port < 0 || port > 65535) port = 80;
	    }  
	    catch (Exception ex)
	    {
	    	port = 80;
	    }  
	    
	    try 
	    {            
	    	JHTTP webserver = new JHTTP(docroot, port);
            webserver.start();
	    }
	    catch(IOException ex) 
	    {
	    	System.out.println("Server could not start because of an " + ex.getClass());
	    	System.out.println(ex);
	    }
  
	}

}


package jHttpNew;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.net.Socket;
import java.util.Date;
import java.util.StringTokenizer;

public class ThreadPoolTask implements Runnable, Serializable
{
	
	private static final long serialVersionUID = 0;
	private File documentRootDirectory;
	private String indexFileName = "index.html";
	private Object threadPoolTaskData;
	ThreadPoolTask(File documentRootDirectory, 
			   String indexFileName,
			   Object tasks)
	{
		if (documentRootDirectory.isFile())
		{
		    throw new IllegalArgumentException("documentRootDirectory must be a directory, not a file"); 
		}
		this.documentRootDirectory = documentRootDirectory;
		try 
		{
		    this.documentRootDirectory = documentRootDirectory.getCanonicalFile();
		}
		catch (IOException ex)
		{
		}
		if (indexFileName != null) 
			this.indexFileName = indexFileName;
		this.threadPoolTaskData = tasks;
	}
	public void run() 
	{    
	    // for security checks
	    String root = documentRootDirectory.getPath();
	        
	    // 建立socket connection对象
	    Socket connection;
	    connection = (Socket) threadPoolTaskData; 

	    try 
	    {            
	        String filename;
	        String contentType;   
	        OutputStream raw = new BufferedOutputStream(
	                            connection.getOutputStream()
	                           );         
	        Writer out = new OutputStreamWriter(raw);
	        Reader in = new InputStreamReader(
	                     new BufferedInputStream(
	                      connection.getInputStream()
	                     ),"ASCII"
	                    );
	        StringBuffer requestLine = new StringBuffer();
	        
	        //读取输入流,这里只读第一行
	        int c;
	        while (true)
	        {
	            c = in.read();
	            if (c == '\r' || c == '\n') break;
	            requestLine.append((char) c);
	        }
	        
	        String get = requestLine.toString();
	        
	        // log the request 
	        System.out.println(get);
	        
	        StringTokenizer st = new StringTokenizer(get);
	        String method = st.nextToken();
	        String version = "";
	        if (method.equals("GET"))
	        {
	            filename = st.nextToken();
	            if (filename.endsWith("/")) filename += indexFileName;
	            contentType = guessContentTypeFromName(filename);
	            if (st.hasMoreTokens())
	            {
	            	version = st.nextToken();
	            }

	            File theFile = new File(documentRootDirectory, 
	            filename.substring(1,filename.length()));
	            if (theFile.canRead() 
	              // Don't let clients outside the document root
	            		&& theFile.getCanonicalPath().startsWith(root)) 
	            {
	            	DataInputStream fis = new DataInputStream(
	                                   new BufferedInputStream(
	                                    new FileInputStream(theFile)
	                                   )
	                                  );
	            byte[] theData = new byte[(int) theFile.length()];
	            fis.readFully(theData);
	            fis.close();
	            if (version.startsWith("HTTP "))
	            {   // send a MIME header
	                out.write("HTTP/1.0 200 OK\r\n");
	                Date now = new Date();
	                out.write("Date: " + now + "\r\n");
	                out.write("Server: JHTTP/1.0\r\n");
	                out.write("Content-length: " + theData.length + "\r\n");
	                out.write("Content-type: " + contentType + "\r\n\r\n");
	                out.flush();
	            }  // end if
	        
	            // send the file; it may be an image or other binary data 
	            // so use the underlying output stream 
	            // instead of the writer
	            raw.write(theData);
	            raw.flush();
	            }  // end if
	            
	            //如果文件不存在,返回404
	            else
	            {  // can't find the file
	                if (version.startsWith("HTTP "))
	                {  // send a MIME header
	                    out.write("HTTP/1.0 404 File Not Found\r\n");
	                    Date now = new Date();
	                    out.write("Date: " + now + "\r\n");
	                    out.write("Server: JHTTP/1.0\r\n");
	                    out.write("Content-type: text/html\r\n\r\n");
	                } 
	                out.write("\r\n");
	                out.write("File Not Found\r\n");
	                out.write("\r\n");
	                out.write("");
	                out.write("

HTTP Error 404: File Not Found

\r\n"); out.write("\r\n"); out.flush(); } } //这里只是先实现了GET方法,如果用户没有输入GET,那么返回501的返回码,表示方法未实现 else { // method does not equal "GET" if (version.startsWith("HTTP ")) { // send a MIME header out.write("HTTP/1.0 501 Not Implemented\r\n"); Date now = new Date(); out.write("Date: " + now + "\r\n"); out.write("Server: JHTTP 1.0\r\n"); out.write("Content-type: text/html\r\n\r\n"); } out.write("\r\n"); out.write("Not Implemented\r\n"); out.write("\r\n"); out.write(""); out.write("

HTTP Error 501: Not Implemented

\r\n"); out.write("\r\n"); out.flush(); } } catch (IOException ex) { } finally { try { connection.close(); } catch (IOException ex) {} } } // end run public static String guessContentTypeFromName(String name) { if (name.endsWith(".html") || name.endsWith(".htm")) { return "text/html"; } else if (name.endsWith(".txt") || name.endsWith(".java")) { return "text/plain"; } else if (name.endsWith(".gif")) { return "image/gif"; } else if (name.endsWith(".class")) { return "application/octet-stream"; } else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) { return "image/jpeg"; } else return "text/plain"; } }


以上只简单实现了GET方法,去获取服务器某个文件路径下的某个文件。这个http服务器的功能可以扩展到POST和DELETE方法,我们可以自行添加。

在Eclipse里面运行JHTTP类,启动这个服务器,控制台显示信息如下:

在本机上用命令行做试验:

回车后在命令行输入:
GET /index.html
就会显示服务器指定路径下的index.html文件的内容


参考资料:
《java Network Programming》 Elliotte Rusty Harold 著
  • 一个使用ThreadPoolExecutor线程池实现的简单的HTTP服务器_第1张图片
  • 大小: 6 KB
  • 一个使用ThreadPoolExecutor线程池实现的简单的HTTP服务器_第2张图片
  • 大小: 4.6 KB
  • 查看图片附件

你可能感兴趣的:(java,socket,thread)