简单实现"Tomcat"
Tomcat的主要功能就是接收客户端的Http请求,然后将请求分发,并且将请求封装,最后返回资源给到客户端。话不多说,开干。
一、实现设计图
(禁止盗图,除非先转支付宝!!!)
二、代码
1、工程结构目录图
新建java project 即可,目录图如下:
2、工程描述
a、dispatcher是用来处理请求信息的,并且分发到静态处理器还是动态处理器。
b、HttpEntity则是实体类,包括request以及response。
c、process包括StaticServletProcess以及DynamicServletProcessor,去构造实际处理的servlet对象以及构建静态资源路径等等。
d、server则是模拟的整个http容器。
e、servlet下则存放实际处理的servlet
三、代码
1、server(http容器类)
- package com.ty.server;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
- import com.ty.dispatcher.ProcessDispatcher;
- /**
- * @author Taoyong
- * @date 2018年5月23日
- * 天下没有难敲的代码!
- */
- /*
- * 此类主要是为了模拟一个http服务器,并且简单起便,使用main方法来启动整个http容器
- */
- public class Server {
- private static boolean shutDown = false;
- /*
- * 为了简单实现,这里直接使用main方法启动
- *
- */
- public static void main(String[] args) throws IOException {
- Server server = new Server();
- server.startServer();
- }
- public void startServer() throws IOException {
- ServerSocket serverSocket = null;
- try {
- /*
- * 创建一个serverSocket,并且绑定本地端口
- */
- serverSocket = new ServerSocket(8088);
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(1);
- }
- while(!shutDown) {
- Socket socket = null;
- InputStream input = null;
- OutputStream output = null;
- try {
- /*
- * socket是对TCP/IP的封装,提供给程序员对TCP/IP传输层进行操作
- * 服务端监听客户端是否有请求过来
- */
- socket = serverSocket.accept();
- //从socket中获取客户端传输内容
- input = socket.getInputStream();
- //从socket中获取传输给客户端的输出流对象
- output = socket.getOutputStream();
- ProcessDispatcher processDispatcher = new ProcessDispatcher(input, output);
- processDispatcher.service();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if(socket != null) {
- socket.close();
- }
- if(input != null) {
- input.close();
- }
- if(output != null) {
- output.close();
- }
- }
- }
- }
- }
2、ProcessDispatcher(分发静态请求or动态请求)
- package com.ty.dispatcher;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import com.ty.httpEntity.Request;
- import com.ty.httpEntity.Response;
- import com.ty.process.Processor;
- import com.ty.process.impl.DynamicServletProcessor;
- import com.ty.process.impl.StaticServletProcessor;
- /**
- * @author Taoyong
- * @date 2018年5月23日
- * 天下没有难敲的代码!
- */
- /*
- * 此类作为一个请求处理分发器,根据客户端请求url的类型(包括静态资源以及动态资源请求),去初始化不同的processor
- * 并且在此时初始化request以及response对象,request、response、processor构成整个处理逻辑与数据传输
- */
- public class ProcessDispatcher {
- private Request request;
- private Response response;
- private Processor processor;
- public ProcessDispatcher(InputStream input, OutputStream output) {
- init(input, output);
- }
- /**
- * 根据input以及output对象对Request、Response以及processor进行初始化
- */
- private void init(InputStream input, OutputStream output) {
- Request request = new Request(input);
- request.resolve();
- this.request = request;
- Response response = new Response(output);
- this.response = response;
- initProcessor(request);
- }
- private void initProcessor(Request request) {
- if(request.getUrl() != null && -1 != request.getUrl().indexOf("dynamic")) {
- DynamicServletProcessor dynamicProcessor = new DynamicServletProcessor();
- this.processor = dynamicProcessor;
- return ;
- }
- if(request.getUrl() != null && -1 != request.getUrl().indexOf("static")) {
- StaticServletProcessor staticProcessor = new StaticServletProcessor();
- this.processor = staticProcessor;
- return ;
- }
- return ;
- }
- /**
- * processor的主要作用就是分发请求到底是由动态servlet处理,还是直接找静态资源
- * @throws IOException
- */
- public void service() throws IOException {
- if(processor == null) {
- return ;
- }
- //根据url获取处理的servletName
- request.setServletName(resolveServletName(request.getUrl()));
- processor.process(request, response);
- }
- private String resolveServletName(String url) {
- String[] arr = url.split("/");
- for(String s: arr) {
- if(s.equals("") || s.equals("dynamic") || s.equals("static")) {
- continue;
- }
- return s;
- }
- return "";
- }
- }
3、Request
- package com.ty.httpEntity;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- /**
- * @author Taoyong
- * @date 2018年5月23日
- * 天下没有难敲的代码!
- */
- /*
- * 此类主要是对请求的封装
- */
- public class Request {
- private InputStream input;
- private String url;
- private String servletName;
- public String getServletName() {
- return servletName;
- }
- public void setServletName(String servletName) {
- this.servletName = servletName;
- }
- public String getUrl() {
- return url;
- }
- public void setUrl(String url) {
- this.url = url;
- }
- public Request(InputStream input) {
- this.input = input;
- }
- /*
- * 此方法主要包括两个作用
- * 1、获取客户端请求的相关数据
- * 2、解析出请求url,并且根据具体url去找到对应的processor
- */
- public void resolve() {
- String requestStr = resolveInput(input);
- if(requestStr == null || requestStr.length() == 0) {
- return;
- }
- resolveURL(requestStr);
- }
- /*
- * 从客户端获取请求相关数据,数据样式如下:
- * GET /static/tomcatProTest HTTP/1.1
- * Host: localhost:8088
- * Connection: keep-alive
- * Upgrade-Insecure-Requests: 1
- * User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
- * Accept: text/html,application/xhtml+xml,application/xml;
- * Accept-Encoding: gzip, deflate, sdch, br
- * Accept-Language: zh-CN,zh;
- */
- private String resolveInput(InputStream input) {
- StringBuilder stringBuilder = new StringBuilder();
- String data = null;
- try {
- /*
- * 关于BufferedReader有个注意项,当读取到的数据为"",会存在阻塞现象,因此这里判断长度是否为0
- */
- BufferedReader reader = new BufferedReader(new InputStreamReader(input, "utf-8"));
- while((data = reader.readLine()) != null && data.length() != 0) {
- stringBuilder.append(data);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return stringBuilder.toString();
- }
- /*
- * HTTP请求头第一行一般为GET /dynamic/helloServlet HTTP/1.1
- * 由于实现的只是简单的Tomcat功能,不实现解析页面传参,另外对于请求url定义如下:
- * 1、动态请求 /dynamic/对应的servlet名称
- * 2、静态资源请求 /static/静态资源名称
- * resolveURL方法是用于切割出/dynamic/helloServlet
- */
- private void resolveURL(String requestStr) {
- int firstSpaceIndex = requestStr.indexOf(" ");
- int secondSpaceIndex = requestStr.indexOf(" ", firstSpaceIndex + 1);
- String url = requestStr.substring(firstSpaceIndex + 1, secondSpaceIndex);
- setUrl(url);
- }
- }
4、Response
- package com.ty.httpEntity;
- import java.io.OutputStream;
- /**
- * @author Taoyong
- * @date 2018年5月23日
- * 天下没有难敲的代码!
- */
- public class Response {
- private OutputStream output;
- public OutputStream getOutput() {
- return output;
- }
- public void setOutput(OutputStream output) {
- this.output = output;
- }
- public Response(OutputStream output) {
- this.output = output;
- }
- }
5、DynamicServletProcessor(动态请求处理器)
- package com.ty.process.impl;
- import com.ty.httpEntity.Request;
- import com.ty.httpEntity.Response;
- import com.ty.process.Processor;
- import com.ty.servlet.Servlet;
- import com.ty.servlet.impl.ErrorServlet;
- /**
- * @author Taoyong
- * @date 2018年5月23日
- * 天下没有难敲的代码!
- */
- public class DynamicServletProcessor implements Processor {
- /*
- * 所有相关处理的servlet都放在这个包下
- */
- private static final String PACKAGE_NAME = "com.ty.servlet.impl.";
- @Override
- public void process(Request request, Response response) {
- String servletName = request.getServletName();
- Class<?> clazz = null;
- Servlet servlet = null;
- try {
- clazz = Class.forName(PACKAGE_NAME + servletName);
- servlet = (Servlet) clazz.newInstance();
- } catch (Exception e) {
- servlet = new ErrorServlet();
- }
- servlet.process(request, response);
- }
- }
6、StaticServletProcessor(静态请求处理器)
- package com.ty.process.impl;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import com.ty.httpEntity.Request;
- import com.ty.httpEntity.Response;
- import com.ty.process.Processor;
- /**
- * @author Taoyong
- * @date 2018年5月23日 天下没有难敲的代码!
- */
- public class StaticServletProcessor implements Processor {
- @Override
- public void process(Request request, Response response) {
- //为了省事,默认都是取txt文件
- File file = new File(Processor.prefix, request.getServletName() + ".txt");
- FileInputStream fis = null;
- BufferedReader reader = null;
- String data = null;
- StringBuilder stringBuilder = new StringBuilder();
- OutputStream output = response.getOutput();
- try {
- if (file.exists()) {
- fis = new FileInputStream(file);
- reader = new BufferedReader(new InputStreamReader(fis, "utf-8"));
- /*
- * 由于返回数据要符合http响应头的格式,所以会存在一个空行,因此这里不能判断data.length != 0的条件
- */
- while((data = reader.readLine()) != null) {
- stringBuilder.append(data + "\r\n");
- }
- output.write(stringBuilder.toString().getBytes("utf-8"));
- output.flush();
- } 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>";
- output.write(errorMessage.getBytes());
- output.flush();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
7、TestServlet(具体的servlet处理类,用于返回客户端数据)
- package com.ty.servlet.impl;
- import java.io.OutputStream;
- import com.ty.httpEntity.Request;
- import com.ty.httpEntity.Response;
- import com.ty.servlet.Servlet;
- /**
- * @author Taoyong
- * @date 2018年5月24日
- * 天下没有难敲的代码!
- */
- public class TestServlet implements Servlet {
- @Override
- public void process(Request request, Response response) {
- OutputStream output = response.getOutput();
- String succMessage = "HTTP/1.1 200 \r\n" + "Content-Type: text/html\r\n"
- + "Content-Length: 63\r\n" + "\r\n" + "请求动态资源:我不管,我最帅,我是你们的小可爱";
- try {
- output.write(succMessage.getBytes("utf-8"));
- output.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
8、ErrorServlet(对于错误的请求url,要求是动态请求,统一的处理servlet)
- package com.ty.servlet.impl;
- import java.io.OutputStream;
- import com.ty.httpEntity.Request;
- import com.ty.httpEntity.Response;
- import com.ty.servlet.Servlet;
- /**
- * @author Taoyong
- * @date 2018年5月24日
- * 天下没有难敲的代码!
- */
- public class ErrorServlet implements Servlet {
- @Override
- public void process(Request request, Response response) {
- OutputStream output = response.getOutput();
- String succMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n"
- + "Content-Length: 21\r\n" + "\r\n" + "请求url出现错误";
- try {
- output.write(succMessage.getBytes("utf-8"));
- output.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
四、测试结果
1、请求动态or静态
由于实现的只是简单的Tomcat功能,不实现解析页面传参,另外对于请求url定义如下:
a、动态请求url样例: /dynamic/对应的servlet名称
b、静态请求url样例: /static/静态资源名称
2、运行server的main方法
首先启动server容器,启动ok后,监听客户端的请求。
3、正确静态url请求本机txt文件
文件路径为:E:\workspace\TomcatPro\webroot\tomcatProTest.txt
项目路径:E:\workspace\TomcatPro
文件内容:
注意点:文件格式需符合http响应头格式,content-length要与具体内容长度对应,还要注意空行一定要有,否则会报错!!!
测试结果:
4、错误的静态请求url
5、正确动态请求url
6、错误动态请求url
所有测试结果都ok,洗澡睡觉!!!
所有源码已经上传至github上:https://github.com/ali-mayun/tomcat
简单实现"Tomcat"的更多相关文章
- JSP学习笔记(三):简单的Tomcat Web服务器
注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...
- 自己模拟的一个简单的tomcat
servlet容器的职责 总的来说,一个全功能的servlet容器会为servlet的每个HTTP请求做下面的一些工作: 1,当第一次调用servlet的时候,加载该servlet类并调用servle ...
- 实现一个简单的Tomcat
实现一个简单的Tomcat 1. Tomcat作用 我们的web应用会运行在Tomcat中,那么显然请求必定是先到达Tomcat的,Tomcat对于请求实际上会进行如下的处理: 提供Socket服务: ...
- 学完微型服务器(Tomcat)对其工作流程的理解,自己着手写个简单的tomcat
学完微型服务器(Tomcat)对其工作流程的理解,自己着手写个简单的tomcat 2019-05-09 19:28:42 注:项目(MyEclipse)创建的时候选择:Web Service Pr ...
- 手动实现简单的tomcat服务器
手动实现tomcat服务器的流程: 分析具体的实现步骤: 1,浏览器和后端服务如何实现通信,首先传输的数据要遵循http协议,通过tcp也就是我们常说的套接字编程来实现,具体的底层数据传输肯定就是我们 ...
- nginx简单学习(tomcat)
一.负载均衡的简单配置 1.下载nginx 2.tomcat*2 配置不同的端口用于正常启动,在jsp中<%= session.getId()%>可以查看jSessionId,tomcat ...
- 简单了解Tomcat与OSGi的类加载器架构
前言: 本次博客主要是对Tomcat与OSGi的类加载器架构,所以就需要对tomcat.OSGi以及类加载机制有所了解 类加载可以在http://www.cnblogs.com/ghoster/p/7 ...
- java实现一个最简单的tomcat服务
在了解tomcat的基本原理之前,首先要了解tomcatt最基本的运行原理. 1.如何启动? main方法是程序的入口,tomcat也不例外,查看tomcat源码,发现main是在Bootstrap ...
- java架构之路-(tomcat网络模型)简单聊聊tomcat(二)
上节课我们说到的Tomcat,并且给予了一般的tomcat配置,和配置的作用,提到了HTTP/1.1 也就是我们的网络通讯模型,那么HTTP/1.1又代表什么呢.我们来简答看一下. tomcat有四种 ...
随机推荐
- ora-12899解决方法
在使用ORACLE的过程中,会出现各种各样的问题,各种各样的错误,其中ORA-12899就是前段时间我在将数据导入到我本地机器上的时候一直出现的问题.不过还好已经解决了这个问题,现在分享一下,解决方案 ...
- synchronized细节问题(二)
使用synchronized声明的方法在某些情况下是有弊端的,比如A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下,可以使用synchronized代 ...
- [Codeforces_713A]Sonya and Queries
题目链接 http://codeforces.com/problemset/problem/713/A 题意 三种操作: + ai 集合里加一个整数ai,相同数记为多个. - ai 集合里减一个 ...
- Delphi: TLabel设置EllipsisPosition属性用...显示过长文本时,以Hint显示其全文本
仍然是处理多语言中碰到问题. Delphi自2006版以后,TLabel有了EllipsisPosition属性,当长文本超过其大小时,显示以...,如下图: 这样虽然解决显示问题,但很显然,不知道. ...
- tcp中的keepalive(转)
理解Keepalive(1) 大家都听过keepalive,但是其实对于keepalive这个词还是很晦涩的,至少我一直都只知道一个大概,直到之前排查线上一些问题,发现keepalive还是有很多玄机 ...
- idea不识别yml配置文件,怎么办?
问题描述: 如下图,新建的springboot项目,添加了自定义的配置文件后,2.yml无法像上方文件的一样,被识别成配置文件! 虽然可能不会影响项目(不确定),但问题不解决,根本没有心情开始下一 ...
- Bootstrap(8) 路径分页标签和徽章组件
一.路径组件路径组件也叫做面包屑导航.//面包屑导航 <ol class="breadcrumb"> <li><a href="#" ...
- 【RabbitMQ】——5种队列(转)
原文地址:https://blog.csdn.net/u012654963/article/details/76417613 应用RabbitMQ,我们可以根据需求选择5种队列之一. 一.简单队列 P ...
- docker搭建lnmp(二)
上一篇利用 不同的命令来构建 nginx,mysql,php镜像 和 容器. 这样做比较麻烦,也很容易出错,当然可以写入 sh脚本来执行.但是可以通过 docker-compose 来达到效果,管理起 ...
- Spring事务<tx:annotation-driven/>的理解
在使用Spring的时候,配置文件中我们经常看到 annotation-driven 这样的注解,其含义就是支持注解,一般根据前缀 tx.mvc 等也能很直白的理解出来分别的作用. <tx:an ...