Tomcat应该都不陌生,我们经常会把写好的代码打包放在Tomcat里并启动,然后在浏览器里就能愉快的调用我们写的代码来实现相应的功能了,那么Tomcat是如何工作的?

一、Tomcat工作原理

我们启动Tomcat时双击的startup.bat文件的主要作用是找到catalina.bat,并且把参数传递给它,而catalina.bat中有这样一段话:

Bootstrap.class是整个Tomcat 的入口,我们在Tomcat源码里找到这个类,其中就有我们经常使用的main方法:

这个类有两个作用 :1.初始化一个守护进程变量、加载类和相应参数。2.解析命令,并执行。

源码不过多赘述,我们在这里只需要把握整体架构,有兴趣的同学可以自己研究下源码。Tomcat的server.xml配置文件中可以对应构架图中位置,多层的表示可以配置多个:

即一个由 Server->Service->Engine->Host->Context 组成的结构,从里层向外层分别是:

  • Server:服务器Tomcat的顶级元素,它包含了所有东西。
  • Service:一组 Engine(引擎) 的集合,包括线程池 Executor 和连接器 Connector 的定义。
  • Engine(引擎):一个 Engine代表一个完整的 Servlet 引擎,它接收来自Connector的请求,并决定传给哪个Host来处理。
  • Container(容器):Host、Context、Engine和Wraper都继承自Container接口,它们都是容器。
  • Connector(连接器):将Service和Container连接起来,注册到一个Service,把来自客户端的请求转发到Container。
  • Host:即虚拟主机,所谓的”一个虚拟主机”可简单理解为”一个网站”。
  • Context(上下文 ): 即 Web 应用程序,一个 Context 即对于一个 Web 应用程序。Context容器直接管理Servlet的运行,Servlet会被其给包装成一个StandardWrapper类去运行。Wrapper负责管理一个Servlet的装载、初始化、执行以及资源回收,它是最底层容器。

比如现在有以下网址,根据“/”切割的链接就会定位到具体的处理逻辑上,且每个容器都有过滤功能。

二、Tomcat实现思路

下面只是简单实现效果,当浏览器访问对应地址时:

实现以上效果整体思路如下:

1.ServerSocket占用8080端口,用while(true)循环等待用户发请求。

2.拿到浏览器的请求,解析并返回URL地址,用I/O输入流读取本地磁盘上相应文件。

3.读取文件,不存在构建响应报文头、HTML正文内容,存在则写到浏览器端。

三、实现Tomcat

工程文件结构和pom.xml文件:

1.HttpServer核心处理类,用于接受用户请求,传递HTTP请求头信息,关闭容器:

public class HttpServer {
// 用于判断是否需要关闭容器
private boolean shutdown = false; public void acceptWait() {
ServerSocket serverSocket = null;
try {
//端口号,最大链接数,ip地址
serverSocket = new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// 等待用户发请求
while (!shutdown) {
try {
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 接受请求参数
Request request = new Request(is);
request.parse();
// 创建用于返回浏览器的对象
Response response = new Response(os);
response.setRequest(request);
response.sendStaticResource();
//关闭一次请求的socket,因为http请求就是采用短连接的方式
socket.close();
//如果请求地址是/shutdown 则关闭容器
if(null != request){
shutdown = request.getUrL().equals("/shutdown");
}
}
catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
public static void main(String[] args) {
HttpServer server = new HttpServer();
server.acceptWait();
}
}

2.创建Request类,获取HTTP的请求头所有信息并截取URL地址返回:

public class Request {
private InputStream is;
private String url; public Request(InputStream input) {
this.is = input;
}
public void parse() {
//从socket中读取一个2048长度字符
StringBuffer request = new StringBuffer(Response.BUFFER_SIZE);
int i;
byte[] buffer = new byte[Response.BUFFER_SIZE];
try {
i = is.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j=0; j<i; j++) {
request.append((char) buffer[j]);
}
//打印读取的socket中的内容
System.out.print(request.toString());
url = parseUrL(request.toString());
} private String parseUrL(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');//看socket获取请求头是否有值
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
} public String getUrL() {
return url;
} }

3.创建Response类,响应请求读取文件并写回到浏览器

public class Response {
public static final int BUFFER_SIZE = 2048;
//浏览器访问D盘的文件
private static final String WEB_ROOT ="D:";
private Request request;
private OutputStream output; public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
} public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
//拼接本地目录和浏览器端口号后面的目录
File file = new File(WEB_ROOT, request.getUrL());
//如果文件存在,且不是个目录
if (file.exists() && !file.isDirectory()) {
fis = new FileInputStream(file);
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch!=-1) {
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
}else {
//文件不存在,返回给浏览器响应提示,这里可以拼接HTML任何元素
String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
String returnMessage ="HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: "+retMessage.length()+"\r\n" +
"\r\n" +
retMessage;
output.write(returnMessage.getBytes());
}
}
catch (Exception e) {
System.out.println(e.toString() );
}
finally {
if (fis!=null)
fis.close();
}
}
}

四、扩展点

1.在WEB_INF文件夹下读取web.xml解析,通过请求名找到对应的类名,通过类名创建对象,用反射来初始化配置信息,如welcome页面,Servlet、servlet-mapping,filter,listener,启动加载级别等。

2.抽象Servlet类来转码处理请求和响应的业务。发过来的请求会有很多,也就意味着我们应该会有很多的Servlet,例如:RegisterServlet、LoginServlet等等还有很多其他的访问。可以用到类似于工厂模式的方法处理,随时产生很多的Servlet,来满足不同的功能性的请求。

3.使用多线程。本文的代码是死循环,且只能有一个链接,而现实中的情况是往往会有很多很多的客户端发请求,可以把每个浏览器的通信封装到一个线程当中。

【Tomcat】Tomcat工作原理及简单模拟实现的更多相关文章

  1. 你还记得 Tomcat 的工作原理么

    SpringBoot 就像一条巨蟒,慢慢缠绕着我们,使我们麻痹.不得不承认,使用了 SpringBoot 确实提高了工作效率,但同时也让我们遗忘了很多技能.刚入社会的时候,我还是通过 Tomcat 手 ...

  2. Optaplanner规划引擎的工作原理及简单示例(2)

    开篇 在前面一篇关于规划引擎Optapalnner的文章里(Optaplanner规划引擎的工作原理及简单示例(1)),老农介绍了应用Optaplanner过程中需要掌握的一些基本概念,这些概念有且于 ...

  3. RabbitMQ系列(二)深入了解RabbitMQ工作原理及简单使用

    深入了解RabbitMQ工作原理及简单使用 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器Exchange介绍 ...

  4. 深入解读RabbitMQ工作原理及简单使用

    RabbitMQ系列目录 RabbitMQ在Ubuntu上的环境搭建 深入解读RabbitMQ工作原理及简单使用 Rabbit的几种工作模式介绍与实践 Rabbit事务与消息确认 Rabbit集群搭建 ...

  5. 深入了解RabbitMQ工作原理及简单使用

    深入了解RabbitMQ工作原理及简单使用 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器Exchange介绍 ...

  6. LVS负载均衡机制之LVS-DR模式工作原理以及简单配置

    本博文主要简单介绍一下LVS负载均衡集群的一个基本负载均衡机制:LVS-DR:如有汇总不当之处,请各位在评论中多多指出. LVS-DR原理: LVS的英文全称是Linux Virtual Server ...

  7. JDBC【2】-- JDBC工作原理以及简单封装

    目录 1. 工作原理 1.1 加载驱动 1.1.1 类加载相关知识 1.1.2 为什么JDK 1.6之后不需要显示加载了? 1.2 驱动加载完成了,然后呢? 2. 简单封装 1. 工作原理 一般我们主 ...

  8. Tomcat Servlet工作原理

    前言 Tomcat的启动过程 Web应用初始化 创建Servlet实例 初始化Servlet 执行service方法 前言 Servlet实际上就是一个java类,只不过可以和浏览器进行一些数据的交换 ...

  9. 【IntelliJ IDEA】idea部署服务到Tomcat的工作原理

    参考地址: https://blog.csdn.net/qq_41116058/article/details/81435084 为什么idea部署服务到tomcat时候,一定要修改Applicati ...

随机推荐

  1. Django:Python3.6.2+Django2.0配置MySQL

    持续学习Django中... Django默认使用的数据库是python自带的SQLlite3,但SQLlite并不适用于大型的项目,因此我将数据库换成了MySQL,下面介绍下Django如何配置数据 ...

  2. Bug的严重等级和优先级别与分类

    一. Bug的严重等级定义: 1. Blocker 即系统无法执行.崩溃或严重资源不足.应用模块无法启动或异常退出.无法测试.造成系统不稳定. 严重花屏 内存泄漏 用户数据丢失或破坏 系统崩溃/死机/ ...

  3. 系统的讲解 - PHP WEB 安全防御

    目录 常见漏洞 SQL注入攻击 XSS攻击 SSRF攻击 CSRF攻击 文件上传漏洞 信息泄露 越权 设计缺陷 小结 常见漏洞 看到上图的漏洞是不是特别熟悉,如果不进行及时防御,就会产生蝴蝶效应. 往 ...

  4. C# - 为值类型重定义相等性

    为什么要为值类型重定义相等性 原因主要有以下几点: 值类型默认无法使用 == 操作符,除非对它进行重写 再就是性能原因,因为值类型默认的相等性比较会使用装箱和反射,所以性能很差 根据业务需求,其实际相 ...

  5. Python-字符版gif图

    一.背景 上一篇文章我们讲了怎么做自己的炫酷二维码,需要的移驾Python-炫酷二维码,本片文章我们讲述下怎么把一张图片处理成字符版图片,就是说使用字符替代每个像素的颜色,形成一个由字符组成的图片,并 ...

  6. ab性能测试工具的使用

    一.什么是ab ab,即Apache Benchmark,是一种用于测试Apache超文本传输协议(HTTP)服务器的工具. ab命令会创建很多的并发访问线程,模拟多个访问者同时对某一URL地址进行访 ...

  7. asp.net core系列 29 EF模型配置(查询类型,关系数据库建模)

    一.查询类型 此功能是EF Core 2.1中的新功能. EF Core除了实体类型之外,EF Core模型还可以包含查询类型,这些查询类型是针对“未映射到实体类型”的数据获取.比如视图,或只读数据表 ...

  8. MVC设计模式思想及简单实现

    一.什么是MVC MVC即Model-View-Controller(模型-视图-控制器)是一种软件设计模式,最早出现在Smalltalk语言中,后被Sun公司推荐为Java EE平台的设计模式. M ...

  9. 【JDBC 笔记】

    JDBC 笔记 作者:晨钟暮鼓c个人微信公众号:程序猿的月光宝盒 对应pdf版:https://download.csdn.net/download/qq_22430159/10754554 没有积分 ...

  10. vue + element + 初始化项目

    前提:已经安装了好了npm 和 vue脚手架 注意: 进入想要放置项目的目录下. 1.vue init webpack sun-vue-element 2.根据提示输入 y/n 3.npm run d ...