• 我手写的简易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("
    ")[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
    ")      //按照HTTP响应报文的格式写入
    17                     .append("Content-Type:text/html
    ")
    18                     .append("
    ")
    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 }

    运行结果

  • 相关阅读:
    理解js中的原型链,prototype与__proto__的关系
    Zepto源码(2016)——Zepto模块(核心模块)
    MySQL增删改查
    ACM典型试题--古代密码(二)
    ACM典型试题--简单的加密算法(一)
    MySQL图文安装配置
    (c语言)二叉树中序线索(数据结构十七)
    (C语言)二叉树层次遍历(数据结构十六)
    Java连接db2数据库(常用数据库连接五)
    java连接oracle数据库(常用数据库连接四)
  • 原文地址:https://www.cnblogs.com/jyroy/p/10778760.html
Copyright © 2020-2023  润新知