简单实现"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有四种 ...
随机推荐
- Gym - 101243F Vitamins(思维+并查集)
题意 有三种药丸,白色W>红色R>蓝色B,给你m个约束条件,问你n个药丸的颜色,不能确定颜色输出‘?’ 题解 如果1<2<3,只要找到2就能确定1和3的颜色 如果2=4,只要确 ...
- 大数据分析界的“神兽”Apache Kylin有多牛?【转】
本文作者:李栋,来自Kyligence公司,也是Apache Kylin Committer & PMC member,在加入Kyligence之前曾就职于eBay.微软. 1.Apache ...
- day 09 函数的进阶
01 动态参数 *args **kwargs 在函数的定义时,* ** 代表聚合. def func(**kwargs): print(kwargs) func(**{"name" ...
- 15-算法训练 P1103
http://lx.lanqiao.cn/problem.page?gpid=T372 算法训练 P1103 时间限制:1.0s 内存限制:256.0MB 编程实现两个复数的运算 ...
- Selenium + Python + Firefox
按网上教程搭建好环境后,执行下面的代码出现了错误: 测试代码如下: from selenium import webdriver driver=webdriver.Firefox() driver.g ...
- 关于变量参数的传递,python让人蛋痛的地方
def find_file(file_table): with open(file_table, 'r', encoding='utf-8') as read_f: line_dict = {} fo ...
- 【APT】SqlServer游标使用
use [ElephantCredit] go begin transaction tran_bank; print '**脚本开始执行!'; declare @tran_error int , @n ...
- VS2015中C#版本6.0的新特性
[z]http://www.cnblogs.com/xszjk/articles/6417173.html [z]https://www.cnblogs.com/qixu/p/6047229.html ...
- 安装scrapy 出现error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools 错误
安装scrapy 出现以下 错误: error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C ...
- 4A - 排序
输入一行数字,如果我们把这行数字中的‘5’都看成空格,那么就得到一行用空格分割的若干非负整数(可能有些整数以‘0’开头,这些头部的‘0’应该被忽略掉,除非这个整数就是由若干个‘0’组成的,这时这个整数 ...