日期:2014-05-16  浏览次数:20435 次

JSP和Servlet那些事儿系列--初探HTTP服务器

? 《JSP和Servlet那些事儿 》系列文章旨在阐述Servlet(Struts和Spring的MVC架构基础)和JSP内部原理以及一些比较容易混淆的概念(比如forward和redirect区别、静态include和<jsp:include标签区别等)和使用,本文为系列文章之启蒙篇--初探HTTP服务器,基本能从本文中折射出Tomcat和Apache HTTPD等处理静态文件的原理。敬请关注连载!

?

? 在学习Servlet和JSP的过程中,如果对HTTP协议本身以及HTTP服务器运行原理有初步的认识的话,这会使得后边的学习更加容易。HTTP服务器本身的内部原理对于Java而言是比较简单的,就是一个Socket处理;请求的解析就是Socket InputStream的读取和分析,所谓的响应仅仅是按照HTTP协议规定的顺序把字节流写入到Socket OutputStream里面。以下是一个简单的HTTP服务器,希望读者在阅读代码的过程中能够想起RFC2616的一些相关术语、或者能够很容易的理解代码。

?

? 一个简单的HTTP服务器需要注意以下几点

? 1,HTTP服务器监听主机和端口:也就是Java Socket的监听主机和端口

? 2,DocRoot:也就是文档根路径,就是http服务器查找客户端请求资源的根路径。

? 3,处理线程:需要有至少一个处理线程用于解析Socket输入流及回写请求到Socket输出流,Socket输出流就是发给客户端的通道

?4,以上配置最好提供配置文件(类似具有HTTP服务功能的tomcat配置文件conf/server.xml,Apache HTTPD的httpd.conf文件)

?

? 根据以上几点,Java实现基本的HTTP服务器的思路如下

? 1,需要写一个类,代码全局的HTTP服务器配置(端口,线程数等);对应本文中Configure类

? 2,需要一个Main类,实质就是主线程,主线程需要绑定ServerSocket用于监听客户端请求,并启动多个处理线程处理客户端Socket请求; 对应本文中的HttpServer类

? 3,需要多个处理线程,用于处理主线程分配的Socket处理任务; 对应本文中的ProcessThread类

? 4,需要一个专门用于解析HTTP请求的类,该类从Socket中获取到输入流,然后读取输入流中的字节,从而解析出客户端希望请求的资源; 对应本文中的HttpRequest类

? 5,需要一个专门回写请求的类,把客户端请求的资源对应的文件流输出到Socket的输出流,如果资源找不到的话,就返回404给客户端; 对应本文中的HttpResponse类

? 以下是全部的代码:

?

? 常量类:

??? 主要定义了一些常量,比如HTTP服务器默认的监听主机和端口、默认的文档根路径、默认处理线程数、默认配置文件等。

?

package lesson1.server;

public final class Constants {
	/**
	 * Listener's default values.
	 */
	public final static String DEFAULT_HOST = "localhost";
	public final static int DEFAULT_PORT = 8080;
	public final static String DEFAULT_DOC_ROOT = "./webapps";
	
	/**
	 * Default work thread count. 
	 */
	public final static int DEFAULT_WORKER_COUNT = 10;

    public static final byte CR = (byte) '\r';
    public static final byte LF = (byte) '\n';
    public static final byte SP = (byte) ' ';
    public static final byte HT = (byte) '\t';
    public static final String CRLF = "\r\n";
    public static final byte COLON = (byte) ':';
    
    public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
    
    public static final int HTTP_MESSAGE_404 = 404;
    public static final int HTTP_MESSAGE_200 = 200;
    public static final int HTTP_MESSAGE_500 = 500;
    public static final int HTTP_MESSAGE_503 = 503;
    
    public static final String DEFAULE_CONFIG_FILE ="server.properties";    
    public static final String CONFIG_HOST = "host";
    public static final String CONFIG_PORT ="port";
    public static final String CONFIG_DOCROOT ="docRoot";
    public static final String CONFIG_THREAD_COUNT ="threadCount";
    
}
?

? 全局配置类:

??? 该类主要负责从配置文件中读取到HTTP服务器的监听主机、端口、DocRoot等重要信息。HTTP服务器的其他代码全部都会应用这个类的属性。

?

package lesson1.server;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

/**
 * HTTP服务器全局配置项
 * 
 * @author sta
 * 
 */
public class Configure {

	// listening host
	private String host = Constants.DEFAULT_HOST;
	// listening port
	private int port = Constants.DEFAULT_PORT;
	// Document Root which locate the static resource
	private String docRoot = Constants.DEFAULT_DOC_ROOT;

	/**
	 * Http Server config file path
	 */
	private String configFile = Constants.DEFAULE_CONFIG_FILE;

	/**
	 * Worker thread count.
	 */
	private int workerCount = Constants.DEFAULT_WORKER_COUNT;

	// 发送HTTP响应的缓冲区大小
	private static final int DEFAULT_SEND_BUFFER_SIZE = 8 * 1024; // default 8k
	private int sendBufferSize = DEFAULT_SEND_BUFFER_SIZE;

	private static final Configure instance = new Configure();

	// for singleton
	private Configure() {
		Properties properties = new Properties();
		InputStream in = null;
		try {
			in = new FileInputStream(configFile);
			pr