前述

  自己手写的简易的tomcat,实现了tomcat的基本响应功能,项目代码已经上传到我的Github,刚刚开始学习这里,当前还存在很多问题

项目简述及代码

  当我们的Web运行的时候,从浏览器发出的请求,必然首先到达tomcat中,之后由tomcat进行处理,由此要考虑tomcat要进行哪些处理,首先便是提供Socket服务,之后对于请求进行分发,把请求和产生的响应封装成request和response

  (1)提供Socket服务

  (2)封装请求/响应对象

  (3)将不同的请求映射到具体的Servlet处理

处理请求

  我们首先考虑的,是客户端发送来请求时,我们应该怎么去识别它,这里涉及到的就是HTTP请求协议的部分,我直接那Github页面的HTTP请求协议做例子来说,如下图

  我们可以看到,在Request头的首行,由 GET  /jyroy  HTTP/1.1 三部分构成,而这三部分分别的含义是 请求方法  请求路径  请求协议及其对应版本号

  我们在拿到Resquest请求之后根据上面的分析,拿到相应的信息就可以进行后续的处理了。

 package myTomcat;

 import java.io.IOException;
import java.io.InputStream; /**
* @author jyroy
*
*/
public class MyRequest { //请求路径
private String url;
//请求方法
private String method; //读取输入字节流,封装成字符串格式的请求内容
public MyRequest(InputStream inputStream) throws IOException{
String httpRequest = ""; byte[] httpRequestBytes = new byte[1024]; int length = 0; if((length = inputStream.read(httpRequestBytes)) > 0) {
httpRequest = new String(httpRequestBytes, 0, length);
}
//HTTP请求协议:首行的内容依次为:请求方法、请求路径以及请求协议及其对应版本号
// GET /index HTTP/1.1
String httpHead = httpRequest.split("\n")[0]; //取出HTTP请求协议的首行
System.out.println(httpHead);
method = httpHead.split("\\s")[0]; //按照空格进行分割,第一个是请求的方法
url = httpHead.split("\\s")[1]; //按照空格进行分割,第二个是请求的路径
System.out.println(this.toString());
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public String getMethod() {
return method;
} public void setMethod(String method) {
this.method = method;
} @Override
public String toString() {
return "MyRequest [url=" + url + ", method=" + method + "]";
} }

处理响应

  考虑完接受请求之后,我们再来考虑一下怎么来做出我们的响应,浏览器才能识别,这里要涉及到的就是HTTP响应报文的内容,我的思路是,利用字符串拼接出Response报文,再将String转换为字节流就可以了。

  我们也是来看一下Github的Response报文的格式,如下图

  这么多的响应头,其实不是全部需要的,我们只需要写入一些基本的必须响应头信息,例如 请求协议及其对应版本号  响应号 响应状态 和 Cotent-type 等,如下

  最后只要转化字节流就可以

  

 package myTomcat;

 import java.io.IOException;
import java.io.OutputStream; public class MyResponse {
private OutputStream outputStream; public MyResponse(OutputStream outputStream) {
this.outputStream = outputStream;
} //将文本转换为字节流
public void write(String content) throws IOException{
StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n") //按照HTTP响应报文的格式写入
.append("Content-Type:text/html\n")
.append("\r\n")
.append("<html><head><link rel=\"icon\" href=\"data:;base64,=\"></head><body>")
.append(content) //将页面内容写入
.append("</body></html>");
outputStream.write(httpResponse.toString().getBytes()); //将文本转为字节流
outputStream.close();
} }

Servlet请求处理基类

  当我们的请求和响应都已经准备好之后,接下来考虑servlet请求处理的部分,tomcat本身是一种满足servlet规范的容器,我们需要识别接收到的请求之后并做出响应,就涉及到了 doGet  doPost  service 三个方法

 package myTomcat;

 /**
* @author jyroy
* 提供API:doGet doPost service 方法
*/
public abstract class MyServlet { public void service(MyRequest myRequest, MyResponse myResponse) {
if(myRequest.getMethod().equalsIgnoreCase("POST")) {
doPost(myRequest, myResponse);
}else if(myRequest.getMethod().equalsIgnoreCase("GET")) {
doGet(myRequest, myResponse);
}
} public void doGet(MyRequest myRequest, MyResponse myResponse) { } public void doPost(MyRequest myRequest, MyResponse myResponse) { } }

Servlet配置

  考虑完上述问题之后,下一步需要的是分配url给哪一个servlet来处理,首先需要的就是一个反应映射关系的类

  

 package myTomcat;

 public class ServletMapping {
private String servletName;
private String url;
private String clazz; public ServletMapping(String servletName, String url, String clazz) {
super();
this.servletName = servletName;
this.url = url;
this.clazz = clazz;
} public String getServletName() {
return servletName;
} public void setServeletName(String servletName) {
this.servletName = servletName;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public String getClazz() {
return clazz;
} public void setClazz(String clazz) {
this.clazz = clazz;
}
}

  以及相关配置文件

 package myTomcat;

 import java.util.ArrayList;
import java.util.List; /**
* @author jyroy
*
*/
public class ServletMappingConfig {
public static List<ServletMapping> servletMappingList = new ArrayList<>(); static {
servletMappingList.add(new ServletMapping("index", "/index", "myTomcat.test.IndexServlet"));
servletMappingList.add(new ServletMapping("myblog", "/myblog", "myTomcat.test.MyBlog"));
}
}

核心类

  最终,我们准备好基类后,需要的就是实现开始提到的整个处理流程

  (1)提供Socket服务

  (2)封装请求/响应对象

  (3)将不同的请求映射到具体的Servlet处理

  这里重点说的是,要利用 ServerSocket 通过服务器上的端口通信 以及 accpt方法一直等待客户端的请求

  具体逻辑在代码中注释

 package myTomcat;

 import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.net.Socket; /**
* @author jyroy
* Tomcat的处理流程:把URL对应处理的Servlet关系形成,解析HTTP协议,封装请求/响应对象,
* 利用反射实例化具体的Servlet进行处理即可。
*/
public class MyTomcat {
private Integer port = 8080; //定义8080端口 private Map<String, String> urlServletMapping = new HashMap<>(); //存储url和对应的类 public MyTomcat(Integer port) {
super();
this.port = port;
} @SuppressWarnings("resource")
public void start() {
initServletMapping(); try {
ServerSocket serverSocket = null; //实例化一个 ServerSocket 对象,表示通过服务器上的端口通信
serverSocket = new ServerSocket(port);
System.out.println("MyTomcat is starting...");
while(true) {
Socket socket = serverSocket.accept(); //服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream(); MyRequest myRequest = new MyRequest(inputStream);
MyResponse myResponse = new MyResponse (outputStream); dispatch(myRequest, myResponse); socket.close();
}
}catch(Exception e) {
e.printStackTrace();
} // }finally {
// if(serverSocket != null) {
// try {
// serverSocket.close();
// }catch(Exception e){
// e.printStackTrace();
// }
// }
// }
} //初始化映射
public void initServletMapping() {
for(ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
urlServletMapping.put(servletMapping.getUrl(), servletMapping.getClazz());
}
} //分发请求
@SuppressWarnings("unchecked")
public void dispatch(MyRequest myRequest, MyResponse myResponse) {
String clazz = urlServletMapping.get(myRequest.getUrl()); try {
Class<MyServlet> myServletClass = (Class<MyServlet>)Class.forName(clazz);
MyServlet myservlet = myServletClass.newInstance();
myservlet.service(myRequest, myResponse);
}catch(ClassNotFoundException e) {
e.printStackTrace();
}catch(InstantiationException e) {
e.printStackTrace();
}catch(IllegalAccessException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
MyTomcat myTomcat = new MyTomcat(8080);
myTomcat.start();
} }

测试类

 package myTomcat.test;

 import java.io.IOException;

 import myTomcat.MyRequest;
import myTomcat.MyResponse;
import myTomcat.MyServlet; public class IndexServlet extends MyServlet {
@Override
public void doGet(MyRequest myRequest, MyResponse myResponse) {
try {
myResponse.write("Hello, myTomcat");
} catch (IOException e) {
e.printStackTrace();
}
} @Override
public void doPost(MyRequest myRequest, MyResponse myResponse) {
try {
myResponse.write("Hello, myTomcat");
} catch (IOException e) {
e.printStackTrace();
}
}
}
 package myTomcat.test;

 import java.io.IOException;

 import myTomcat.MyRequest;
import myTomcat.MyResponse;
import myTomcat.MyServlet; public class MyBlog extends MyServlet {
@Override
public void doGet(MyRequest myRequest, MyResponse myResponse) {
try {
myResponse.write("Hello, this is my blog");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest, MyResponse myResponse) {
try {
myResponse.write("Hello, this is my blog");
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果

我手写的简易tomcat的更多相关文章

  1. 来,我们手写一个简易版的mock.js吧(模拟fetch && Ajax请求)

    预期的mock的使用方式 首先我们从使用的角度出发,思考编码过程 M1. 通过配置文件配置url和response M2. 自动检测环境为开发环境时启动Mock.js M3. mock代码能直接覆盖g ...

  2. 手写Promise简易版

    话不多说,直接上代码 通过ES5的模块化封装,向外暴露一个属性 (function(window){ const PENDING = 'pending'; const RESOLVED = 'fulf ...

  3. 手写一个简易版Tomcat

    前言 Tomcat Write MyTomcat Tomcat是非常流行的Web Server,它还是一个满足Servlet规范的容器.那么想一想,Tomcat和我们的Web应用是什么关系? 从感性上 ...

  4. 用python 10min手写一个简易的实时内存监控系统

    简易的内存监控系统 本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客 文章github源地址,还可以看到具体的代码,喜欢请在原链接右上角加个star 腾讯视频链接 ...

  5. [转]用python 10min手写一个简易的实时内存监控系统

    简易的内存监控系统 本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客 文章github源地址,还可以看到具体的代码,喜欢请在原链接右上角加个star 腾讯视频链接 ...

  6. 手写一个简化版Tomcat

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

  7. 手写一个简易的IOC

    这个小项目是我读过一点Spring的源码后,模仿Spring的IOC写的一个简易的IOC,当然Spring的在天上,我写的在马里亚纳海沟,哈哈 感兴趣的小伙伴可以去我的github拉取代码看着玩 地址 ...

  8. 如何手写实现简易的Dubbo[z]

    [z]https://juejin.im/post/5ccf8dec6fb9a0321c45ebb5 前言 结束了集群容错和服务发布原理这两个小专题之后,有朋友问我服务引用什么时候开始,本篇为服务引用 ...

  9. 手写一个简易的多周期 MIPS CPU

    一点前言 多周期 CPU 相比单周期 CPU 以及流水线 CPU 实现来说其实写起来要麻烦那么一些,但是相对于流水线 CPU 和单周期 CPU 而言,多周期 CPU 除了能提升主频之外似乎并没有什么卵 ...

随机推荐

  1. Angular HttpClient upload file with FormData

    从sof上找到一个example:https://stackoverflow.com/questions/46206643/asp-net-core-2-0-and-angular-4-3-file- ...

  2. jtds驱动更新对一个老问题的解决

    07年年末的一篇blog: 以前网站做初期开发时,有一个问题:hibernate下text大字符串读取时出这个异常:JDBCExceptionReporter - The amount of data ...

  3. 使用Glide以及OkHttp集成

    1.glide的使用: 添加依赖: compile 'com.github.bumptech.glide:glide:3.7.0' 调用代码: ImageView imageView = (Image ...

  4. linux环境安装svn并进行多个源码库区分管理

    关于svn的文档有很多大部分已Windows为例子,因公司没有Windows服务器经过一天的曲折终于初步安装了解了svn.下面一些经验希望能帮助新手 本文采用的yum安装(简单快速没必要源码) 1.y ...

  5. php仿经典省市县三级联动

    之前有个需求要写个类似省市县三级联动的页面,于是,网上找了点资料看了下,其实原理很简单: 当我们选择一级栏目中某条记录的时候,会获取该栏目的vaule值,并发起ajax请求,后台根据这个vaule值, ...

  6. redis安装、使用

    官网:http://redis.io/ github地址:https://github.com/antirez/redis 简介:         redis是一个key-value存储系统.和Mem ...

  7. Selenium2Lib库之键盘常用关键字实战

    Press Key关键字 按F5 查看Press Key关键字的说明,如下图: Press Key关键字是用于通过键盘模拟由定位器确定的元素的用户按键.‘值’是单个字符,字符串或数值的ASCII码的“ ...

  8. The JRE_HOME environment variable is not defined correctly

    启动Tomcat后startup.bat脚本调用了catalina.bat,然后catalina.bat调用了setclasspath.bat,setclasspath.bat的头部定义了JAVA_H ...

  9. SpringMVC中Controller的方法返回值

    1. 返回ModelAndView对象 controller方法中定义ModelAndView对象并返回,对象中可添加model数据.指定view. 实例 @RequestMapping(" ...

  10. Kali Linux远程连接Windows服务器

    前言: 为了在Kali上远程连接Windows系统的服务器我们需要安装两个工具,rdesktop和tsclient.另外,我们从主机服务商那里购买的Windows操作系统的服务器都是默认开启了远程连接 ...