How Tomcat Works读书笔记三-------连接器
几个概念
HttpServlet,Servlet
Servlet是一个接口,定义了一种网络服务,我们所有的servlet都要实现它(或它的子类)
HttpServlet是一个抽象类,它针对的就是http网络服务
当然如果以后再有其他的网络服务,可以再定义一个类,让它实现Servlet即可。
HttpServletRequest,ServletRequest
先说ServletRequest接口,它用来传递网络服务的请求,用在Servlet类的service方法。
那么HttpServletRequest接口,主要针对的就是http请求。
HttpServletRequest比ServletRequest多了一些针对于Http协议的方法。如getHeader,getMethod,getSession等。
StringManager
对于一个大型的系统,任何时候都要小心的处理错误信息。如果将所有的错误信息都保存在一个properties文件中,会无比繁杂,因此tomcat为系统里面的每一个包都建立一组properties文件(是一组 而不是一个 是为了支持多语言 LocalStrings.properties,LocalStrings_es.properties,LocalStrings_ja.properties就分别是英语 西班牙语 日语)
例如在ex03.pyrmont.connector.http包下的HttpProcessor类中 我们引用StringManager 如下
protected StringManager sm =
StringManager.getManager("ex03.pyrmont.connector.http");
在要抛出异常的地方使用
throw new ServletException
(sm.getString("httpProcessor.parseHeaders.colon"));
现在我们看看httpProcessor.parseHeaders.colon包下的properties文件(部分)
httpProcessor.parseHeaders.colon=Invalid HTTP header format
httpProcessor.starting=Starting background thread
httpProcessor.stopping=Stopping background thread
requestStream.close.closed=Request stream has already been closed
这下知道StringManager的功能了吧。
另外,StringManager的产生是单例模式,大家可以看看源码
lozy load
在本章,http请求对象使用httprequst来表示(jdk中并不存在这个类 我们自己写的),它实现了HttpServletRequest接口。
在使用的时候httprequst会被转型成HttpServletRequest,并作为servlet的service方法的参数而调用。
所以我们得设置每个httprequest实例的成员变量供servlet实例使用,需要设定的值包括uri,查询字符串,参数,Cookie等等
因而,这里就存在一个问题,解析http的请求体(注意是请求体 请求方法get/post那一行和请求头会全部解析)涉及很多字符串操作,如果我们只解析会用到的值就能节省很多cpu周期。
因此只有当我们调用javax.servlet.http.HttpServletRequest的getParameter(),getParameterMap(),getParameter-Names(),getParameterValues()时才会解析请求体(并且即使多次调用那个四个方法 也只解析一次)!
具体做法就是在HttpRequest类(我们自己写的那个实现了HttpServletRequest接口的那个)里上述四个方法里面第一行代码就调用一个parseParameters()方法。parsed的作用就是保证parseParameters只运行一次
(另外我觉得请求字符串应该就是请求体的一部分)
第二章帮我们建立了一个简单的servlet容器,它可以加载静态资源,还可以处理一些简单的servlet。在这一章里,我们要进一步完善我们的容器,让它能取得request请求的一些参数。
因此我们这一节的servlet就会继承HttpServlet,传递的参数也会是HttpServletRequest。
应用代码
我们这一章的servlet代码如下
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class ModernServlet extends HttpServlet {
public void init(ServletConfig config) {
System.out.println("ModernServlet -- init");
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Modern Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h2>Headers</h2");
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
out.println("<br>" + header + " : " + request.getHeader(header));
}
//省略部分request信息
out.println("</body>");
out.println("</html>");
}
}
首先看看UML图
系统提炼出来了一个Bootstrap类,加载HttpConnector类,调用其start()方法
package ex03.pyrmont.connector.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpConnector implements Runnable {
boolean stopped;
private String scheme = "http";
public String getScheme() {
return scheme;
}
public void run() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
}
catch (Exception e) {
continue;
}
// Hand this socket off to an HttpProcessor
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
}
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
}
HttpConnector的run方法其实与书的第一章的HttpServer1类似,只是第一章中获得inputstream后,就让他生成request了,而在这里,我们创建了httpprocessor,并调用其process方法将serverSocket.accept()的socket传入。
HttpProcessor的process方法如下
public void process(Socket socket) {
SocketInputStream input = null;
OutputStream output = null;
try {
input = new SocketInputStream(socket.getInputStream(), 2048);
output = socket.getOutputStream();
// create HttpRequest object and parse
request = new HttpRequest(input);
// create HttpResponse object
response = new HttpResponse(output);
response.setRequest(request);
response.setHeader("Server", "Pyrmont Servlet Container ***");
parseRequest(input, output);
parseHeaders(input);
//check if this is a request for a servlet or a static resource
//a request for a servlet begins with "/servlet/"
if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
// Close the socket
socket.close();
// no shutdown for this application
}
catch (Exception e) {
e.printStackTrace();
}
}
看这个一行代码
input = new SocketInputStream(socket.getInputStream(), 2048);
SocketInputStream是一个实现了InputStream接口的包装类,我们之所以用SocketInputStream,就是为了用它的readRequestLine()方法readHeader方法。前者能读出request请求的请求方法,uri和请求协议,后者能读出请求头。
构建HttpRequest
parseRequest(input, output);
parseHeaders(input);
这两个方法是process方法的核心,也可以说是整个第三章的核心。往简单的说,这两个方法干的事就是解析http请求填充HttpRequst对象,当然这里面有很多很多细节的东西。
这部分说简单很简单,说麻烦也巨麻烦,大体来说就是五个部分
1读取套接字的输入流
就是下面这行代码
input = new SocketInputStream(socket.getInputStream(), 2048);
大家有兴趣可以看看readRequestLine的源代码,不过不必深究。
2解析请求行(就是 http请求的第一行 包括请求方法 uri 协议版本)
3解析请求头
4解析Cookie
5获取参数(就是前文提到的lazy load)
构建HttpResponse
public PrintWriter getWriter() throws IOException {
// autoflush is true, println() will flush,
// but print() will not.
writer = new PrintWriter(output, true);
return writer;
}
通过上面第二章的代码,我们知道writer是一个PrintWriter类的实例。
1 在本章,我们使用ResponseWriter来构建writer实例(ResponseWriter继承自PrintWriter)
2 第二章的output是OutputStream接口的实例,在第三章,我们换成ResponseStream(ResponseStream 继承 ServletOutputStream,而后者又实现了OutputStream接口)
3 同时还需要一个OutputStreamWriter作为ResponseWriter与ResponseStream之间的桥梁。
所以代码如下
public PrintWriter getWriter() throws IOException {
ResponseStream newStream = new ResponseStream(this); // this 指代HttpResopnse
newStream.setCommit(false);
OutputStreamWriter osr =
new OutputStreamWriter(newStream, getCharacterEncoding());
writer = new ResponseWriter(osr);
return writer;
}
静态资源处理器与servlet处理器与上一章的内容基本一致,不再赘述。
参考资料
http://zhidao.baidu.com/link?url=BGdS8iiz12FmAlVLsoGj6n3LwclvQ7rD-8GOJ6U8uguM8IGZrDUooTkjnySAInrMkqA6a7XofgNu4J3ynbOPC_
How Tomcat Works读书笔记三-------连接器的更多相关文章
- how tomcat works 读书笔记(二)----------一个简单的servlet容器
app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...
- how tomcat works 读书笔记四 tomcat的默认连接器
事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析tomcat4里面的默认连接器. 连接器 Tomcat连接器必须满足下面几个要求 1 实现org.apache ...
- How tomcat works 读书笔记十四 服务器组件和服务组件
之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...
- how tomcat works 读书笔记 十一 StandWrapper 上
方法调用序列 下图展示了方法调用的协作图: 这个是前面第五章里,我画的图: 我们再回顾一下自从连接器里 connector.getContainer().invoke(request, resp ...
- how tomcat works 读书笔记(一)----------一个简单的webserver
http协议 若是两个人能正常的说话交流,那么他们间必然有一套统一的语言规则<在网络上server与client能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertex ...
- how tomcat works 读书笔记九 Session管理
在看本文之前,请先查阅相关Session与Cookie的资料. 这篇资料不错 http://blog.csdn.net/fangaoxin/article/details/6952954 Catali ...
- how tomcat works 读书笔记(一)----------一个简单的web服务器
http协议 若是两个人能正常的说话交流,那么他们间必定有一套统一的语言规则<在网络上服务器与客户端能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertext tran ...
- How Tomcat Works 读书笔记 八 载入器 上
Java的类载入器 详细资料见 http://blog.csdn.net/dlf123321/article/details/39957175 http://blog.csdn.net/dlf1233 ...
- How tomcat works 读书笔记十七 启动tomcat 上
一路跋山涉水,这是最后一章了. 关于tomcat的启动,有两个类,一个是Catalina类,一个是Bootstrap类. 理论上,两个类可以和到一起,但是为了支持多种运行模式,又把他们分开了. 为了让 ...
随机推荐
- Redis集群功能预览
目前Redis Cluster仍处于Beta版本,Redis 3.0将会加入,在此可以先对其主要功能和原理进行一个预览.参考<Redis Cluster - a pragmatic approa ...
- Linux动态频率调节系统CPUFreq之三:governor
在上一篇文章中,介绍了cpufreq的core层,core提供了cpufreq系统的初始化,公共数据结构的建立以及对cpufreq中其它子部件提供注册功能.core的最核心功能是对policy的管理, ...
- SQL Server2012 AlwaysOn 无法将数据库联接到可用性组 针对主副本的连接未处于活动状态
在配置alwayson的可用性组时遇到如下截图中的错误,这里的服务器86是作为副本数据库服务器的. 解决该问题只需将SQL服务的运行账号改成管理员,并且打开防火墙中的5022端口(该端口号可在可用性组 ...
- request.setAttribute()怎么用的?
request.setAttribute()怎么用的? JSP1代码 String [] test=new String[2]; test[0]="1"; test[1]=&quo ...
- 发布一个参考ssdb,用go实现的类似redis的高性能nosql:ledisdb
起因 ledisdb是一个参考ssdb,采用go实现,底层基于leveldb,类似redis的高性能nosql数据库,提供了kv,list,hash以及zset数据结构的支持. 我们现在的应用极大的依 ...
- Freemarker中如何遍历List
Freemarker中如何遍历List(附源码) 关键词(Keyword):Freemarker,Freemarker遍历list 在Freemarker应用中经常会遍历List获取需要的数据, ...
- Android开发技巧——ViewPager衍生出来的2个类
1.不能左右滑动的ViewPager /* * Date: 14-7-28 * Project: Access-Control-V2 */ package cn.irains.access.v2.co ...
- UNIX环境高级编程——System V 共享内存区
共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最 ...
- JUI/DWZ 分页 Servlet
分页介绍 参考:官方用户手册中的"分页组件" 分页思路服务器返回当前页的数据,总条数,再由js来生成分页标签.分页是配合服务器端来处理的, 不是存js做的分页. 因 ...
- Linux之解决你的网络问题
在网络方面,Linux系统通常可以正常的工作,但是偶尔也会出现让人心烦一些的问题,下面就是一些网络问题的常用的解决方案. 如果你的网络接口看起来已经启动和运行,但是不能访问因特网,这时你就可以试试pi ...