zoukankan      html  css  js  c++  java
  • 简单实现"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 + "
    ");
                    }
    
                    output.write(stringBuilder.toString().getBytes("utf-8"));
                    output.flush();
                } else {
                    String errorMessage = "HTTP/1.1 404 File Not Found
    " + "Content-Type: text/html
    "
                            + "Content-Length: 23
    " + "
    " + "<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 
    " + "Content-Type: text/html
    "
                    + "Content-Length: 63
    " + "
    " + "请求动态资源:我不管,我最帅,我是你们的小可爱";
            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
    " + "Content-Type: text/html
    "
                    + "Content-Length: 21
    " + "
    " + "请求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:workspaceTomcatProwebroot omcatProTest.txt

    项目路径:E:workspaceTomcatPro

    文件内容:

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

    测试结果:

     

    4、错误的静态请求url

     

    5、正确动态请求url

     

     

    6、错误动态请求url

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

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

    如果想给予我更多的鼓励,求打

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【阿里马云】!

  • 相关阅读:
    CodeForces 710CMagic Odd Square(经典-奇数个奇数&偶数个偶数)
    CodeForces 710A King Moves(水题-越界问题)
    CodeForces 701C They Are Everywhere (滑动窗口)
    CodeForces 701B Cells Not Under Attack
    [补档]happiness
    [补档]王者之剑
    [补档]士兵占领
    [补档]搭配飞行员
    [补档]暑假集训D6总结
    [补档][Lydsy2017年4月月赛]抵制克苏恩
  • 原文地址:https://www.cnblogs.com/alimayun/p/9080442.html
Copyright © 2011-2022 走看看