Tomcat源码分析(一) -- 初识Tomcat

313 阅读3分钟

HttpServer类

public class HttpServer {
  // shutdown command
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

  // the shutdown command received
  private boolean shutdown = false;

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

  public void await() {
    ServerSocket serverSocket = null;
    int port = 8080;
    try {
      //创建一个socket等待连接
      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 {
        //获取socket对象
        socket = serverSocket.accept();
        //获取输入输出流
        input = socket.getInputStream();
        output = socket.getOutputStream();

        // create Request object and parse
        //获取到socket的流转换为Request对象,这里可以看出 http协议 为什么是应用层?
        //http协议实际上就是socket传过来的输入流,理解成字符串?
        //可以这样理解 http协议解析可以看作是把socket的输入流的字符串转换为了一个request对象
        Request request = new Request(input);
        request.parse();

        // create Response object
        //创建一个response对象,这里传入了socket的输出流
        Response response = new Response(output);
        //response对象关联request对象
        response.setRequest(request);

        // check if this is a request for a servlet or a static resource
        // a request for a servlet begins with "/servlet/"
        //这里可以开始解析request对象,然后交给对应的servlet对象处理
        if (request.getUri().startsWith("/servlet/")) {
          ServletProcessor processor = new ServletProcessor();
          processor.process(request, response);
        }
        else {
          //静态文件,这个自己实现
          //思路:从request中获取对应的静态资源,然后读去本地文件的流通过response中的输出流输出到客户端
          StaticResourceProcessor processor = new StaticResourceProcessor();
          processor.process(request, response);
        }

        // Close the socket
        //处理完后,关闭当前的socket
        socket.close();
        //check if the previous URI is a shutdown command
        //如果是停止指令,关闭服务
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
      }
    }
  }
}

Request类

/**
*这里只是简单的解析url,真实的Request对象复杂的多
*/
public class Request implements ServletRequest{
  private InputStream input;
  private String uri;

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

  public String getUri() {
    return uri;
  }

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

  public void parse() {
    // Read a set of characters from the socket
    StringBuffer request = new StringBuffer(2048);
    int i;
    byte[] buffer = new byte[2048];
    try {
      i = input.read(buffer);
    }
    catch (IOException e) {
      e.printStackTrace();
      i = -1;
    }
    for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
    }
    System.out.print(request.toString());
    uri = parseUri(request.toString());
  }
    ....
}

Response 类

//代码很简单
public class Response implements ServletResponse {

  private static final int BUFFER_SIZE = 1024;
  Request request;
  OutputStream output;
  PrintWriter writer;

  public Response(OutputStream output) {
    this.output = output;
  }

  public void setRequest(Request request) {
    this.request = request;
  }

  /* This method is used to serve a static page */

  /**
   * 发送静态资源
   * @throws IOException
   */
  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    try {
      /* request.getUri has been replaced by request.getRequestURI */
      File file = new File(Constants.WEB_ROOT, request.getUri());
      fis = new FileInputStream(file);
      int ch = fis.read(bytes, 0, BUFFER_SIZE);
      while (ch!=-1) {
        output.write(bytes, 0, ch);
        ch = fis.read(bytes, 0, BUFFER_SIZE);
      }
    }
    catch (FileNotFoundException e) {
      String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: 23\r\n" +
        "\r\n" +
        "<h1>File Not Found</h1>";
      output.write(errorMessage.getBytes());
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }
    
    public PrintWriter getWriter() throws IOException {
    // autoflush is true, println() will flush,
    // but print() will not.
    writer = new PrintWriter(output, true);
    return writer;
  }
}

ServletProcessor 类

public class ServletProcessor {
    public void process(Request request, Response response) {
        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        if (servletName.equals("hello")) {
            Class myClass = null;
            try {
                //通过反射加载servlet
                myClass = getClass().getClassLoader().loadClass(HelloServlet.class.getName());
            } catch (ClassNotFoundException e) {
                System.out.println(e.toString());
            }
            Servlet servlet = null;
            try {
                //通过反射实例化servlet
                servlet = (Servlet) myClass.newInstance();
                //调用servlet的service方法
                servlet.service((ServletRequest) request, (ServletResponse) response);
            } catch (Exception e) {
                System.out.println(e.toString());
            } catch (Throwable e) {
                System.out.println(e.toString());
            }
        }
    }
}

HelloServlet 类

public class HelloServlet implements Servlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    //这里是核心
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        //直接调用response的输出流输出
        PrintWriter writer = res.getWriter();
        writer.println("hello world");
        res.flushBuffer();
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

小结

  • 通过上面的代码能够看出tomcat实际就是一个ServerSocket对象等待连接
  • 连接上以后获取客户端的socket对象
  • 从客户端socket对象中获取输入输出流并解析成对应的request和response对象
  • 根据request的url来查找对应的servlet对象
  • servlet是通过反射创建的,servlet的核心方法是service方法
  • 最后通过response中的输出流返回数据给客户端
  • 关闭socket

tomcat启动图