Tomcat学习笔记002-手写Tomcat小demo

概要

时间:2021-07-11
学习内容: 视频-动手实现Tomcat
提取码:jpdu

内容输出:

  1. 学习笔记
  2. 代码demo

1.学习笔记

1)Tomcat底层业务逻辑

在这里插入图片描述

2)测试案例1

主要模拟流程中的5-6流程,即客户端向指定服务端服务器发送请求,获取响应信息的过程!
在这里插入图片描述
测试代码:

package test;

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

public class TestClient001 {

    public static void main(String[] args) {
        Socket socket = null;
        OutputStream os = null;
        InputStream is = null;

        try {
            //    1.建立一个和服务端对应的Socket对象,指明需要连接的服务端的ip和端口号
            socket = new Socket("www.itcast.cn", 80);
            //    2.通过socket获取到指向服务端的输出流对象
            is = socket.getInputStream();
            //    3.通过socket获取到一个输入流对象
            os = socket.getOutputStream();
            //    4.将HTTP协议的请求部分参数发送到服务端
            os.write("GET /subject/about/index.html HTTP/1.1\n".getBytes());
            os.write("HOST:www.itcast.cn\n".getBytes());
            os.write("\n".getBytes());

            //    5.接收来自服务端数据并输出
            int i = is.read();
            while (i != -1) {
                System.out.print((char) i);
                i = is.read();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //    6.释放资源
            closeClient(socket, is, os);
        }
    }

    /**
     * 释放资源
     * @param socket
     * @param is
     * @param os
     */
    private static void closeClient(Socket socket, InputStream is, OutputStream os) {
        if (is != null) {
            try {
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (os != null) {
            try {
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:
在这里插入图片描述

3)测试案例2

主要模拟流程中的2-4流程,即服务端向指定客户端响应请求信息的过程!
在这里插入图片描述
测试代码:

package test;

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

public class TestServer001 {

    public static void main(String[] args) {
        // 浏览器请求地址:http://localhost:8080
        ServerSocket serverSocket = null;
        Socket socket = null;
        OutputStream os = null;

        try {
            // 1.创建ServerSocket对象,监听本机的8080端口号
            int port = 8080;
            serverSocket = new ServerSocket(port);
            while (true) {
                // 2.等待来自客户端的请求和客户端对应的Socket对象
                socket = serverSocket.accept();
                // 3.通过获取到的Socket对象获取到输出流对象
                os = socket.getOutputStream();
                // 4.通过获取到的输出流对象将HTTP协议的相应部分发送到客户端
                // 响应头
                os.write("HTTP/1.1 200 OK\n".getBytes());
                os.write("Content-Type:text/html;charset-utf-8\n".getBytes());
                os.write("Server:Apache-Coyote/1.1\n".getBytes());
                os.write("\n\n".getBytes());
                // 响应体
                StringBuffer buffer = new StringBuffer();
                buffer.append("<html>");
                buffer.append("<head><title>I love you</title></head>");
                buffer.append("<body>");
                buffer.append("<h1>I love you.</h1>");
                buffer.append("<a href=\"#\">I love you too.</a>");
                buffer.append("</body>");
                buffer.append("</html>");
                os.write(buffer.toString().getBytes());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 5.释放资源
            closeServer(socket, os);
        }
    }

    /**
     * 释放资源
     * @param socket
     * @param os
     */
    private static void closeServer(Socket socket, OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:
在这里插入图片描述

4)测试案例3-终极版在这里插入图片描述

完整的业务流程图:
在这里插入图片描述
解析:
1、如果请求的是demo.html类的静态资源,直接从项目中找到对应资源信息,以流的方式返回页面信息
2、如果请求的是AAServlet.class类的动态资源,找到对应的java类,调用其核心方法执行,返回执行结果

demo案例完成结构:
在这里插入图片描述
废话不多说,案例相对简单,我直接上代码吧!

核心代码类

TestServer002.java

package test;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

public class TestServer002 {

    public static String URL = "";
    public static String baseFileUrl = "web/file/";

    public static Map<String, Object> propertiesMap = new HashMap<>();
    static {
        FileInputStream fileInputStream = null;
        Properties properties = new Properties();
        try {
            // 加载配置文件 server.properties
            fileInputStream = new FileInputStream(new File(baseFileUrl + "\\server.properties"));
            properties.load(fileInputStream);
            // 遍历获取key,value值
            Set<Object> objects = properties.keySet();
            // 通过迭代器的方式获取到key
            Iterator<Object> iterator = objects.iterator();
            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                String value = properties.getProperty(key);
                System.out.println(key + ":" + value);
                propertiesMap.put(key, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 浏览器请求地址:http://localhost:8080
        ServerSocket serverSocket = null;
        Socket socket = null;
        OutputStream os = null;
        InputStream is = null;
        int port = 8080;

        try {
            // 1.创建ServerSocket对象,监听本机的8080端口号
            serverSocket = new ServerSocket(port);
            while (true) {
                // 2.等待来自客户端的请求和客户端对应的Socket对象
                socket = serverSocket.accept();
                // 3.通过获取到的Socket对象获取到输出流对象
                os = socket.getOutputStream();
                // 获取输入流信息
                is = socket.getInputStream();
                // 获取请求地址信息
                URL = getRequestUrl(is);
                System.out.println("zwd: " + URL);
                // 判断url是否包含“.”号(即xxx.xx类型字符串,有则表示请求的是静态文件。这里是简单的分类!)
                // 4.通过获取到的输出流对象将HTTP协议的相应部分发送到客户端
                if (URL.contains(".")) {
                    // 输出静态文件信息
                    writeStaticResponse(os);
                } else {
                    // 输出动态请求信息
                    writeDynomicResponse(is, os);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 5.释放资源
            closeServer(socket, os, is);
        }
    }

    private static void writeDynomicResponse(InputStream is, OutputStream os) throws Exception {
        // 响应头部信息
        os.write("HTTP/1.1 200 OK\n".getBytes());
        os.write("Content-Type:text/html;charset-utf-8\n".getBytes());
        os.write("Server:Apache-Coyote/1.1\n".getBytes());
        os.write("\n\n".getBytes());

        if (propertiesMap.containsKey(URL)) {
            String className = propertiesMap.get(URL) == null ? "" : propertiesMap.get(URL) + "";
            try {
                // 通过反射创建对象
                Class<Servlet> clazz = (Class<Servlet>) Class.forName(className);
                Servlet servlet = clazz.newInstance();
                // 调用其方法
                servlet.init();
                servlet.service(is, os);
            } catch (Exception e) {
                // 异常情况处理
                e.printStackTrace();
                String errorMsg = e.getMessage();
                os.write(errorMsg.getBytes());
            }
        } else {
            // 无效请求情况处理
            String errorMsg = "No find request Info!";
            os.write(errorMsg.getBytes());
        }
    }

    private static void writeStaticResponse(OutputStream os) throws IOException {
        File file = new File(baseFileUrl, URL);
        FileInputStream fis = null;
        if (file.exists()) {
            // 响应头部信息
            os.write("HTTP/1.1 200 OK\n".getBytes());
            os.write("Content-Type:text/html;charset-utf-8\n".getBytes());
            os.write("Server:Apache-Coyote/1.1\n".getBytes());
            os.write("\n\n".getBytes());
            // 处理响应参数
            byte[] bytes = new byte[2048];
            fis = new FileInputStream(file);
            int ch = fis.read(bytes);
            while (ch != -1) {
                os.write(bytes, 0, ch);
                ch = fis.read(bytes);
            }

//            其他方式输出流信息
//            int i = fis.read(bytes);
//            for (int j = 0; j < i; j++) {
//                buffer.append((char) bytes[j]);
//            }

        } else {
            os.write("HTTP/1.1 404 Not found\n".getBytes());
            os.write("Content-Type:text/html;charset-utf-8\n".getBytes());
            os.write("Server:Apache-Coyote/1.1\n".getBytes());
            os.write("\n\n".getBytes());
            os.write("<html>".getBytes());
            os.write("<head><title>404</title></head>".getBytes());
            os.write("<body>".getBytes());
            os.write("<h1>404</h1>".getBytes());
            os.write("</body>".getBytes());
            os.write("</html>".getBytes());
        }
        if (fis != null) {
            fis.close();
        }
    }

    /**
     * 获取请求地址信息
     * @param is
     * @return
     */
    private static String getRequestUrl(InputStream is) throws IOException {
        StringBuffer content = new StringBuffer(2048);
        byte[] buffer = new byte[2048];

        // 将读取到的每行数据存储进buffer字节数组中
        int i = is.read(buffer);
        // 遍历将每个字符数据存储进content中
        for (int j = 0; j < i; j++) {
            content.append((char) buffer[j]);
        }
//        System.out.println(content);
        // 获取请求的地址信息(例如: GET /demo.html HTTP/1.1中的demo.html信息)
        int index1,index2;
        index1 = content.indexOf(" ");
        if (index1 != -1) {
            // 获取第二个节点位置,获取上一个节点的后一个(index1+1)
            index2 = content.indexOf(" ", index1+1);
            // 第二个节点也存在的话
            if (index2 > index1) {
                return content.substring(index1+2, index2);
            }
        }
        return "";
    }

    /**
     * 释放资源
     * @param socket
     * @param os
     * @param is
     */
    private static void closeServer(Socket socket, OutputStream os, InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (os != null) {
            try {
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
其他类

server.properties

aa=test.AAServlet
bb=test.BBServlet

demo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>/demo.html.</h1>
</body>
</html>

Servlet.java

package test;

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

public interface Servlet {
    void init();
    void service(InputStream is, OutputStream os);
    void destry();
}

AAServlet.java

package test;

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

public class AAServlet implements Servlet {
    @Override
    public void init() {
        System.out.println("AAServlet init...");
    }

    @Override
    public void service(InputStream is, OutputStream os) {
        String msg = "AAServlet service...";
        try {
            os.write(msg.getBytes());
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void destry() {
        System.out.println("AAServlet destry...");
    }
}

2.代码demo

链接: https://pan.baidu.com/s/1l5050pQA4-2qBkBtzCwCDg
提取码:ze2b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值