手写简单版tomcat

1.tomcat简介

Tomcat 是由 Apache 软件基金会属下 Jakarta 项目开发的 Servlet 容器,Tomcat 本身也内含了 HTTP 服务器,因此也可以视作单独的 Web 服务器。但是,不能将 Tomcat 和 Apache HTTP 服务器混淆,Apache HTTP 服务器是用 C 语言实现的 HTTPWeb 服务器;这两个 HTTP web server 不是捆绑在一起的。Apache Tomcat 包含了配置管理工具,也可以通过编辑 XML 格式的配置文件来进行配置。

2.tomcat运行流程

1.用户通过浏览器访问服务器,发送一个请求
2.服务器获得请求并且解析其中的请求头信息(http协议规范)
3.获得资源地址
4.根据资源不同做出不同的处理方式(静态或者是动态)
5.将资源通过通道返回给浏览器,浏览器接收后渲染视图

3.tomcat类代码

这个类运行后执行main方法,然后一直等待请求,当浏览器发送一个请求后,会根据请求地址选择servlet,所以首先需要提供一个socket服务
注意:建立了soket后,必须要执行close方法,否则网页发送请求后会一直停留在等待服务器响应阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class MyTomcat {

private int port = 8080;
private ServerSocket server;

private Map<String,Class<Servlet>> stringClassMap = new HashMap<String, Class<Servlet>>();

public MyTomcat() {
}

public static void main(String[] args) {
MyTomCat myTomCat = new MyTomCat();
myTomCat.start();
}

private void start() {
initServlet();
try {
server = new ServerSocket(port);
System.out.println("Tomcat启动,端口:" + port);
while (true) {
Socket socket = server.accept();
process(socket);
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}

private void initServlet() {
for (ServletConfig servletConfig : ServletMapping.getServletConfigs()){
try {
stringClassMap.put(servletConfig.getUrlMapping(), (Class<Servlet>) Class.forName(servletConfig.getClazz()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

private void process(Socket client){

try {
OutputStream outputStream = client.getOutputStream();
InputStream inputStream = client.getInputStream();
MyRequest myRequest = new MyRequest(inputStream);
MyResponse myResponse = new MyResponse(outputStream);
if (stringClassMap.containsKey(myRequest.getUrl())) {
dispatch(myRequest,myResponse);;
}else {
myResponse.write("404-NOT FOUND");
}
} catch (Exception e) {
e.printStackTrace();
}

}

public void dispatch(MyRequest myRequest , MyResponse myResponse) throws IOException {
Class<Servlet> servletClass = stringClassMap.get(myRequest.getUrl());
if (servletClass!=null){
Servlet servlet = null;
try {
servlet = servletClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
servlet.service(myRequest,myResponse);
}else{
myResponse.write("404 not found page");
}
}
}

4.工具类HttpUtil代码

HttpUtil这个类作用是当请求头处理后,根据状态码返回相应的提示信息
如果想要其他参数可以百度响应头参数(如中文显示在content-type后加: charset=utf-8)

1
2
3
4
5
6
7
8
9
10
public class HttpUtil {
public static String getHttpResponseContext(int code,String content,String errorMsg){
if(code == 200){
return "Http/1.1 200 ok \n"+"Content-Type: text/html \n"+"\r\n"+content;
}else if(code == 500){
return "Http/1.1 500 Internal Error="+errorMsg+"\n"+"Content-Type: text/html \n"+"\r\n";
}
return "Http/1.1 404 Not Found \n"+"Content-Type: text/html \n"+"\r\n";
}
}

5.封装的MyRequest类

将请求头信息处理的步骤封装成一个单独类,方便使用,提高复用性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.io.IOException;
import java.io.InputStream;

public class MyRequest {

private String method;
private String url;

public MyRequest(InputStream is){
int count = 0;
try {
while (count==0){
count = is.available();
}
byte[] buff = new byte[is.available()];
is.read(buff);
extractFileds(new String(buff));
} catch (IOException e) {
e.printStackTrace();
}
}

private void extractFileds(String content){
if(content.equals("")){
System.out.println("empty");
}else{
String firstLine = content.split("\\n")[0];
String[] arr = firstLine.split("\\s");
setUrl(arr[1]);
setMethod(arr[0]);
}
}

public String getMethod() {
return method;
}

public String getUrl() {
return url;
}

public void setMethod(String method) {
this.method = method;
}

public void setUrl(String url) {
this.url = url;
}
}

5.封装的MyResponse类

将返回内容的步骤封装成一个单独类,方便使用,提高复用性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.IOException;
import java.io.OutputStream;

public class MyResponse {

private OutputStream os;

public MyResponse(OutputStream os){
this.os = os;
}

public void writeHtml(String content) throws IOException {
os.write(content.getBytes());
}

/**
public void writeHtml(String path){
String resoucePath = FileUtil.getResoucePath(path);
File file = new File(resoucePath);
if(file.exists()){
FileUtil.writeFile(file.outputStream);
}else{
HttpUtil.getHttpResponseContext(404,"","");
}
}**/
}

注释的方法是返回一个网页文件的功能,FileUtil类是封装了处理文件的操作

6.servlet的编写

tomcat既然是一个servlet的容器,那么servlet的编写肯定是必不可少的
首先是抽象类Servlet,自己编写的servlet要继承这个抽象类

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Servlet {
public abstract void doGet(MyRequest request, MyResponse response);
public abstract void doPost(MyRequest request, MyResponse response);

public void service(MyRequest request, MyResponse response){
if(request.getMethod()=="Get"){
doGet(request,response);
}else{
doPost(request,response);
}
}
}

然后写自己建立的servlet做相应的需求处理

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.IOException;

public class MyServlet extends Servlet{
public void doGet(MyRequest request, MyResponse response) throws IOException {
String content = "<h1>我是单身狗</h1>";
response.write(content);
}

public void doPost(MyRequest request, MyResponse response) throws IOException {
String content = "<h1>我是单身狗</h1>";
response.write(content);
}
}

写一个ServletConfig类,作用是模拟tomcat里面的web.xml文件
name是servlet的名字,clazz是文件地址,urlMapping是一个映射地址,就是浏览器输入的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.util.Map;

public class ServletConfig {
private String name;
private String urlMapping;
private String clazz;

public ServletConfig(String name, String urlMapping, String clazz) {
this.name = name;
this.urlMapping = urlMapping;
this.clazz = clazz;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getUrlMapping() {
return urlMapping;
}

public void setUrlMapping(String urlMapping) {
this.urlMapping = urlMapping;
}

public String getClazz() {
return clazz;
}

public void setClazz(String clazz) {
this.clazz = clazz;
}
}

写一个ServletMapping类,作用是保存已创建的servlet,后面找servlet就在这个类里面找,然后创建servlet实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.ArrayList;
import java.util.List;

public class ServletMapping {
private static List<ServletConfig> servletConfigs = new ArrayList<ServletConfig>();

static {
servletConfigs.add(new ServletConfig("MyServlet","/index","MyServlet"));
}

public static List<ServletConfig> getServletConfigs() {
return servletConfigs;
}
}