我手写的简易tomcat

前述

  自己手写的简易的tomcat,实现了tomcat的基本响应功能,项目代码已经上传到我的Github,刚刚开始学习这里,当前还存在很多问题

项目简述及代码

  当我们的Web运行的时候,从浏览器发出的请求,必然首先到达tomcat中,之后由tomcat进行处理,由此要考虑tomcat要进行哪些处理,首先便是提供Socket服务,之后对于请求进行分发,把请求和产生的响应封装成request和response

  (1)提供Socket服务

  (2)封装请求/响应对象

  (3)将不同的请求映射到具体的Servlet处理

处理请求

  我们首先考虑的,是客户端发送来请求时,我们应该怎么去识别它,这里涉及到的就是HTTP请求协议的部分,我直接那Github页面的HTTP请求协议做例子来说,如下图

  我们可以看到,在Request头的首行,由 GET  /jyroy  HTTP/1.1 三部分构成,而这三部分分别的含义是 请求方法  请求路径  请求协议及其对应版本号

  我们在拿到Resquest请求之后根据上面的分析,拿到相应的信息就可以进行后续的处理了。

 

 1 package myTomcat;
 2 
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 
 6 /**
 7  * @author jyroy
 8  *
 9  */
10 public class MyRequest {
11     
12     //请求路径
13     private String url;
14     //请求方法
15     private String method;
16     
17     //读取输入字节流,封装成字符串格式的请求内容
18     public MyRequest(InputStream inputStream) throws IOException{
19         String httpRequest = "";
20         
21         byte[] httpRequestBytes = new byte[1024];
22         
23         int length = 0;
24         
25         if((length = inputStream.read(httpRequestBytes)) > 0) {
26             httpRequest = new String(httpRequestBytes, 0, length);
27         }
28         //HTTP请求协议:首行的内容依次为:请求方法、请求路径以及请求协议及其对应版本号
29         //                           GET    /index        HTTP/1.1
30         String httpHead = httpRequest.split("\n")[0];    //取出HTTP请求协议的首行
31         System.out.println(httpHead);
32         method = httpHead.split("\\s")[0];     //按照空格进行分割,第一个是请求的方法
33         url = httpHead.split("\\s")[1];      //按照空格进行分割,第二个是请求的路径
34         System.out.println(this.toString());
35     }
36 
37     public String getUrl() {
38         return url;
39     }
40 
41     public void setUrl(String url) {
42         this.url = url;
43     }
44 
45     public String getMethod() {
46         return method;
47     }
48 
49     public void setMethod(String method) {
50         this.method = method;
51     }
52 
53     @Override
54     public String toString() {
55         return "MyRequest [url=" + url + ", method=" + method + "]";
56     }
57     
58     
59 }

 

处理响应

  考虑完接受请求之后,我们再来考虑一下怎么来做出我们的响应,浏览器才能识别,这里要涉及到的就是HTTP响应报文的内容,我的思路是,利用字符串拼接出Response报文,再将String转换为字节流就可以了。

  我们也是来看一下Github的Response报文的格式,如下图

  这么多的响应头,其实不是全部需要的,我们只需要写入一些基本的必须响应头信息,例如 请求协议及其对应版本号  响应号 响应状态 和 Cotent-type 等,如下

  最后只要转化字节流就可以

  

 1 package myTomcat;
 2 
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5 
 6 public class MyResponse {
 7     private OutputStream outputStream;
 8     
 9     public MyResponse(OutputStream outputStream) {
10         this.outputStream = outputStream;
11     }
12     
13     //将文本转换为字节流
14     public void write(String content) throws IOException{
15         StringBuffer httpResponse = new StringBuffer();
16         httpResponse.append("HTTP/1.1 200 OK\n")      //按照HTTP响应报文的格式写入
17                     .append("Content-Type:text/html\n")
18                     .append("\r\n")
19                     .append("<html><head><link rel=\"icon\" href=\"data:;base64,=\"></head><body>")
20                     .append(content)          //将页面内容写入
21                     .append("</body></html>");
22         outputStream.write(httpResponse.toString().getBytes());      //将文本转为字节流
23         outputStream.close();
24     }
25     
26 }

 

Servlet请求处理基类

  当我们的请求和响应都已经准备好之后,接下来考虑servlet请求处理的部分,tomcat本身是一种满足servlet规范的容器,我们需要识别接收到的请求之后并做出响应,就涉及到了 doGet  doPost  service 三个方法 

 1 package myTomcat;
 2 
 3 /**
 4  * @author jyroy
 5  * 提供API:doGet doPost service 方法
 6  */
 7 public abstract class MyServlet {
 8     
 9     public void service(MyRequest myRequest, MyResponse myResponse) {
10         if(myRequest.getMethod().equalsIgnoreCase("POST")) {
11             doPost(myRequest, myResponse);
12         }else if(myRequest.getMethod().equalsIgnoreCase("GET")) {
13             doGet(myRequest, myResponse);
14         }
15     }
16     
17     public void doGet(MyRequest myRequest, MyResponse myResponse) {
18         
19     }
20     
21     public void doPost(MyRequest myRequest, MyResponse myResponse) {
22         
23     }
24     
25 }

 

 

 Servlet配置

  考虑完上述问题之后,下一步需要的是分配url给哪一个servlet来处理,首先需要的就是一个反应映射关系的类

  

 1 package myTomcat;
 2 
 3 public class ServletMapping {
 4     private String servletName;
 5     private String url;
 6     private String clazz;
 7     
 8     public ServletMapping(String servletName, String url, String clazz) {
 9         super();
10         this.servletName = servletName;
11         this.url = url;
12         this.clazz = clazz;
13     }
14 
15     public String getServletName() {
16         return servletName;
17     }
18 
19     public void setServeletName(String servletName) {
20         this.servletName = servletName;
21     }
22 
23     public String getUrl() {
24         return url;
25     }
26 
27     public void setUrl(String url) {
28         this.url = url;
29     }
30 
31     public String getClazz() {
32         return clazz;
33     }
34 
35     public void setClazz(String clazz) {
36         this.clazz = clazz;
37     }
38 }

 

  以及相关配置文件

 1 package myTomcat;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * @author jyroy
 8  * 
 9  */
10 public class ServletMappingConfig {
11     public static List<ServletMapping> servletMappingList = new ArrayList<>();
12     
13      static {
14         servletMappingList.add(new ServletMapping("index", "/index", "myTomcat.test.IndexServlet"));
15         servletMappingList.add(new ServletMapping("myblog", "/myblog", "myTomcat.test.MyBlog"));
16      }
17 }

 

 核心类

  最终,我们准备好基类后,需要的就是实现开始提到的整个处理流程

 

  (1)提供Socket服务

 

  (2)封装请求/响应对象

 

  (3)将不同的请求映射到具体的Servlet处理

 

  这里重点说的是,要利用 ServerSocket 通过服务器上的端口通信 以及 accpt方法一直等待客户端的请求

  具体逻辑在代码中注释

 1 package myTomcat;
 2 
 3 import java.io.InputStream;
 4 import java.io.OutputStream;
 5 import java.net.ServerSocket;
 6 import java.util.HashMap;
 7 import java.util.Map;
 8 import java.net.Socket;
 9 
10 /**
11  * @author jyroy
12  * Tomcat的处理流程:把URL对应处理的Servlet关系形成,解析HTTP协议,封装请求/响应对象,
13  * 利用反射实例化具体的Servlet进行处理即可。
14  */
15 public class MyTomcat {
16     private Integer port = 8080;     //定义8080端口
17     
18     private Map<String, String> urlServletMapping = new HashMap<>();    //存储url和对应的类
19 
20     public MyTomcat(Integer port) {
21         super();
22         this.port = port;
23     }
24     
25     @SuppressWarnings("resource")
26     public void start() {
27         initServletMapping();
28         
29             try {
30                 ServerSocket serverSocket = null;     //实例化一个 ServerSocket 对象,表示通过服务器上的端口通信
31                 serverSocket = new ServerSocket(port);   
32                 System.out.println("MyTomcat is starting...");
33                 while(true) {
34                     Socket socket = serverSocket.accept();     //服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口 
35                     InputStream inputStream = socket.getInputStream();
36                     OutputStream outputStream = socket.getOutputStream();
37                     
38                     MyRequest myRequest = new MyRequest(inputStream);
39                     MyResponse myResponse = new MyResponse (outputStream);
40                     
41                     dispatch(myRequest, myResponse);
42             
43                     socket.close();                
44                 }
45             }catch(Exception e) {
46                 e.printStackTrace();
47             }
48         
49 //        }finally {
50 //            if(serverSocket != null) {
51 //                try {
52 //                    serverSocket.close();
53 //                }catch(Exception e){
54 //                    e.printStackTrace();
55 //                }
56 //            }
57 //        }
58     }
59     
60     //初始化映射
61     public void initServletMapping() {
62         for(ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
63             urlServletMapping.put(servletMapping.getUrl(), servletMapping.getClazz());
64         }
65     }
66     
67     //分发请求
68     @SuppressWarnings("unchecked")
69     public void dispatch(MyRequest myRequest, MyResponse myResponse) {
70         String clazz = urlServletMapping.get(myRequest.getUrl());
71         
72         try {
73             Class<MyServlet> myServletClass = (Class<MyServlet>)Class.forName(clazz); 
74             MyServlet myservlet = myServletClass.newInstance();
75             myservlet.service(myRequest, myResponse);
76         }catch(ClassNotFoundException e) {
77             e.printStackTrace();
78         }catch(InstantiationException e) {
79             e.printStackTrace();
80         }catch(IllegalAccessException e) {
81             e.printStackTrace();
82         }
83     }
84     
85     public static void main(String[] args) {
86         MyTomcat myTomcat = new MyTomcat(8080);
87         myTomcat.start();
88     }
89     
90 }

 

测试类

 1 package myTomcat.test;
 2 
 3 import java.io.IOException;
 4 
 5 import myTomcat.MyRequest;
 6 import myTomcat.MyResponse;
 7 import myTomcat.MyServlet;
 8 
 9 public class IndexServlet extends MyServlet {
10     @Override
11     public void doGet(MyRequest myRequest, MyResponse myResponse) {
12         try {
13             myResponse.write("Hello, myTomcat");
14         } catch (IOException e) {
15             e.printStackTrace();
16         }
17     }
18     
19     @Override
20     public void doPost(MyRequest myRequest, MyResponse myResponse) {
21         try {
22             myResponse.write("Hello, myTomcat");
23         } catch (IOException e) {
24             e.printStackTrace();
25         }
26     }
27 }
 1 package myTomcat.test;
 2 
 3 import java.io.IOException;
 4 
 5 import myTomcat.MyRequest;
 6 import myTomcat.MyResponse;
 7 import myTomcat.MyServlet;
 8 
 9 public class MyBlog extends MyServlet {
10     @Override
11     public void doGet(MyRequest myRequest, MyResponse myResponse) {
12         try {
13             myResponse.write("Hello, this is my blog");
14         } catch (IOException e) {
15             e.printStackTrace();
16         }
17     }
18     @Override
19     public void doPost(MyRequest myRequest, MyResponse myResponse) {
20         try {
21             myResponse.write("Hello, this is my blog");
22         } catch (IOException e) {
23             e.printStackTrace();
24         }
25     }
26 }

 

运行结果

 

posted @ 2019-04-27 17:11  JYRoy  阅读(3493)  评论(0编辑  收藏  举报