简单实现"Tomcat"
Posted 阿里-马云的学习笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单实现"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"的主要内容,如果未能解决你的问题,请参考以下文章
[原创][Synth 8-2543] port connections cannot be mixed ordered and named ["*_Top.v":1151](代码片
nginx: [error] open() "/var/run/nginx/nginx.pid" failed (2: No such file or directory)(代码片
ie 下面 js动态设置 iframe src 不显示,一片空白
Apache Tomcat/Jboss远程代码执行漏洞 怎样解决
startup.bat运行闪推 加了pause看到的是下列代码 Using CATALINA_BASE: "D:\tomcat"