《HowTomcatWork》笔记总结(一)(上)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 《HowTomcatWork》笔记总结(一)(上)

前言


这一篇是howTomcatWork的书籍笔记内容。下面是根据书中的内容做的一部分个人笔记。


书籍地址:


链接:pan.baidu.com/s/1jazo55sA…提取码:lp96 --来自百度网盘超级会员V6的分享


个人评价


没啥好说的,tomcat作者写的书,看了下海淘居然要500多确实吓到了。虽然代码使用的是tomcat5的版本,但是可以基本理解tomcat的内部工作机制。也可以看到作者是如何用一个十多行的肉鸡服务器代码不断升级成为现在的tomcat的模样。

本文是个人根据看书记录的一些笔记,中间逻辑不一定连贯,因为有些内容过于基础没有记录的价值,所以挑了一些个人关注的点。


一个最简单的Servlet是如何工作的


  1. 创建Request 对象,并且解析HTTP的请求信息,通过Request对象封装了这些信息的具体细节

具体接口:

javax.servlet.ServletRequest javax.servlet.http.ServletRequest

  1. 创建Response对象,封装了客户需需要的真正数据,封装了响应体的相关信息

javax.servlet.ServletResponsejavax.servlet.http.ServletResponse

  1. servlet的service 方法,根据此方法对于请求头进行解析,同时创建response将数据回传给客户端


tomcat的基本结构



Tomcat把服务器在大体上可以拆分为两部分,一部分叫做容器,另一部分叫做连接器


连接器


作用:接收到每一个 HTTP 请求构造一个 ==request== 和 ==response== 对象


容器


作用:接受连接器的请求根据service方法进行响应给对应的客户端

Tomcat 4 和 和  5的主要区别

  • Tomcat 5 支持 Servlet 2.4 和 JSP 2.0 规范,而 Tomcat 4 支持 Servlet 2.3 和 JSP 1.2。
  • 比起 Tomcat 4,Tomcat 5 有一些更有效率的默认连接器。
  • Tomcat 5 共享一个后台处理线程,而 Tomcat 4 的组件都有属于自己的后台处理线程。 因此,就这一点而言,Tomcat 5 消耗较少的资源。
  • Tomcat 5 并不需要一个映射组件(mapper component)用于查找子组件,因此简化了代码。


构建一个最简单的web程序



构建对象


下方的代码简单阅读即可,无需自己动手实验


HttpServer


用于构建一个服务器,同时建立serverSocket套接字等待链接

  • 调用httprequest.parse()方法

代码如下:


public class HttpServer {
    /**
     * 关闭容器的请求路径
     */
    private static final String SHUTDOWN = "/SHUTDOWN";
    /**
     * 静态资源根路径
     */
    public static final String WEBROOT = System.getProperty("user.dir") + File.separator + "webroot";
    /**
     * 是否关闭标识
     */
    private boolean SHUTDOWN_FLAG = false;
    public static void main(String[] args) {
        new HttpServer().await();
    }
    /**
     * 具体的server 方法,等待socket请求
     */
    public void await() {
        // 默认为8080端口
        int port = 8080;
        String host = "127.0.0.1";
        ServerSocket serverSocket = null;
        try {
            // 创建套接字
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }
        while (!SHUTDOWN_FLAG) {
            try {
                // 等待套接字
                Socket accept = serverSocket.accept();
                HttpRequest httpRequest = new HttpRequest(accept.getInputStream());
                // 处理请求数据
                httpRequest.parse();
                // 创建响应对象,处理响应信息
                HttpResponse httpResponse = new HttpResponse(accept.getOutputStream());
                // 设置静态资源
                httpResponse.setRequest(httpRequest);
                httpResponse.setResource();
                // 关闭的套接字
                accept.close();
                // 判断请求Url是否为 /shutdown
                SHUTDOWN_FLAG = httpRequest.getUri().equalsIgnoreCase(SHUTDOWN);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }
}
复制代码


HttpRequest


以httpserver 的请求inputstream, 解析请求内容,分解请求uri

使用parse()方法解析请求信息,设置到stringbuffer里面

使用parseUri(str)截取请求信息的请求uri,设置到属性里面


public class HttpRequest {
    /**
     * 缓冲区的大小为 1M
     */
    private static final int BUFFER_COUNT = 1024;
    /**
     * 请求路径
     */
    private String uri;
    /**
     * 请求流
     */
    private InputStream inputStream;
    public HttpRequest(InputStream inputStream) {
        this.inputStream = inputStream;
    }
    /**
     * 解析inputstream 对于内容进行解析
     */
    public void parse() {
        // 字符串缓冲池
        StringBuffer stringBuffer = new StringBuffer(BUFFER_COUNT);
        byte[] byteBuffer = new byte[BUFFER_COUNT];
        if (inputStream == null) {
            System.err.println("未找到套接字");
            return;
        }
        int read = 0;
        try {
            // 读取数据到byte数组
            read = inputStream.read(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }
        //读取byte数组的数据进入到stringbuffer
        for (int i = 0; i < read; i++) {
            stringBuffer.append((char)byteBuffer[i]);
        }
        // 打印stringbuffer
        System.err.println(stringBuffer.toString());
        // 获取uri
        uri = parseUri(stringBuffer.toString());
    }
    /**
     * 解析请求,获取请求Uri
     * @param requestString 需要处理的uri
     */
    public String parseUri(String requestString){
        // 建立index1 和 2
        int index1, index2;
        // 获取到第一个空行
        index1 = requestString.indexOf(' ');
        if(index1 != -1){
            // 从index1 开始找
            index2 = requestString.indexOf(' ', index1 + 1);
            if(index2 > index1){
                // 获取请求路径
                return requestString.substring(index1 + 1, index2);
            }
        }
        return null;
    }
    public String getUri() {
        return uri;
    }
}
复制代码


HttpResonse


以 httpserver 的请求outputstream ,获取输入流,将数据返回给客户端

关键方法为setResouces,获取请求Uri,同时使用file 读取文件


public class HttpResponse {
    /**
     * 组合httprequest
     * 根据request返回对应到信息
     */
    private HttpRequest request;
    /**
     * 输出流
     */
    private OutputStream outputStream;
    /**
     * 缓冲区大小
     */
    private static final int BUFFER_COUNT = 1024;
    public HttpResponse(OutputStream outputStream) {
        this.outputStream = outputStream;
    }
    /**
     * 设置静态资源
     */
    public void setResource() throws IOException {
        String errMsg = "404 msg";
        // 字节缓存区
        byte[] bytes = new byte[BUFFER_COUNT];
        // 读取静态资源
        File file = new File(HttpServer.WEBROOT, request.getUri());
        if (file.exists()) {
            // 文件流
            try {
                FileInputStream fileInputStream = new FileInputStream(file);
                // 读取字节
                int ch = fileInputStream.read(bytes, 0, BUFFER_COUNT);
                // 输出
                while (ch != -1) {
                    // 写入流
                    outputStream.write(bytes, 0, ch);
                    // 重复读取数据到缓冲区
                    ch = fileInputStream.read(bytes, 0, BUFFER_COUNT);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } else {
            try {
                outputStream.write(errMsg.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        }
    }
    /**
     * 设置request
     *
     * @param httpRequest
     */
    public void setRequest(HttpRequest httpRequest) {
        this.request = httpRequest;
    }
}
复制代码



相关文章
|
Java 机器人 数据安全/隐私保护
蓝桥杯历届真题题目+解析+代码+答案(2013-2020)(JavaA、B、C组)(C++语言)(Python)
蓝桥杯历届真题题目+解析+代码+答案(2013-2020)(JavaA、B、C组)(C++语言)(Python)
342 0
|
机器学习/深度学习 算法 Java
算法宝典2——Java版本(此系列持续更新,这篇文章目前3道)(有题目的跳转链接)(此份宝典包含了二叉树的算法题)
算法宝典2——Java版本(此系列持续更新,这篇文章目前3道)(有题目的跳转链接)(此份宝典包含了二叉树的算法题)
|
编译器 C语言 C++
C++入门(一)(上)
C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度 的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
102 0
C++入门(一)(上)
|
存储 安全 编译器
|
网络协议 安全 算法
大网进阶安全刷题讲解(带答案)(1)(上)
大网进阶安全刷题讲解(带答案)(1)
88 0
|
网络协议 安全 算法
大网进阶安全刷题讲解(带答案)(1)(下)
大网进阶安全刷题讲解(带答案)(1)
104 0
|
自然语言处理 编译器 Linux
C++入门(一)(下)
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该 形参的缺省值,否则使用指定的实参。
88 0
|
存储 算法
【数据结构与算法】图的概述(内含源码)
【数据结构与算法】图的概述(内含源码)
96 0
|
C++
C++学习——坚持(二)(2)
C++学习——坚持(二)(2)
157 0
C++学习——坚持(二)(2)
|
算法 编译器 C++
C++学习——坚持(二)(1)
C++学习——坚持(二)
120 0
C++学习——坚持(二)(1)