3_Tomcat都做了什么

  • 参考了"https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247487938&idx=1&sn=6b195038397f261c401fce11c3e8f0de&chksm=eb5394f4dc241de287ece3aba5c5d8c866ed01bd5fba6d19874d7b15888154c98071f44fe63a&mpshare=1&scene=1&srcid=0218x1u8CCLgfl5TpTFZ9OWV#rd"这篇文章,修改了部分代码,添加了一些自己的理解

  • (1) 提供Socket服务

    Tomcat的启动是Socket服务,只不过它支持HTTP协议而已

    (Tomcat既然是基于Socket,那么是基于BIO or NIO or AIO 呢?)

    (2) 进行请求的分发

    Tomcat可以为多个Web应用提供服务,所以Tomcat可以把URL下发到不同的Web应用

    (3) 把请求和响应封装成 request / response

  • 从头开始手写Tomcat都需要完成哪些步骤

    (1) 一个类用于记录url、Servlet名称\Servlet的class名称三者的对应关系

    真实的tomcat是靠web.xml中的配置做到的

      <servlet>
          <servlet-name>CompressionFilterTestServlet</servlet-name>
          <servlet-class>compressionFilters.CompressionFilterTestServlet</servlet-class>
      </servlet>
    
      ...
      <servlet-mapping>
          <servlet-name>CompressionFilterTestServlet</servlet-name>
          <url-pattern>/CompressionTest</url-pattern>
      </servlet-mapping>
    

    示例

      public class ServletMappingConfig {
    
          public static List<ServletMapping> servletMappingList = new ArrayList<>();
    
          static {
              servletMappingList.add(new ServletMapping("haha", "/haha", "myTomcat.HahaServlet"));
          }
      }
    
      public class ServletMapping {
    
          private String servletName;
          private String url;
          private String clazz;
    
          public ServletMapping(String servletName, String url, String clazz) {
    
              this.servletName = servletName;
              this.url = url;
              this.clazz = clazz;
          }
    
          public String getServletName() {
              return servletName;
          }
    
          public String getUrl() {
              return url;
          }
    
          public String getClazz() {
              return clazz;
          }
      }
    

    (2) 一个类用于解析输入流,从中提取URL、GET/POST

    示例

      public class MyRequest {
    
          private String url;
          private String method;
    
          public MyRequest(InputStream inputStream) throws IOException {
    
              // Step 1: Read request from stream
              String httpRequest = "";
              byte[] httpRequestBytes = new byte[1024];
              int requestLength = inputStream.read(httpRequestBytes);
              if (requestLength > 0) {
                  httpRequest = new String(httpRequestBytes, 0, requestLength);
              }
    
              // Step 2: Parse the request
              /* A typical Http request protocol
                  GET /favicon.ico HTTP/1.1
                  Accept: ./.
                  Accept-Encoding: gzip, deflate
                  User-Agent: Mozilla/5.0 (Windows NT/6.1)
                  Host: localhost:8080
                  Connection: Keep-Alive
              */
    
              String httpHead = httpRequest.split("\n")[0];  //  GET /favicon.ico HTTP/1.1
           
              this.url = httpHead.split("\\s")[1];    // /favicon.ico
              this.method = httpHead.split("\\s")[0];  //  GET
          }
    
          public String getMethod() {
              return method;
          }
    
          public String getUrl() {
              return url;
          }
      }
    

    (3) 一个类用于包装输出流

    示例

      public class MyResponse {
    
          private OutputStream outputStream;
    
          public MyResponse(OutputStream outputStream) {
              this.outputStream = outputStream;
          }
    
          public void write(String content) throws IOException {
    
              /* A typical Http response protocol
                   HTTP/1.1 200 OK
                   Content-Type: text/html
                   <html><body>...</body></html>
              */
    
              String httpResponse = "HTTP/1.1 200 OK\n" + "Content-Type: text/html\n" +
                  "\r\n" + "<html><body>" +
                  content +
                  "</body></html>";
              
              outputStream.write(httpResponse.getBytes());
              outputStream.close();
          }
      }
    

    (4) 和Tomcat类似的, 实现一个具体的Servlet

      public abstract class AbstractServlet {
    
          public abstract void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException;
    
          public abstract void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException;
    
          public void service(MyRequest myRequest, MyResponse myResponse) throws IOException{
    
              if (myRequest.getMethod().toUpperCase().equals("POST")) {
                  doPost(myRequest, myResponse);
              } else if (myRequest.getMethod().toUpperCase().equals("GET")) {
                  doGet(myRequest, myResponse);
              }
          }
      }
    
    
      public class HahaServlet extends AbstractServlet {
          @Override
          public void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException {
              myResponse.write("Get haha");
          }
    
          @Override
          public void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException {
              myResponse.write("Post haha");
          } 
      }
    

    (4) Tomcat的启动类

    1° 读取URL和Servlet class的映射关系

    2° 根据端口号(默认8080)创建一个ServerSocket

    3° 主循环中这个ServerSocket不断accept, 等待请求到来

    4° 创建新线程(或者用线程池),处理当前的请求

      I. 从到来的Socket中取出输入流InputStream和输出流OutputStream
    
      II. 包装输入流和输出流为Request和Response
    
      III. 根据Request中的URL和映射关系找到对应Servlet的class位置
    
      IV. 利用反射机制创建这个Servlet的实例对象
    
      V. 用这个Servlet的实例对象进行操作
    

    示例

      public class MyTomcat {
    
          private final int port;
    
          private final Map<String, String> urlServletMap = new HashMap<>();
    
          public MyTomcat(int port) {
    
              this.port = port;
              initServletMapping();
          }
    
          private void initServletMapping() {
    
              for (ServletMapping servletMapping: ServletMappingConfig.servletMappingList) {
    
                  urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz());
              }
          }
    
          public void start() {
    
              ServerSocket serverSocket = null;
              try {
                  serverSocket = new ServerSocket(this.port);
    
                  while (true) {
                      Socket currentSocket = serverSocket.accept();
    
                      Thread thread = new Thread(new SingleRequestAndResponseRunnable(
                      currentSocket, urlServletMap));
    
                      thread.start();
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  if (serverSocket != null) {
                      try {
                          serverSocket.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
    
          public static void main(String[] args) {
    
              MyTomcat tomcat = new MyTomcat(8080);
    
              tomcat.start();
          }
      }
    
    
      public class SingleRequestAndResponseRunnable implements Runnable {
    
          private final Socket socket;
          private final Map<String, String> urlServletMapping;
    
          public SingleRequestAndResponseRunnable(Socket socket, Map<String, String> urlServletMap) {
    
              this.socket = socket;
              this.urlServletMapping = urlServletMap;
          }
    
          @Override
          public void run() {
    
              try {
                  InputStream inputStream = this.socket.getInputStream();
                  OutputStream outputStream = this.socket.getOutputStream();
    
                  MyRequest myRequest = new MyRequest(inputStream);
                  MyResponse myResponse = new MyResponse(outputStream);
    
                  dispatch(myRequest, myResponse);
    
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
    
                  if (!this.socket.isClosed()) {
                      try {
                          this.socket.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
    
          private void dispatch(MyRequest myRequest, MyResponse myResponse) {
    
              String clazz = this.urlServletMapping.get(myRequest.getUrl());
    
              // Reflection used
              try {
                  Class<AbstractServlet> abstractServletClass
                      = (Class<AbstractServlet>) Class.forName(clazz);
                  AbstractServlet abstractServlet
                      = abstractServletClass.getDeclaredConstructor().newInstance();
    
                  abstractServlet.service(myRequest, myResponse);
    
              } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException
                   | InstantiationException | IllegalAccessException | IOException e) {
                  e.printStackTrace();
              }
          }
      }
    

猜你喜欢

转载自blog.csdn.net/captxb/article/details/87691875