阅读How Tomcat Works笔记

一 概述

1 Servlet容器

Servlet容器为一个Servlet请求提供服务基本要做三件事

  1. 创建request对象填充可能被servlet使用的信息,如参数、头部、cookies。一个request对象是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的实例
  2. 创建一个response对象给客户端发送响应,一个response对象是javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的实例
  3. 调用servlet的service方法,传入request和response对象,servlet会从request中取值,给response写值

2 Catalina

Catalina架构

2.1 Connector

连接器连接容器中的请求,为接收到的每一个HTTP请求构造一个request和response对象,然后把流程传递给容器

2.2 Container

容器接收到request和response对象调用servlet的service方法进行响应。容器还要做相当多的事情,比如加载servlet、验证用户、更新会话等

3 Tomcat4和Tomcat5

Tomcat4

  • 每一个组件有自己的后台处理线程
  • 使用Servlet2.3和JSP1.2规范

Tomcat5

  • 使用Servlet2.4和JSP2.0规范
  • 更有效率的默认连接器
  • 所有组件共享一个后台处理线程,占有更少的资源
  • 简化了代码,不需要一个映射组件(mapper component)查找子组件

二 一个简单的Web服务器

1 超文本传输协议

HTTP协议是一种请求和响应协议,使用可靠的TCP连接,TCP连接默认80端口。允许Web服务器和浏览器通过互联网发送和接受数据。再HTTP中,始终是客户端通过建立连接和发送请求来开启一个事务

1.1 HTTP请求

HTTP请求包括四部分

  • 请求方式 统一资源标识符URI 协议/版本号
    统一资源标识符相对于服务器的根目录解释,始终用/开头
  • 请求头
    请求头包括客户端环境和请求主体的有用信息
  • 请求空行
  • 请求主体(GET请求没有、POST请求有)

1.2 HTTP响应

  • 请求方式 统一资源标识符URI 协议/版本号
  • 响应的头
  • 响应空行
  • 响应主体

2 Socket

Socket是网络连接的端点,使一个应用可以从网络中读取和写入数据,使两台不同计算机上的不同应用通过连接发送和接收字节流。向一个应用发送信息需要知道应用的IP地址和套接字端口。Socket代表一个客户端套接字。

2.1 使用Socket访问Tomcat主页

public class TestSocket {
    public static void main(String[] args) {
        try {
            //创建套接字
            Socket socket = new Socket("127.0.0.1", 8080);
            //OutPutStream发送字节流
            OutputStream out = socket.getOutputStream();
            //PrintWriter发送文本到远程应用
            PrintWriter writer = new PrintWriter(out, true);
            //访问Tomcat主页
            writer.println("GET /index.jsp HTTP/1.1");
            writer.println("host:localhost:8080");
            writer.println("Connection:close");
            writer.println();
            //缓冲字符输入流,获得请求结果
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            StringBuffer stringBuffer = new StringBuffer();
            boolean loop = true;
            while (loop){
                if (bufferedReader.ready()){
                    int i = 0;
                    while (i != -1){
                        //读取单个字符,读到流的末尾返回-1
                        i = bufferedReader.read();
                        stringBuffer.append((char)i);
                    }
                    loop = false;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(stringBuffer.toString());
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

启动本地Tomcat,运行程序在浏览器访问localhost:8080

3 ServerSocket

ServerSocket代表一个服务端套接字。服务端套接字等待来自客户端的连接请求,当获得一个请求时,创建一个Socket对象与客户端进行通信。构建ServerSocket的三个参数分别是port(监听端口),backlog(可接受的连接的队列的最大长度),bindAddr(服务器将绑定的本地的InetAddress,通常使用InetAddress.geBytName(host)获得)。

3.1 使用ServerSocket获得请求的资源

  • 这是我的项目工程目录
    在这里插入图片描述

  • 项目资源

  • HttpServer

    public class HttpServer {
    
        public static String WEB_ROOT = null;
    
        public static final String SHUTDOWN = "/shutdown";
    
        private boolean shutdown = false;
    
        public static void main(String[] args) {
            //获取当前类所在的模块编译之后的根路径
            File file1 = new File(HttpServer.class.getResource("/").getPath());
            String path = file1.getAbsolutePath();
            String[] split = path.split("\\\\");
            //System.getProperty("user.dir") 获得项目根路径
            //split[5] 获得模块名
            WEB_ROOT = System.getProperty("user.dir") + File.separator + split[5] + File.separator + "web";
    
            HttpServer httpServer = new HttpServer();
            System.out.println("启动服务器...");
            try {
                //启动服务器
                httpServer.await(WEB_ROOT);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void await(String webRoot) throws IOException {
            ServerSocket serverSocket = null;
            int port = 8080;
            try {
                serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
            //如果响应的资源里有图片,浏览器需要重新开启一个线程读取图片
            while (!shutdown){
                //收到请求时才往下执行,否则阻塞
                Socket socket = serverSocket.accept();;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        OutputStream output = null;
                        InputStream input = null;
                        try {
                            System.out.println("收到请求...");
                            input = socket.getInputStream();
                            output = socket.getOutputStream();
                            Request request = new Request(input);
                            request.parse();
                            Response response = new Response(output);
                            response.setRequest(request);
                            response.getResponseOfTheRequest();
                            socket.close();
                            shutdown = request.getUri().equals(SHUTDOWN);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
            System.out.println("关闭服务器");
            serverSocket.close();
        }
    
    }
    
  • Request

    public class Request {
        //客服端的请求输入流,包含客户端的请求信息
        private InputStream input;
    
        private String uri;
    
        public Request(InputStream is) {
            this.input = is;
        }
    
        /**
         * 解析客服端发送的请求
         */
        public void parse(){
            StringBuffer request= new StringBuffer(2048);
            byte[] bytes = new byte[2048];
            int i;
            try {
                i = input.read(bytes);
            } catch (IOException e) {
                e.printStackTrace();
                i = -1;
            }
            for (int j = 0; j < i; j++) {
                request.append((char)bytes[j]);
            }
            System.out.println(request.toString());
            uri = parseUri(request.toString());
        }
    
        /**
         * 获得请求uri
         */
        public String parseUri(String request) {
            int index1,index2;
            index1 = request.indexOf(" ");
            if (index1 != -1){
                index2 = request.indexOf(" ", index1 + 1);
                if (index2 > index1){
                    return request.substring(index1+1,index2);
                }
            }
            return null;
        }
    
        /**
         * 调用过parse方法uri才有值
         */
        public String getUri(){
            return uri;
        }
    
    }
    
  • Response

    public class Response {
        public static String WEB_ROOT = null;
        private static final int BUFFER_SIZE = 1024;
        OutputStream outputStream;
        Request request;
    
        public Response(OutputStream outputStream){
            this.outputStream = outputStream;
        }
    
        public void setRequest(Request request){
            this.request = request;
        }
    
        /**
         * 向浏览器写出请求的响应
         */
        public void getResponseOfTheRequest(){
    
            File file1 = new File(HttpServer.class.getResource("/").getPath());
            String path = file1.getAbsolutePath();
            String[] split = path.split("\\\\");
    
            WEB_ROOT = System.getProperty("user.dir") + File.separator + split[5] + File.separator + "web";
    
            FileInputStream fis = null;
    
            byte[] bytes = new byte[BUFFER_SIZE];
            int i;
            try {
                File file = new File(WEB_ROOT, request.getUri());
                System.out.println("请求uri:"+request.getUri());
                if (file.exists()){
                    fis = new FileInputStream(file);
                    int ch = fis.read(bytes,0,BUFFER_SIZE);
                    System.out.println("请求成功...");
                    System.out.println("请求响应结果...");
                    String msg = "HTTP/1.1 200 OK\r\n" +
                            "Content-Type:text/html\r\n" +
                            "\r\n";
                    outputStream.write(msg.getBytes());
                    while (ch != -1){
                        outputStream.write(bytes,0,ch);
                        ch = fis.read(bytes,0,BUFFER_SIZE);
                    }
                }else {
                    String errorMsg = "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>";
                    System.out.println("请求失败...");
                    System.out.println("请求响应结果...");
                    outputStream.write(errorMsg.getBytes());
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }finally {
                if (fis!=null){
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }
    }
    
  • 启动项目HttpServer
    在这里插入图片描述
    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值