从零开始写一个Tomcat(叁)--请求解析
挖坑挖了这么长时间也该继续填坑了,上文书讲到从零开始写一个Tomcat(贰)--建立动态服务器,讲了如何让服务器解析请求,分离servlet请求和静态资源请求,读取静态资源文件输出或是通过URLClassLoader找到我们请求的servlet,反射生成对应的实例,调用其service方法,传递初级解析的request和response,完成请求.
这很tomcat,but too simple
阅读本文,你将了解
- 连接器(connector),处理器(processor)逻辑分离
- 如何高效的解析请求中的header,parameters
- 生成更加完善的ServletRequest和ServletResponse
- Tomcat经典的facade模式
本章的代码部分是自己写的,更多的是从Tomcat源码上扒下来的
HttpConnector / HttpProcessor
之前的AsServer的主类中实现了所有接收请求,解析request,生成response,调用ServletProcessor的方法.我们首先将逻辑分离.
HttpConnector只负责接收请求,将SocketServer调用accept方法获得的socket传递给processor实例,本次请求中HttpConnector的工作结束,接下来的任务由HttpProcessor继续
HttpProcessor总体的逻辑和之前没有大的变化,基本上就是创建Request,创建Response,解析request,解析header,判断请求的是静态资源还是动态资源,并交给相应的处理器处理.
public void process(Socket socket){
SocketInputStream is;
OutputStream os;
try{
is=new SocketInputStream(socket.getInputStream(),2048);
os=socket.getOutputStream();
request = new HttpRequest(is);
response = new HttpResponse(os);
response.setRequest(request);
response.setHeader("Server", "Container");
parseRequest(is, os);
parseHeaders(is);
if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}
else {
response.getWriter().write("no servlet");
((HttpResponse) response).finishResponse();
}
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
新加入SocketInputStream类,SocketInputStream中主要有2个方法
public void readRequestLine(HttpRequestLine requestLine) throws IOException public void readHeader(HttpHeader header) throws IOException
readRequestLine负责读取request的第一行,分别得到method,path和protocol
之前的方法也能解析出这些信息,为什么还要额外去解析呢,因为效率.
之前的方法是读取出全部流的字节码,转换成字符串,再用indexOf和substring等方法解析,而SocketInputStream并不会去把得到的字节码装换成字符串而是保持字节码的形态,逐位的读取字节码,转化成char,填充到method,path和protocol对应的数组中,最后从method,path和protocol对应的数组中得到其对应的字符串.readHeader中也是相似的方法.节省了字符串的"繁重"的操作.
解析得到各项信息之后开始填充ServletRequest接口中的各种方法,为了加快速度,对于请求的各种参数,并不是在生成request的过程中,而是发生在第一次调用时
public String getParameter(String name) {
parseParameters();
String values[] = (String[]) parameters.get(name);
if (values != null)
return (values[0]);
else
return (null);
}
然后生成response,填充了部分新的方法,就不一一列举了,值得一提的是,为了保证response.getWriter().write/print/println能正确的写出,创建了一个ResponseWriter,对于PrintWriter的写的方法之前全加上了一层flush(),就像这样,并且在最后调用finishResponse()保证全部刷出以及关闭writer
public void print(char c) {
super.print(c);
super.flush();
}
public void print(char ca[]) {
super.print(ca);
super.flush();
}
public void print(double d) {
super.print(d);
super.flush();
}
现在得到生成的HttpRequest和HttpResponse,但是并不能直接的将这个传递给servlet,里面有属于Tomcat自身的方法,tomcat并不希望开发者去调用这些方法,因此使用了外观模式,
新建HttpRequestFacade和HttpResponseFacade,实现ServletRequest和ServletRespons接口,将HttpRequest和HttpResponse传递进来,仅实现HttpRequest和HttpResponse中属于ServletRequest和ServletRespons接口的方法,这样servlet就只能调用这些方法了.
源码地址:https://github.com/Asens/AsServer/tree/master/AsServerV1.1
附上测试:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
String aa=req.getParameter("username");
System.out.println(aa);
res.getWriter().write(aa);
}

本来准备把多线程也写进来的,但是多线程这个概念相对独立,与之前的内容关系不大,就下一章再写吧,demo已经写好了,有兴趣的同学可以先看一下
https://github.com/Asens/AsServer/tree/master/AsServerThreadDemo
下集预告:引入线程池和容器的基础概念,服务器能够同时处理多个请求
从零开始写一个Tomcat(叁)--请求解析的更多相关文章
- 从零开始写一个Tomcat(壹)
Tomcat是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,也是一个servlet容器的优秀解决方案,做Java web开发的基本上都使用过,但是tomcat大多时间对于我们是一个黑 ...
- 从零开始写一个Tomcat(贰)--建立动态服务器
上文书说道如何通过http协议建立一个静态的服务器来访问静态网页,但我们选择tomcat最主要的原因还是因为它能动态的执行servlet,这边文章将引导你实现一个能够运行servlet的服务器,这个简 ...
- 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise
本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...
- 深入浅出React Native 3: 从零开始写一个Hello World
这是深入浅出React Native的第三篇文章. 1. 环境配置 2. 我的第一个应用 将index.ios.js中的代码全部删掉,为什么要删掉呢?因为我们准备从零开始写一个应用~学习技术最好的方式 ...
- 从零开始写一个武侠冒险游戏-8-用GPU提升性能(3)
从零开始写一个武侠冒险游戏-8-用GPU提升性能(3) ----解决因绘制雷达图导致的帧速下降问题 作者:FreeBlues 修订记录 2016.06.23 初稿完成. 2016.08.07 增加对 ...
- 从零开始写一个武侠冒险游戏-7-用GPU提升性能(2)
从零开始写一个武侠冒险游戏-7-用GPU提升性能(2) ----把地图处理放在GPU上 作者:FreeBlues 修订记录 2016.06.21 初稿完成. 2016.08.06 增加对 XCode ...
- 从零开始写一个武侠冒险游戏-6-用GPU提升性能(1)
从零开始写一个武侠冒险游戏-6-用GPU提升性能(1) ----把帧动画的实现放在GPU上 作者:FreeBlues 修订记录 2016.06.19 初稿完成. 2016.08.05 增加对 XCod ...
- 使用PHP文件锁写一个多个请求同时并发写入一个文件,要求不脏读、数据不丢失
使用PHP文件锁写一个多个请求同时并发写入一个文件,要求不脏读.数据不丢失. //并发文件操作 function filehandle($filename,$data){ $start = 0; $e ...
- 一起学习造轮子(二):从零开始写一个Redux
本文是一起学习造轮子系列的第二篇,本篇我们将从零开始写一个小巧完整的Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Red ...
随机推荐
- Javascript闭包函数快速上手
闭包函数是什么?在开始学习的闭包的时候,大家很能都比较难理解.就从他的官方解释来说,都是比较概念化的. 不过我们也还是从闭包的含义出发. 闭包是指函数有自由独立的变量.换句话说,定义在闭包中的函数可以 ...
- 微信开放平台获取component_verify_ticket
官方文档说明: 在公众号第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket.第三方平台方在收到ticket推送后也需进行 ...
- DEDE更改版权信息
DEDECMSV5.7版本出现后,在前台网页底部会出现织梦版权信息 “powered by dedecms”,很多人都不知道怎么去掉 1. 方法一: 在include/dedesql.classs. ...
- php——composer 1、安装使用
Composer 是PHP中用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer会帮你安装这些依赖的库文件. 系统要求 运 ...
- linux系统删除空间后系统分区空间仍不释放问题
总结的原因: 1.删除文件文件后没有清空回收站; 2.删除的文件不在系统分区,在其他分区上; 3.删除的文件被保留在了/tmp分区下,而/tmp分区不是独立的分区,是在根分区/的基础上划分出来的分区; ...
- bzoj2215: [Poi2011]Conspiracy
Description Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动.国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是 ...
- Objective-C基础 便利构造器 单例模式1-17
Objective-C基础 便利构造器 单例模式1-17 便利构造器 单例模式 1.在声明时指定setter或getter方法,则用点运算符方法调用时默认调用的就是自己指定的方法2.单例:唯一性,如: ...
- STM32F10x 学习笔记5(USART实现串口通讯 1)
STM32F10x 系列单片机中都包含了USART 模块,所谓USART,就是通用同步异步收发器.通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间 ...
- SaaS是个不错的生意模式和创业的选择(独立SAAS厂商的三大优势)
1. 软件独特而又强大,界面绝美2. 数据存在本国3. 数据不留底 4. 可随时寻求卖身或者合作,不受制于人 --------------------------------------------- ...
- C++中强制变换之const_cast
今天学习了一下C++中的强制转换,看了const_cast,我发现了这个转换关键字的奇怪之处,于是把它记录一下,废话不说,先看一个程序: #include <iostream> using ...