深入剖析Tomcat(一) 实现一个简单的Web容器

news2025/1/27 12:41:24

前言

在Java生态中,Tomcat的应用可谓经久不衰,众多Java Web应用都依赖它来进行部署。

虽然我们经常使用它,但是如果不深入了解的话,它对我们来说就一直是一个黑盒。就单纯的作为一个使用者来说,肯定也知道它内部应用了很多知识

  • 它是一个Web服务器,支持HTTP与HTTPS协议。
  • 它支持并发,内部肯定存在多线程编码。
  • 它支持会话连接,内部有Session的实现。
  • 它能打印应用运行的日志,内部含有日志模块。
  • 它是一个Servlet容器,Servlet究竟是什么?

等等…,这里不再一一列举。可见如果你真正的去将Tomcat深刻了解一遍的话,能学习或者巩固不少知识点,对众多编程技术的应用也是一个很出色的参考案例。

那怎么去深入学习它呢?网络上相关的体系教程很少,几乎没有;相关书籍也是凤毛麟角。我翻看了各大论坛,线索都指向了一本外文书籍《How Tomcat Works》,它也有中文译本《深入剖析Tomcat》。这本书籍很老,是由Budi Kurniawan于2004年出版的,文中代码也是基于jdk 1.4来编写的。不过虽然这本书年代久远,但是它对Tomcat的讲解却是非常详细,更是带着读者从零到一来编写了一个简易版的Tomcat。

所以我的设想是:以《深入剖析Tomcat》作为参照,按照文章中的代码示例来一步一步造出一个完整的Tomcat出来。

由于这本书的示例代码太久远了,当我把项目下载下来打开后,选择使用jdk 1.8来编译,出现了很多编译报错,不过我都一一解决了,改正后的代码我已传到gitee上,文末附有连接,需要的话自取。

我尝试运行了前两章的示例代码,但是运行结果却不全是书中所描述的那样。因此我决定对书中每一章的代码都重新编码,然后将踩过的坑与大家分享共勉。

接下来进入正题,第一章:一个简单的Web容器

实现一个简单的Web容器

第一章的内容很简单,实现一个基于HTTP协议的Web服务器,并且仅支持静态资源的获取即可。

HTTP协议的详细介绍可以看我这篇历史文章 网络协议(七)应用层-HTTP

HTTP在传输层使用的协议为TCP协议,TCP的详细介绍可以看这篇文章 网络协议(六)传输层

本章的代码使用Socket进行编程,对Socket不了的同学可以先看看这篇文章 Java Socket通信编程

我一口气拉了三篇历史文章过来,全是网络协议相关的内容。可见要建造上层建筑,基础知识必不可少啊!

如果你对HTTP协议与Socket编程很熟悉了,那么接下来设计Web服务就简单多了,大致分为这三步

  1. 服务端接收Socket的InputStream,转化为Request对象
  2. 根据Socket的OutputStream,构建Response对象
  3. 处理客户端的请求,由Response将结果推送回客户端

Request类代码如下

主要功能为解析HTTP请求,并得到客户端要请求的资源uri

package ex01.hml;

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


public class Request {

    private String uri;

    // 从输入流中解析出 HTTP 协议内容,并得到客户端要请求的资源uri
    public void parse(InputStream inputStream) {

        try {
            byte[] bytes = new byte[1024];
            int readLenth = inputStream.read(bytes);
            String content = "";
            while(readLenth != -1) {
                content  += new String(bytes);
                if(readLenth < 1024) {
                    break;
                }
                readLenth = inputStream.read(bytes);
            }
            System.out.println("request body --->");
            System.out.println(content);
            //获取请求头中第一个空格和第二个空格之间的内容,例如:请求头 【GET /index.html HTTP/1.1】,uri即为 【/index.html】 
            setUri(content);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setUri(String content) {
        int index1 = content.indexOf(" ");
        if(index1 == -1) {
            return;
        }
        int index2 = content.indexOf(" ", index1+1);
        String substring = content.substring(index1+1, index2);
        this.uri = substring;
    }

    public String getUri() {
        return uri;
    }

}

Reponse类的代码如下

主要功能为访问目标静态资源,并拼装HTTP响应,返给客户端

package ex01.hml;

import java.io.*;


public class Response {

    private Request request;

    private OutputStream outputStream;

    // 静态资源的存放路径
    public static final String rootDir = System.getProperty("user.dir") + File.separatorChar + "webroot";

    public Response(Request request, OutputStream outputStream) {
        this.request = request;
        this.outputStream = outputStream;
    }

    /**
     * 通过客户端请求的 uri 获取指定静态资源,并拼装成HTTP响应消息,返回给客户端
     */
    public void sendStaticResource() {
        try {
            if (request.getUri().equals("/shutdown")) {
                String msg = "HTTP/1.1 200 OK\r\n" +
                        "Content-Type: text/html\r\n" +
                        "Content-Length: 32\r\n" +
                        "\r\n" +
                        "<h1>server already shutdown</h1>";
                outputStream.write(msg.getBytes());
                return;
            }

            File file = new File(rootDir + request.getUri());
            if (file.exists()) {
                FileInputStream fileInputStream = new FileInputStream(file);
                byte[] bytes = new byte[fileInputStream.available()];
                fileInputStream.read(bytes);
                String successMsg = "HTTP/1.1 200 OK\r\n" +
                        "Content-Type: text/html\r\n" +
                        "Content-Length: " + bytes.length + "\r\n" +
                        "\r\n";
                outputStream.write(successMsg.getBytes());
                outputStream.write(bytes);
                fileInputStream.close();
            } else {
                String errorMessage = "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>";
                outputStream.write(errorMessage.getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}

HttpServer服务启动类代码如下

package ex01.hml;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class HttpServer {

    // 声明一个结束标识,用来判断是否需要终止服务
    public static boolean shutDown = false;

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080,10,InetAddress.getByName("127.0.0.1"));
            Socket socket;
            while(!shutDown) {
                socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                Request request = new Request();
                request.parse(inputStream);
                OutputStream outputStream = socket.getOutputStream();
                Response response = new Response(request,outputStream);
                response.sendStaticResource();
                inputStream.close();
                socket.close();

                // 判断是否是结束服务的请求
                shutDown = request.getUri().equals("/shutdown");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

静态资源index.html

<html>
    <head>
        <title>Welcome to BrainySoftware</title>
    </head>
    <body>
        <img src="./images/logo.gif">
        <br>
        Welcome to BrainySoftware.
    </body>
</html>

请求结果

浏览器截图

服务端日志

request body --->
GET /index.html HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Cookie: userToken=t6tnfg5d82lshqufl69g4j


request body --->
GET /images/logo.gif HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
sec-ch-ua-platform: "macOS"
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://127.0.0.1:8080/index.html
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Cookie: userToken=t6tnfg5d82lshqufl69g4j


request body --->
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
sec-ch-ua-platform: "macOS"
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://127.0.0.1:8080/index.html
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Cookie: userToken=t6tnfg5d82lshqufl69g4j


/favicon.ico 这个请求是浏览器自动请求的,favicon.ico是指显示在浏览器收藏夹、地址栏和标签标题前面的个性化图标。当你使用浏览器浏览不同站点时,浏览器将自动发送请求。 如果你的浏览器收到有效 favicon.ico 文件,将显示此图标。 如果未收到,则不会显示特殊图标,且会报404错误。

由于我的项目里没有这个favicon.ico文件,所以这里后端返回404。

好了,一个简单的静态资源Web服务器就搭好了,是不是很简单?下篇文章将引入servlet来处理动态内容,敬请期待!

源码分享

https://gitee.com/huo-ming-lu/HowTomcatWorks

原书作者的源码在 ex01.pyrmont 包下,我修改后的代码在 ex01.hml 包下,以此类推,后面每一章的代码都会是这个形式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1586586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Win10下安装Anaconda

Anaconda是可以便捷获取包且对包能够进行管理&#xff0c;同时对环境可以统一管理的发行版本&#xff0c;它包含了conda、Python在内的超过180个科学包及其依赖项。 安装Anaconda Anaconda官方下载网址&#xff1a;https://www.anaconda.com/download 官网页面中&#xff0c…

探究 ChatGPT 的心脏--Transformer(基础知识第一篇)

Transformer 是 ChatGPT 的核心部分&#xff0c;如果将 AI 看做一辆高速运转的汽车&#xff0c;那么 Transformer 就是最重要的引擎。它是谷歌于 2017 年发表的《Attention is All You Need》中提出的 Sequence-to-sequence 的模型&#xff0c;诞生之后便一统江湖&#xff0c;在…

【考研数学】《660》+《880》高分搭配方法

&#x1f4dd;《660题》和《880题》高效刷题方法 1️⃣做题要有针对性&#xff0c;不要为了做题而做题 &#x1f4aa;660和880题虽然多&#xff0c;但是你不用全都做完&#xff0c;你可以把它当成是题源&#xff0c;里面的每一道题都很经典&#xff0c;如果搞懂一道&#xff…

51蓝桥杯之DS18B20

DS18B20 基础知识 代码流程实现 将官方提供例程文件添加到工程中 添加onewire.c文件到keil4里面 一些代码补充知识 代码 #include "reg52.h" #include "onewire.h" #include "absacc.h" unsigned char num[10]{0xc0,0xf9,0xa4,0xb0,0x99,…

最近一些前端面试问题整理

最近一些前端面试问题整理 4月8号1. TS 中的 类型别名 和接口的区别是什么&#xff1f;2. 什么是深拷贝和浅拷贝&#xff1f;深浅拷贝的方法有哪些&#xff1f;浅拷贝&#xff08;Shallow Copy&#xff09;深拷贝&#xff08;Deep Copy&#xff09;区别总结 3. 使用 JSON.strin…

如何在淘~宝接单和解决别人问题-java开发

如下这是一个连接&#xff1a;https://s.tb.cn/c.0vDtL3https://s.tb.cn/c.0vDtL3 解决各种问题。可付费咨询

炒股自动化:交易接口API才是重点,券商官方散户可用的接口

上一篇我们用get_full_tick取到了数据&#xff0c;也讲了变量和字典的基本概念&#xff0c;这次我们向交易所发送订单试试。前面文章的链接放在文末了&#xff0c;需要的可以看一下 这些内容是给新手看的&#xff0c;找接口的大佬们直接拉到文末即可 取市场数据的方法很多&am…

查询优化-ANY类型子连接提升

瀚高数据库 目录 文档用途 详细信息 文档用途 已知查询树基本结构的基础上&#xff0c;在一定条件下对SQL中的子连接提升&#xff0c; 详细信息 子连接提升流程 SQL: SELECT sname FROM student WHERE sno > ANY (SELECT sno FROM score);图1.1是该SQL对应的查询树&#…

代码随想录算法训练营第三十一天| 455.分发饼干、376.摆动序列、53.最大子序和

系列文章目录 目录 系列文章目录455.分发饼干贪心算法大饼干喂胃口大的&#xff08;先遍历胃口&#xff09;胃口大的先吃大饼干(先遍历饼干&#xff09;小饼干先喂胃口小的&#xff08;先遍历胃口&#xff09;胃口小的先吃小饼干&#xff08;先遍历饼干&#xff09; 376. 摆动序…

为什么在学校很难真正学好嵌入式?

10几年前,我是读电气工程专业,学了很多东西,结构,电机、绘图,plc等等.. 其实,都没什么鸟用,出来还是像个废物。 后面我自学转了单片机开发,说句难听点,自己买个开发板都比在学校学得深。 可能是这个专业的问题,主攻不是嵌入式方向,老师用汇编点个流水灯,这门课就…

第四百五十三回

文章目录 1. 问题描述2. 优化方法2.1 缩小范围2.2 替代方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取AppBar的高度"相关的内容&#xff0c;本章回中将介绍关于MediaQuery的优化.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 问题描述 我们在…

编程新手必看,python中条件控制语句学习(13)

介绍&#xff1a; Python3中的条件控制主要通过if、elif和else关键字来实现&#xff0c;它们用于根据条件执行特定的代码块。 if语句&#xff1a;这是最基本的条件控制结构。如果满足某个条件&#xff08;条件为True&#xff09;&#xff0c;则执行相应的代码块。在Python中&am…

Java学习42-Java 流(Stream)、文件(File)和IO - FileReader\FileWriter的使用

IO流 IO流的概述&#xff0c;分类等 Java程序中&#xff0c;对于数据的输入输出以stream方式进行&#xff0c;可以看作是一种数据的流动。 IO流中的IO是Input和Output的缩写&#xff0c;是非常实用的技术&#xff0c;用于处理设备之间的数据传输。读写文件&#xff0c;网络通…

Linux下批量的批量操作

批量删除docker 镜像 docker images | grep ent-form-web |awk ‘{print $3}’ | xargs docker rmi docker images: 列出所有的docker 镜像 docker images | grep ent-form-web : 选取出结果带 ent-form-web的信息 docker images | grep ent-form-web |awk ‘{print $3}’ 选取…

黑白图像彩色化

黑白图像彩色化 黑白图像彩色化&#xff0c;调用训练好的模型&#xff0c;将图片加上彩色&#xff0c;只需要OPENCV&#xff0c;支持C/PYTHON

【Python函数和类3/6】函数的返回值

目录 知识回顾 目标 函数的返回值 Tips 练习 ​编辑return的其它特性 任意类型的返回值 返回多个值 return的位置 小结 局部变量 局部变量的作用域 全局变量 全局变量的作用域 同名变量 pi的作用域 总结 知识回顾 在上篇博客中&#xff0c;我们学习给函数设置参…

基于 S2-LP 实现 802.15.4g 帧格式的数据透传

1. 引言 S2-LP 硬件上支持 802.15.4g 的帧格式&#xff0c;但是现有的 SDK 包并没有基于该帧格式的示例工程&#xff0c;因此本篇文章将介绍如何实现基于 802.15.4g 帧格式的数据透传。 2. 802.15.4g 帧格式 在开始之前&#xff0c;需要对 802.15.4g 帧格式有一个初步的了解…

VS中使用QT的UI提升类时,找不到头文件的情况

1、情况简述 在使用VS时&#xff0c;会发现与QCreator存在一些差异。最主要的就是要设置很多东西&#xff0c;如果不配置的话&#xff0c;就会遇到一些问题。下面我分享下我调试过程中遇到的一个问题。使用Qdesigner的UI提升类时&#xff0c;找不到头文件的情况&#xff1a; …

Flutter - 环境配置提示 cmdline-tools component is missing

问题&#xff1a; flutter doctor运行命令 flutter doctor 报错&#xff1a; Android toolchain - develop for Android devices (Android SDK version 30.0.2) ✗ cmdline-tools component is missing Run path/to/sdkmanager --install "cmdline-tools;lates…

ChatGPT国内镜像站大全(全都是能白嫖的)

今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像站到处都是&a…