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 

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

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

  • 相关阅读:
    zoj 3279 线段树 OR 树状数组
    fzu 1962 树状数组 OR 线段树
    hdu 5057 块状链表
    hdu3487 Play with Chain
    bzoj 1588营业额统计(HNOI 2002)
    poj2823 Sliding Window
    poj2828 Buy Tickets
    poj2395 Out of Hay
    poj3667 Hotel
    poj1703 Lost Cows
  • 原文地址:https://www.cnblogs.com/alimayun/p/9080442.html
Copyright © 2011-2022 走看看