Handwritten a simplified version of Tomcat

      Tomcat is very popular in the market as a Web server, and it is necessary to conduct in-depth research on it. At work, we often package the written code in Tomcat and start it, and then we can happily call the code we wrote in the browser to achieve the corresponding function, so how does Tomcat work?

First, the working principle of Tomcat

      The main function of the startup.bat file that we double-clicked when we started Tomcat is to find catalina.bat and pass parameters to it, and there is such a passage in catalina.bat:

      Bootstrap.class is the entrance to the entire Tomcat. We find this class in the Tomcat source code, which includes the main method we often use:

          

      This class has two functions: 1. Initialize a daemon variable, load the class and the corresponding parameters. 2. Parse the command and execute it.

      The source code will not be repeated too much. We only need to grasp the overall structure here. Interested students can study the source code by themselves. Tomcat's server.xml configuration file can correspond to the location in the architecture diagram, and multiple layers of representation can be configured:

That is, a structure composed of Server->Service->Engine->Host->Context, from the inner layer to the outer layer is:

  • Server: The top-level element of the server Tomcat, it contains everything.
  • Service: A collection of Engine (engine), including the definition of thread pool Executor and connector Connector.
  • Engine (engine): An Engine represents a complete servlet engine, which receives requests from Connector and decides which Host to pass to for processing.
  • Container (container): Host, Context, Engine and Wraper all inherit from the Container interface, and they are all containers.
  • Connector: Connect the Service and the Container, register with a Service, and forward the request from the client to the Container.
  • Host: The virtual host, the so-called "a virtual host" can be simply understood as "a website".
  • Context (context): The Web application, a Context is for a Web application. The Context container directly manages the operation of the servlet, and the servlet will be packaged into a StandardWrapper class to run. Wrapper is responsible for managing the loading, initialization, execution, and resource recycling of a servlet, and it is the lowest-level container.

For example, if there are the following URLs now, the links cut according to "/" will locate the specific processing logic, and each container has a filtering function.

Second, sort out your own Tomcat implementation ideas

      The effect of this article is relatively simple, and it is only for novice reference. When the browser accesses the corresponding address:

The overall idea to achieve the above effects is as follows:

      1. ServerSocket occupies port 8080 and uses a while (true) loop to wait for the user to send a request.

      2. Get the browser's request, parse and return the URL address, and use the I/O input stream to read the corresponding file on the local disk.

      3. Read the file, if there is no response header or HTML body content, it will be written to the browser if it exists.

3. Implement your own Tomcat

Project file structure and pom.xml file:

1. HttpServer core processing class, used to accept user requests, pass HTTP request header information, and close the container:

public class HttpServer {
  // 用于判断是否需要关闭容器
  private boolean shutdown = false;
  
  public void acceptWait() {
    ServerSocket serverSocket = null;
    try {
    	//端口号,最大链接数,ip地址
      serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
        e.printStackTrace();
        System.exit(1); 
    }
    // 等待用户发请求
    while (!shutdown) {
      try {
    	Socket socket = serverSocket.accept();
    	InputStream is = socket.getInputStream();
    	OutputStream  os = socket.getOutputStream();
        // 接受请求参数
        Request request = new Request(is);
        request.parse();
        // 创建用于返回浏览器的对象
        Response response = new Response(os);
        response.setRequest(request);
        response.sendStaticResource();
        //关闭一次请求的socket,因为http请求就是采用短连接的方式
        socket.close();
        //如果请求地址是/shutdown  则关闭容器
        if(null != request){
        	 shutdown = request.getUrL().equals("/shutdown");
        }
      }
      catch (Exception e) {
          e.printStackTrace();
          continue;
      }
    }
  }
  public static void main(String[] args) {
	    HttpServer server = new HttpServer();
	    server.acceptWait();
  }
}

2. Create the Request class, get all the information of the HTTP request header and intercept the URL address to return:

public class Request {
  private InputStream is;
  private String url;

  public Request(InputStream input) {
    this.is = input;
  }
  public void parse() {
    //从socket中读取一个2048长度字符
    StringBuffer request = new StringBuffer(Response.BUFFER_SIZE);
    int i;
    byte[] buffer = new byte[Response.BUFFER_SIZE];
    try {
      i = is.read(buffer);
    }
    catch (IOException e) {
      e.printStackTrace();
      i = -1;
    }
    for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
    }
    //打印读取的socket中的内容
    System.out.print(request.toString());
    url = parseUrL(request.toString());
  }

  private String parseUrL(String requestString) {
    int index1, index2;
    index1 = requestString.indexOf(' ');//看socket获取请求头是否有值
    if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }

  public String getUrL() {
    return url;
  }

}

3. Create a Response class, read the file in response to the request and write it back to the browser

public class Response {
  public static final int BUFFER_SIZE = 2048;
  //浏览器访问D盘的文件
  private static final String WEB_ROOT ="D:";
  private Request request;
  private OutputStream output;

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

  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    try {
    	//拼接本地目录和浏览器端口号后面的目录
      File file = new File(WEB_ROOT, request.getUrL());
      //如果文件存在,且不是个目录
      if (file.exists() && !file.isDirectory()) {
        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);
        }
      }else {
           //文件不存在,返回给浏览器响应提示,这里可以拼接HTML任何元素
    	  String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
          String returnMessage ="HTTP/1.1 404 File Not Found\r\n" +
                  "Content-Type: text/html\r\n" +
                  "Content-Length: "+retMessage.length()+"\r\n" +
                  "\r\n" +
                  retMessage;
        output.write(returnMessage.getBytes());
      }
    }
    catch (Exception e) {
      System.out.println(e.toString() );
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }
}

Fourth, readers can do their own optimization, expansion points

      1. Read the web.xml parsing in the WEB_INF folder, find the corresponding class name through the request name, create an object through the class name, and use reflection to initialize the configuration information, such as welcome page, servlet, servlet-mapping, filter, listener, Boot loading levels, etc.

      2. Abstract Servlet class to transcode the business of processing requests and responses. There will be a lot of requests sent, which means that we should have a lot of Servlets, such as RegisterServlet, LoginServlet, etc. and many other accesses. A method similar to the factory pattern can be used to generate many servlets at any time to meet different functional requests.

      3. Use multithreading technology. The code in this article is an infinite loop, and there can only be one link. In reality, there are often many, many clients sending requests, which can encapsulate the communication of each browser into a thread.

What other extensions can be made and what functions can be implemented, readers can discuss with me in the comments.

My blog will be moved and synchronized to Tencent Cloud + Community, and everyone is invited to join: https://cloud.tencent.com/developer/support-plan

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325984138&siteId=291194637