`
xglv2013
  • 浏览: 38008 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

    博客分类:
  • Java
阅读更多
这里使用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<Runnable>(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("<HTML>\r\n");
	                out.write("<HEAD><TITLE>File Not Found</TITLE>\r\n");
	                out.write("</HEAD>\r\n");
	                out.write("<BODY>");
	                out.write("<H1>HTTP Error 404: File Not Found</H1>\r\n");
	                out.write("</BODY></HTML>\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("<HTML>\r\n");
	            out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n");
	            out.write("</HEAD>\r\n");
	            out.write("<BODY>");
	            out.write("<H1>HTTP Error 501: Not Implemented</H1>\r\n");
	            out.write("</BODY></HTML>\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 著
  • 大小: 6 KB
  • 大小: 4.6 KB
2
3
分享到:
评论

相关推荐

    NIO加线程池实现Android消息推送

    本项目"**NIO加线程池实现Android消息推送**"旨在通过Java NIO和线程池技术构建一个高效的推送系统,尤其适用于服务器需要处理大量并发连接的情况。 首先,我们来理解NIO(Non-blocking Input/Output)的概念。NIO...

    多线程控制、线程池模型、HTTP线程.rar

    当一个HTTP请求到达服务器,服务器会分配一个线程来处理这个请求,包括解析请求、执行业务逻辑、生成响应等。如果使用线程池模型,HTTP请求会被放入线程池的任务队列,等待空闲线程处理,这可以提高服务器的并发处理...

    多线程控制,线程池模式,HTTP线程

    线程池模式是一种管理线程的技术,它预先创建了一组线程,当有新的任务需要执行时,线程池会从池中选择一个空闲线程来执行任务,而不是每次都创建新线程。这样可以减少线程创建和销毁的开销,提高系统响应速度。...

    Asynchronous-Server:使用非阻塞 IO 和自制线程池实现的异步服务器,能够使用有限的线程集处理数百个并发网络连接

    综上所述,Asynchronous-Server项目展示了如何使用Java的非阻塞I/O和自定义线程池来构建一个高效的异步服务器,以处理大规模并发网络连接。理解并掌握这些技术对于任何需要处理高并发问题的Java开发者来说都是至关...

    android 线程池下载

    在使用线程池下载大文件时,我们可以将文件分块,每个线程负责下载一个块。这样可以提高下载效率,同时在断点续传时也能轻松处理。线程间通过共享变量记录当前下载进度,确保各个线程协调工作。 在`Android人必看....

    操作系统_线程池_WebServer

    这个实验是一个很好的实践,它帮助我们理解操作系统如何通过线程池来高效地处理Web服务器的并发请求,同时也展示了Java并发编程在实际项目中的应用。通过分析和改进这个实验,我们可以深入理解操作系统、线程管理和...

    http server服务器

    在"MultithreadedServer"这个文件中,你可能找到一个简单的多线程HTTP服务器实现,它可能使用了Java的`java.net.ServerSocket`和`java.lang.Thread`类来监听连接和创建处理请求的线程。而"HttpServer"文件可能是一个...

    Java实现的线程池、消息队列功能

    当一个新任务提交时,线程池会分配一个空闲线程来执行任务,而不是每次都创建新的线程。这降低了创建和销毁线程的开销,提高了系统性能。在Java中,`java.util.concurrent`包下的`ExecutorService`和`...

    java(简易)多线程HTTP服务器

    在Java编程领域,构建一个简易的多线程HTTP服务器是一项常见的练习,这有助于理解网络通信、线程管理和HTTP协议的基本原理。在这个项目中,我们利用Java的线程池和Socket库来实现这样一个服务器,它支持GET、POST...

    Java线程池实战PDF下载 .pdf

    Java线程池的核心类`ThreadPoolExecutor`是`ExecutorService`接口的一个实现。它通过以下几个关键组件实现了线程池的基本功能: - **ExecutorService接口**:提供了任务提交和管理的基本方法。 - **...

    深入研究线程池~

    例如,在Web服务器中,可以设置一个固定的线程池大小,以处理HTTP请求;在数据库访问层,可以采用具有动态调整能力的线程池,以应对不同时间段的负载变化。 总之,线程池作为现代软件架构中的重要组成部分,其设计...

    线程池网页访问

    线程池在IT行业中是一种非常重要的并发处理机制,特别是在服务器端编程中,它扮演着优化系统资源使用、提高系统响应速度的关键角色。本篇将深入探讨线程池、HTTP请求以及它们与服务器之间的关联。 首先,线程是操作...

    Java分布式应用学习笔记07线程池应用

    ### Java分布式应用学习笔记07线程池应用 在深入探讨Java分布式应用中线程池的应用之前,我们先来理解一下线程池的基本概念及其...因此,掌握线程池的设计原理和使用技巧,对于每一个Java开发人员来说都是非常必要的。

    线程池参考

    Java中的线程池实现通常基于工作队列,这是一个包含待处理任务的队列,由一组固定的线程进行处理。当线程从队列中取出任务并执行时,其他线程可以在队列空时等待新的任务到来。这种设计避免了调用者线程被阻塞的情况...

    curl线程池多线程调用

    在IT行业中,网络请求是应用程序不可或缺的一部分,而`curl`作为一个强大的命令行工具,被广泛用于HTTP和其他协议的客户端操作。当我们需要处理大量的网络请求,尤其是进行数据抓取或者批量接口调用时,单线程的效率...

    基于Java线程池技术的数据爬虫设计与实现.zip

    总结,本项目“基于Java线程池技术的数据爬虫设计与实现”旨在通过Java编程,利用线程池的优势,实现一个高效、稳定、可扩展的数据爬虫。这涵盖了多线程编程、网络通信、HTML解析、数据存储等多个IT领域的知识点,...

    JAVA实现知识问答服务器

    在本项目"JAVA实现知识问答服务器"中,我们将探讨如何使用Java编程语言构建一个能够处理问题与答案交互的服务器。这个服务器是针对初学者设计的,因此它将涵盖Java的基础概念,同时也为进阶学习者提供了实践平台。...

    webserver:java中的简单多线程Web服务器。 它使用线程池以获得更好的客户服务

    标题“webserver:java中的简单多线程Web服务器”指出,这是一个使用Java实现的Web服务器,它利用了多线程技术。多线程允许服务器同时处理多个客户端请求,提高了并发性能。每个客户端连接都会被分配到一个独立的线程...

    了解线程池

    线程池是多线程编程中的一个重要概念,它在Java、Python、C#等许多编程语言中都有广泛应用。线程池的出现是为了有效地管理和控制线程的创建与销毁,提高系统的资源利用率,防止过多线程导致系统资源耗尽。本文将深入...

    Android线程池+单例模式+webService

    综合以上,这个项目可能是一个实现Android应用与服务器通信的示例,利用线程池优化后台任务执行,采用单例模式管理网络请求,并通过WebService接口进行数据交换。理解并熟练掌握这些技术,对于提升Android开发能力...

Global site tag (gtag.js) - Google Analytics