简单实现"Tomcat"

Posted 阿里-马云的学习笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单实现"Tomcat"相关的知识,希望对你有一定的参考价值。

Tomcat的主要功能就是接收客户端的Http请求,然后将请求分发,并且将请求封装,最后返回资源给到客户端。话不多说,开干。

 

一、实现设计图

 

                                           (禁止盗图,除非先转支付宝!!!)

 

二、代码

1、工程结构目录图

新建java project 即可,目录图如下:

 

2、工程描述

 a、dispatcher是用来处理请求信息的,并且分发到静态处理器还是动态处理器。

 b、HttpEntity则是实体类,包括request以及response。

c、process包括StaticServletProcess以及DynamicServletProcessor,去构造实际处理的servlet对象以及构建静态资源路径等等。

d、server则是模拟的整个http容器。

e、servlet下则存放实际处理的servlet

 

三、代码

1、server(http容器类)

package com.ty.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import com.ty.dispatcher.ProcessDispatcher;

/**
 * @author Taoyong
 * @date 2018年5月23日
 * 天下没有难敲的代码!
 */

/*
 * 此类主要是为了模拟一个http服务器,并且简单起便,使用main方法来启动整个http容器
 */
public class Server {

    private static boolean shutDown = false;
    
    /*
     * 为了简单实现,这里直接使用main方法启动
     * 
     */
    public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.startServer();
    }

    public void startServer() throws IOException {
        ServerSocket serverSocket = null;
        try {
            /*
             * 创建一个serverSocket,并且绑定本地端口
             */
            serverSocket = new ServerSocket(8088);        
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        
        while(!shutDown) {
            Socket socket = null;
            InputStream input = null;
            OutputStream output = null;
            try {
                /*
                 * socket是对TCP/IP的封装,提供给程序员对TCP/IP传输层进行操作
                 * 服务端监听客户端是否有请求过来
                 */                
                socket = serverSocket.accept();                
                
                //从socket中获取客户端传输内容
                input = socket.getInputStream();
                
                //从socket中获取传输给客户端的输出流对象
                output = socket.getOutputStream();
                
                ProcessDispatcher processDispatcher = new ProcessDispatcher(input, output);
                processDispatcher.service();
                
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(socket != null) {
                    socket.close();
                }
                
                if(input != null) {
                    input.close();
                }
                
                if(output != null) {
                    output.close();
                }
            }
        }
    }
}

 

 2、ProcessDispatcher(分发静态请求or动态请求)

package com.ty.dispatcher;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.ty.httpEntity.Request;
import com.ty.httpEntity.Response;
import com.ty.process.Processor;
import com.ty.process.impl.DynamicServletProcessor;
import com.ty.process.impl.StaticServletProcessor;

/**
 * @author Taoyong
 * @date 2018年5月23日
 * 天下没有难敲的代码!
 */

/*
 * 此类作为一个请求处理分发器,根据客户端请求url的类型(包括静态资源以及动态资源请求),去初始化不同的processor
 * 并且在此时初始化request以及response对象,request、response、processor构成整个处理逻辑与数据传输
 */
public class ProcessDispatcher {

    private Request request;
    
    private Response response;
    
    private Processor processor;
    
    public ProcessDispatcher(InputStream input, OutputStream output) {
        init(input, output);
    }

    /**
     * 根据input以及output对象对Request、Response以及processor进行初始化
     */
    private void init(InputStream input, OutputStream output) {
        Request request = new Request(input);
        request.resolve();        
        this.request = request;

        Response response = new Response(output);
        this.response = response;
        
        initProcessor(request);
    }
    
    private void initProcessor(Request request) {
        if(request.getUrl() != null && -1 != request.getUrl().indexOf("dynamic")) {
            DynamicServletProcessor dynamicProcessor = new DynamicServletProcessor();
            this.processor = dynamicProcessor;
            return ;
        }
        
        if(request.getUrl() != null && -1 != request.getUrl().indexOf("static")) {
            StaticServletProcessor staticProcessor = new StaticServletProcessor();
            this.processor = staticProcessor;
            return ;
        }
        
        return ;
    }

    /**
     * processor的主要作用就是分发请求到底是由动态servlet处理,还是直接找静态资源
     * @throws IOException 
     */
    public void service() throws IOException {
        if(processor == null) {
            return ;
        }
        
        //根据url获取处理的servletName
        request.setServletName(resolveServletName(request.getUrl()));
        processor.process(request, response);
    }

    private String resolveServletName(String url) {
        String[] arr = url.split("/");
        for(String s: arr) {
            if(s.equals("") || s.equals("dynamic") || s.equals("static")) {
                continue;
            }
            
            return s;
        }
        return "";
    }
}

 

3、Request

package com.ty.httpEntity;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author Taoyong
 * @date 2018年5月23日
 * 天下没有难敲的代码!
 */

/*
 * 此类主要是对请求的封装
 */
public class Request {

    private InputStream input;
    
    private String url;    

    private String servletName;
    
    public String getServletName() {
        return servletName;
    }

    public void setServletName(String servletName) {
        this.servletName = servletName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Request(InputStream input) {
        this.input = input;
    }
    
    /*
     * 此方法主要包括两个作用
     *         1、获取客户端请求的相关数据
     *         2、解析出请求url,并且根据具体url去找到对应的processor
     */
    public void resolve() {
        String requestStr = resolveInput(input);
        if(requestStr == null || requestStr.length() == 0) {
            return;
        }
        resolveURL(requestStr);
    }
    
    /*
     * 从客户端获取请求相关数据,数据样式如下:
     * GET /static/tomcatProTest HTTP/1.1
     * Host: localhost:8088
     * Connection: keep-alive
     * Upgrade-Insecure-Requests: 1
     * User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/55.0.2883.87 Safari/537.36
     * Accept: text/html,application/xhtml+xml,application/xml;
     * Accept-Encoding: gzip, deflate, sdch, br
     * Accept-Language: zh-CN,zh;
     */
    private String resolveInput(InputStream input) {        
        StringBuilder stringBuilder = new StringBuilder();
        String data = null;
        try {
            /*
             * 关于BufferedReader有个注意项,当读取到的数据为"",会存在阻塞现象,因此这里判断长度是否为0
             */
            BufferedReader reader = new BufferedReader(new InputStreamReader(input, "utf-8"));    
            while((data = reader.readLine()) != null && data.length() != 0) {
                stringBuilder.append(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }
    
    /*
     * HTTP请求头第一行一般为GET /dynamic/helloServlet HTTP/1.1
     * 由于实现的只是简单的Tomcat功能,不实现解析页面传参,另外对于请求url定义如下:
     *         1、动态请求 /dynamic/对应的servlet名称
     *        2、静态资源请求 /static/静态资源名称
     * resolveURL方法是用于切割出/dynamic/helloServlet
     */
    private void resolveURL(String requestStr) {
        int firstSpaceIndex = requestStr.indexOf(" ");
        int secondSpaceIndex = requestStr.indexOf(" ", firstSpaceIndex + 1);
        String url = requestStr.substring(firstSpaceIndex + 1, secondSpaceIndex);
        setUrl(url);
    }
}

 

4、Response

package com.ty.httpEntity;

import java.io.OutputStream;

/**
 * @author Taoyong
 * @date 2018年5月23日
 * 天下没有难敲的代码!
 */
public class Response {

    private OutputStream output;
    
    public OutputStream getOutput() {
        return output;
    }

    public void setOutput(OutputStream output) {
        this.output = output;
    }

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

}

 

5、DynamicServletProcessor(动态请求处理器)

package com.ty.process.impl;

import com.ty.httpEntity.Request;
import com.ty.httpEntity.Response;
import com.ty.process.Processor;
import com.ty.servlet.Servlet;
import com.ty.servlet.impl.ErrorServlet;

/**
 * @author Taoyong
 * @date 2018年5月23日
 * 天下没有难敲的代码!
 */
public class DynamicServletProcessor implements Processor {

    /*
     * 所有相关处理的servlet都放在这个包下
     */
    private static final String PACKAGE_NAME = "com.ty.servlet.impl.";
    
    @Override
    public void process(Request request, Response response) {
        String servletName = request.getServletName();
        Class<?> clazz = null;
        Servlet servlet = null;
        try {
            clazz = Class.forName(PACKAGE_NAME + servletName);
            servlet = (Servlet) clazz.newInstance();
        } catch (Exception e) {            
            servlet = new ErrorServlet();
        } 
        servlet.process(request, response);
    }
}

 

 6、StaticServletProcessor(静态请求处理器)

package com.ty.process.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import com.ty.httpEntity.Request;
import com.ty.httpEntity.Response;
import com.ty.process.Processor;

/**
 * @author Taoyong
 * @date 2018年5月23日 天下没有难敲的代码!
 */
public class StaticServletProcessor implements Processor {

    @Override
    public void process(Request request, Response response) {
        //为了省事,默认都是取txt文件
        File file = new File(Processor.prefix, request.getServletName() + ".txt");
        FileInputStream fis = null;
        BufferedReader reader = null;
        String data = null;
        StringBuilder stringBuilder = new StringBuilder();
        OutputStream output = response.getOutput();
        try {
            if (file.exists()) {
                fis = new FileInputStream(file);
                reader = new BufferedReader(new InputStreamReader(fis, "utf-8"));
                /*
                 * 由于返回数据要符合http响应头的格式,所以会存在一个空行,因此这里不能判断data.length != 0的条件
                 */
                while((data = reader.readLine()) != null) {
                    stringBuilder.append(data + "\\r\\n");
                }

                output.write(stringBuilder.toString().getBytes("utf-8"));
                output.flush();
            } else {
                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());
                output.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

 

7、TestServlet(具体的servlet处理类,用于返回客户端数据)

package com.ty.servlet.impl;

import java.io.OutputStream;

import com.ty.httpEntity.Request;
import com.ty.httpEntity.Response;
import com.ty.servlet.Servlet;

/**
 * @author Taoyong
 * @date 2018年5月24日
 * 天下没有难敲的代码!
 */
public class TestServlet implements Servlet {

    @Override
    public void process(Request request, Response response) {
        OutputStream output = response.getOutput();
        String succMessage = "HTTP/1.1 200 \\r\\n" + "Content-Type: text/html\\r\\n"
                + "Content-Length: 63\\r\\n" + "\\r\\n" + "请求动态资源:我不管,我最帅,我是你们的小可爱";
        try {
            output.write(succMessage.getBytes("utf-8"));
            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

 

 8、ErrorServlet(对于错误的请求url,要求是动态请求,统一的处理servlet)

 

package com.ty.servlet.impl;

import java.io.OutputStream;

import com.ty.httpEntity.Request;
import com.ty.httpEntity.Response;
import com.ty.servlet.Servlet;

/**
 * @author Taoyong
 * @date 2018年5月24日
 * 天下没有难敲的代码!
 */
public class ErrorServlet implements Servlet {

    @Override
    public void process(Request request, Response response) {
        OutputStream output = response.getOutput();
        String succMessage = "HTTP/1.1 404 File Not Found\\r\\n" + "Content-Type: text/html\\r\\n"
                + "Content-Length: 21\\r\\n" + "\\r\\n" + "请求url出现错误";
        try {
            output.write(succMessage.getBytes("utf-8"));
            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

}

 

 四、测试结果

 1、请求动态or静态

由于实现的只是简单的Tomcat功能,不实现解析页面传参,另外对于请求url定义如下:
a、动态请求url样例: /dynamic/对应的servlet名称
b、静态请求url样例: /static/静态资源名称

 

2、运行server的main方法

首先启动server容器,启动ok后,监听客户端的请求。

 

3、正确静态url请求本机txt文件

文件路径为:E:\\workspace\\TomcatPro\\webroot\\tomcatProTest.txt

项目路径:E:\\workspace\\TomcatPro

文件内容:

注意点:文件格式需符合http响应头格式,content-length要与具体内容长度对应,还要注意空行一定要有,否则会报错!!!

测试结果:

 

4、错误的静态请求url

 

5、正确动态请求url

 

 

6、错误动态请求url

 

 所有测试结果都ok,洗澡睡觉!!!

所有源码已经上传至github上:https://github.com/ali-mayun/tomcat 

以上是关于简单实现"Tomcat"的主要内容,如果未能解决你的问题,请参考以下文章

[原创][Synth 8-2543] port connections cannot be mixed ordered and named ["*_Top.v":1151](代码片

nginx: [error] open() "/var/run/nginx/nginx.pid" failed (2: No such file or directory)(代码片

ie 下面 js动态设置 iframe src 不显示,一片空白

Apache Tomcat/Jboss远程代码执行漏洞 怎样解决

多线程与进程

startup.bat运行闪推 加了pause看到的是下列代码 Using CATALINA_BASE: "D:\tomcat"