您的位置:首页 > 运维架构 > Tomcat

Tomcat 源码解析 (二)自己写服务器

2013-04-15 18:55 429 查看
首先原谅我上次《How Tomcat Works 1》的粗制滥造, 这次给必要的代码都给上必要注释。

第二章是说明简单的servlet容器是如何工作的。这一章带有2个servlet容器应用,可以处

理静态资源和简单的servlet请求。尤其是你将会学到如何创建request和response对象,然

后把它们传递给被请求的servlet的service方法。在servlet容器里边还有一个servlet,你

可以从一个web浏览器中调用它。

一个基于java Web的服务器, 两个重中之重的类便是java.net.Socket 和 java.net.ServerSocket,

Socket即套接字,为了发送数据到另一台机器, 首先要知道那台机器的IP及套接字端口。

有更深入兴趣的可以继续深入了解Socket用法

上面的Socket代表客户端套接字,那么同样的就应该有服务端的套接字:ServerSocket

我们这边使用的构造方法是 public ServerSocket(int port, int backLog, InetAddress bindingAddress);

port就是端口, backLog为输入连接指示(对连接的请求)的最大队列长度,我们这里设置为1,bindingAddress便是绑定地址,我们这里通过InetAddress.getByName("127.0.0.1")来获取。

另外我们还需要了解一下HTTP协议的基本概念:

一个HTTP请求包括三个组成部分:
 方法—统一资源标识符(URI)—协议/版本
 请求的头部
 主体内容

下面是给一个栗子:

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

其中第一句包含了 method(POST), uri(/examples/default.jsp) 和version(HTTP/1.1)

然后最后一句便是请求主题(Body)

没错, 中间那长长的一坨就是http请求的头部(实际上请求主题可以很长,这边我们简化了)

更详细的可以看这篇文章 http 协议详解

我们要知道,servlet实现服务基本上要有3件事情要做:

1:Servlet;

通过service()方法接受和处理request, response

对于此, 我们创建类HttpServer:

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

public class HttpServer {
// getProperty("user.dir")就是返回当前目录, File.separator就是'\';
// 以博主机子为例, WEB_ROOT的内容就是"D:/workspace/howTomcatWorks/chap1_ASimpleWebServer/webroot"
public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";

// 如果要关闭服务器, 则在浏览器地址栏中输入 http://localhost:8080/SHUTDOWN private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

private boolean shutdown = false;

// 类似Severlet.service(), 现在只是简单的了解下概念:
public void await() {
ServerSocket serverSocket = null;
int port = 8080;
try {
// port就是端口, backLog为输入连接指示(对连接的请求)的最大队列长度,我们这里设置为1,
// bindingAddress便是绑定地址,我们这里通过InetAddress.getByName("127.0.0.1")来获取。
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// loop waiting for a request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
// 先是通过ServerSocket实例 得到 Socket实例
socket = serverSocket.accept();
// 再通过Socket实例来获取InputStream和OutputStream的实例
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object & parse
// 通过input创建request
Request request = new Request(input);
request.parse();
// create Response object
// 通过request创建response
Response response = new Response(output);
response.setRequest(request);
response.sendResource();
// close socket
socket.close();
// check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}

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


2:Request;

包含http头等信息, 是javax.Servlet.http.ServletRequest 接口的实例

package chap1_ASimpleWebServer;
import java.io.IOException;
import java.io.InputStream;

/*
* 在这一章中,我们仅仅关注HTTP请求的第一部分,请求行。请求行从
* 一个方法标记开始,接下去是请求的URI和协议版本,最后是用回车换
* 行符(CRLF)结束。请求行里边的元素是通过一个空格来分隔的。例如,
* 使用GET方法来请求index.html文件的请求行如下所示:
*
* GET /index.html HTTP/1.1
*/
public class Request {
private InputStream input;
private String uri;

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

public void parse() {
// 新建StringBuffer request, 用于接收字节转化的字符
StringBuffer request = new StringBuffer(2048);
int len;
// 新建字节数组buffer, 用于接收套接字的InputStream字节流
byte[] buffer = new byte[2048];
try {
len = input.read(buffer);
} catch (IOException e) {
e.printStackTrace();
len = -1;
}
for (int i = 0; i < len; i++) {
// 这里不要忘了强制转型(byte -> char)
request.append((char)buffer[i]);
}
System.out.println(request.toString());
// 得到URI的值
uri = parseUri(request.toString());
}

// 从HTTP请求行里截取URI, 即 "GET /index.html HTTP/1.1" 中的 "/index.html"
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;
}

// 返回URI
public String getUri() {
return uri;
}
}


3:Response

通过service()处理赋值后,生成对于客户的响应,对应于Request,, 它是javax.Servlet.http.ServletResponse接口的实例

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

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;
}

// 用来发送一个静态资源,例如一个HTML文件。它首先通过传递上一级
// 目录的路径和子路径给File累的构造方法来实例化java.io.File类
public void sendResource() throws IOException {
// 创建byte数组bytes用于接收文件流数据
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
// 根据当前目录和URI获取文件
File file = new File(HttpServer.WEB_ROOT, request.getUri());
if (file.exists()) {
fis = new FileInputStream(file);
int len = fis.read(bytes, 0, BUFFER_SIZE);
while (len != -1) {
// 写到output流中
output.write(bytes, 0, len);
len = fis.read(bytes, 0, BUFFER_SIZE);
}
} else {
// 如果文件不存在,页面提示File Not Found信息(404)
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"ContentType: text/html\r\n" +
"ContentLength: 23\r\n\r\n" +
"<head>File Not Found</head>";
output.write(errorMessage.getBytes());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最后别忘记关闭文件流
if (fis != null) {
fis.close();
}
}
}
}


现在在项目文件夹下建立一个webroot文件夹:



先运行主程序HttpServer, 然后打开浏览器,输入地址:http://localhost:8080/index.html



另外, 控制台结果显示了HTTP信息:



到此为止, 我们已经简单的了解到了一个简单web服务器是如何工作的。这一次仅仅由三个类组

成,所以并不是功能是十分局限的。下一次是真的要将要讨论动态内容的处理过程了

。。。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: