相信大多Web開發(fā)者對(duì)Tomcat是非常熟悉的,眾所周知Tomcat是一款非常好用的開源Servlet容器,您一定對(duì)這個(gè)最流行的Servlet容器充滿好奇,雖然它并不像一個(gè)黑盒子那樣讓人無法觸摸但是Tomcat的源碼的確讓人看起來頭疼。筆者就在這里和大家共同分析一個(gè)簡單的Web服務(wù)器是如何工作的源碼下載地址

Web服務(wù)器

Web服務(wù)器是一個(gè)復(fù)雜的系統(tǒng),一個(gè)Web服務(wù)器要為一個(gè)Servlet的請(qǐng)求提供服務(wù),需要做三件事:

1、創(chuàng)建一個(gè)request對(duì)象并填充那些有可能被所引用的Servlet使用的信息,如參數(shù)、頭部、cookies、查詢字符串等等。一個(gè)request對(duì)象是javax.servlet.ServletRequestjavax.servlet.http.ServletRequest接口的一個(gè)實(shí)例

2、創(chuàng)建一個(gè)response對(duì)象,所引用的servlet使用它來給客戶端發(fā)送響應(yīng)。一個(gè)response對(duì)象是javax.servlet.ServletRequestjavax.servlet.http.ServletRequest接口的一個(gè)實(shí)例。

3、調(diào)用servletservice方法,并傳入requestresponse對(duì)象。這里servlet會(huì)從request對(duì)象取值,給response寫值。

在正式展示代碼之前還需要了解一些必須額HTTP的知識(shí)(如果您對(duì)此非常熟悉您可以直接看下面分析代碼)

HTTP

HTTP的定義不知道的童鞋可以自己去度娘,這里主要要說的就是HTTP協(xié)議的格式

HTTP請(qǐng)求包括三部分

1、方法、統(tǒng)一資源標(biāo)識(shí)符(URI)、協(xié)議/版本

2、請(qǐng)求的頭部

3、主題內(nèi)容

下面是一個(gè)HTTP請(qǐng)求的例子

POST /examples/default.jsp HTTP/1.1 
Accept: text/plain; text/html 
Accept-Language: en-gb 
Connection: Keep-Alive 
Host: localhost 
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) 
Content-Length: 33 
Content-Type: application/x-www-form-urlencoded 
Accept-Encoding: gzip, deflate 
 
lastName=Franks&firstName=Michael  

第一行表明這是POST請(qǐng)求方法,/examples/default.jspURIHTTP/1.1是協(xié)議以及版本。其中URI指明了一個(gè)互聯(lián)網(wǎng)資源,這里通常是相對(duì)服務(wù)器根目錄解釋的,也就是說這個(gè)HTTP請(qǐng)求就是告訴服務(wù)器我需要這個(gè)文件目錄如下:根目錄/ examples/default.jsp

最后一行是HTTP的主題內(nèi)容,Servlet會(huì)處理請(qǐng)求的主題內(nèi)容,然后返回給客戶端HTTP響應(yīng)。

類似于HTTP請(qǐng)求,一個(gè)HTTP響應(yīng)也包括上面三個(gè)部分。

1、方法、統(tǒng)一資源標(biāo)識(shí)符(URI)、協(xié)議/版本

2、響應(yīng)的頭部

3、主題內(nèi)容

下面是一個(gè)HTTP響應(yīng)的例子

HTTP/1.1 200 OK 
Server: Microsoft-IIS/4.0 
Date: Mon, 5 Jan 2004 13:13:33 GMT 
Content-Type: text/html 
Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT 
Content-Length: 112 
 
<html> 
<head> 
<title>HTTP Response Example</title> 
</head> 
<body> 
Welcome to Brainy Software 
</body> 
</html>

第一行告訴協(xié)議版本,以及請(qǐng)求成功(200表示成功)

響應(yīng)頭部和請(qǐng)求頭部一樣,一些有用的信息。響應(yīng)的主體就是響應(yīng)本身HTML內(nèi)容。

好了基本知識(shí)介紹完畢,下面開始解釋代碼

部分相關(guān)代碼

import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;

public class HttpServer {

	public static final String WEB_ROOT = System.getProperty("user.dir")
			+ File.separator + "webroot";

	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

	private boolean shutdown = false;

	public static void main(String[] args) {
		HttpServer server = new HttpServer();
		server.await();
	}

	public void await() {
		ServerSocket serverSocket = null;
		int port = 8080;
		try {
			serverSocket = new ServerSocket(port, 1,
					InetAddress.getByName("127.0.0.1"));
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}

		while (!shutdown) {
			Socket socket = null;
			InputStream input = null;
			OutputStream output = null;
			try {
				socket = serverSocket.accept();
				input = socket.getInputStream();
				output = socket.getOutputStream();

				Request request = new Request(input);
				request.parse();

				Response response = new Response(output);
				response.setRequest(request);
				response.sendStaticResource();

				socket.close();

				shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}
	}
}

HttpServer類代表一個(gè)web服務(wù)器。首先提供一個(gè)WEB_ROOT所在的目錄和它下面所有的子目錄下靜態(tài)資源。其次定義了一個(gè)中止服務(wù)的命令,也就是說當(dāng)?shù)玫降恼?qǐng)求后面跟/shutdown的時(shí)候停止服務(wù),默認(rèn)是把服務(wù)設(shè)置為開啟。下面就是進(jìn)入main函數(shù)了,首先實(shí)例化一個(gè)HttpServer類,然后就是通過await方法等待客戶端發(fā)來的請(qǐng)求。如果客戶端輸入的URL不是http://localhost:8080/SHUTDOWN則表示不停止服務(wù)器,然后就是繼續(xù)執(zhí)行await方法中的內(nèi)容,在await方法中最重要的就是定義兩個(gè)對(duì)象,一個(gè)是request一個(gè)是response,下面就來說說RequestResponse類。

import java.io.InputStream;
import java.io.IOException;

public class Request {

	private InputStream input;
	private String uri;

	public Request(InputStream input) {
		this.input = input;
	}

	public void parse() {

		StringBuffer request = new StringBuffer(2048);
		int i;
		byte[] buffer = new byte[2048];
		try {
			i = input.read(buffer);
		} catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}
		for (int j = 0; j < i; j++) {
			request.append((char) buffer[j]);
		}
		System.out.print(request.toString());
		uri = parseUri(request.toString());
	}

	private String parseUri(String requestString) {
		int index1, index2;
		index1 = requestString.indexOf(' ');
		if (index1 != -1) {
			index2 = requestString.indexOf(' ', index1 + 1);
			if (index2 > index1)
				return requestString.substring(index1 + 1, index2);
		}
		return null;
	}

	public String getUri() {
		return uri;
	}

}

首先調(diào)用InputStream對(duì)象中的read方法獲取HTTP請(qǐng)求的原始數(shù)據(jù),然后在parseUri方法中獲得uri也就是要請(qǐng)求的靜態(tài)資源。說白了Request類的主要作用就是告訴服務(wù)器用戶要的是什么也就是在http://localhost:8080后面出現(xiàn)的東西。

import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;

public class Response {

	private static final int BUFFER_SIZE = 1024;
	Request request;
	OutputStream output;

	public Response(OutputStream output) {
		this.output = output;
	}

	public void setRequest(Request request) {
		this.request = request;
	}

	public void sendStaticResource() throws IOException {
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try {
			File file = new File(HttpServer.WEB_ROOT, request.getUri());
			if (file.exists()) {
				fis = new FileInputStream(file);
				int ch = fis.read(bytes, 0, BUFFER_SIZE);
				while (ch != -1) {
					output.write(bytes, 0, ch);
					ch = fis.read(bytes, 0, BUFFER_SIZE);
				}
			} else {
				
				String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
						+ "Content-Type: text/html\r\n"
						+ "Content-Length: 23\r\n" + "\r\n"
						+ "<h1>File Not Found</h1>";
				output.write(errorMessage.getBytes());
			}
		} catch (Exception e) {
			
			System.out.println(e.toString());
		} finally {
			if (fis != null)
				fis.close();
		}
	}
}

Response類代表一個(gè)HTTP響應(yīng)。首先Response接收一個(gè)OutputStream對(duì)象,然后通過sendStaticResource方法對(duì)接收的Request進(jìn)行處理,整個(gè)處理過程就是根據(jù)請(qǐng)求在服務(wù)器端進(jìn)行尋找對(duì)應(yīng)靜態(tài)資源的過程。找到所需要的資源后發(fā)送給客戶端然后讓客戶端顯示出來。

運(yùn)行程序

運(yùn)行上面的HttpServer類,然后在瀏覽器的地址欄中鍵入下面的地址:http:localhost:8080/index.jsp,然后你會(huì)在瀏覽器中看到index.jsp頁面。

在控制臺(tái)可以看到類似于下面的HTTP請(qǐng)求

GET /index.jsp HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7 360EE
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3


小結(jié)

上面自己動(dòng)手寫的這個(gè)所謂的服務(wù)器僅僅有三個(gè)類組成,從功能上來說他只能顯示一些靜態(tài)的資源,并不是全部功能。一個(gè)優(yōu)秀的服務(wù)器還有很多細(xì)節(jié)要做,但是出于學(xué)習(xí)的目的大家現(xiàn)在有這些了解就足夠了,后面還會(huì)有對(duì)服務(wù)器的詳細(xì)介紹,敬請(qǐng)期待。

參考資料《How Tomcat Works》

作者:beijiguangyong 發(fā)表于2012-4-9 9:16:49 原文鏈接
閱讀:6583 評(píng)論:79 查看評(píng)論