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类. 理论上,两个类可以和到一起,但是为了支持多种运行模式,又把他们分开了. 为了让 ...
随机推荐
- 软件测试之BUG分析定位概述(QA如何分析定位BUG)
你是否遇到这样的场景? QA发现问题后找到DEV说: 不好了,你的程序出问题了! DEV(追查半小时之后): 唉,是你们测试环境配置的问题 唉,是你们数据不一致 唉,是你们**程序版本不对 唉,是** ...
- SQL Server 扩展事件(Extented Events)从入门到进阶(2)——在GUI中创建基础扩展事件
本文属于 SQL Server 扩展事件(Extented Events)从入门到进阶 系列 第一篇文章中提到了如何在Profiler中创建跟踪(trace),并以服务器端(server-side)跟 ...
- Dynamics CRM2016 时间字段属性中的新增行为
之前的博客中有特地介绍过CRM中的时间字段以及它在不同的应用场景中涉及的时制转换,而CRM2016又给时间字段添加了新的行为,具体见下属截图,简单介绍下每个图中对应的行为的意思,最后会做demo来具体 ...
- 【SSH系列】---Hibernate的基本映射
开篇前言 在前面的博文中,小编分别介绍了[SSH系列]-- hibernate基本原理&&入门demo,通过这篇博文,小伙伴们对hibernate已经有了基本的了解,以及h ...
- 反射模拟DbUtils实现ResultSet转成Bean实例
前几天接触到了apache的一个小框架DbUtils,真的被其优雅的设计所震撼到了,尤其是其中的 MyBean mybean = QueryRunner.query(sqlConnection,sql ...
- Linux之dmesg命令
功能说明:显示内核缓冲区系统控制信息的工具 ,比如系统在启动时的信息会写到/var/log/中.语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存 ...
- Spark技术内幕: Shuffle详解(二)
本文主要关注ShuffledRDD的Shuffle Read是如何从其他的node上读取数据的. 上文讲到了获取如何获取的策略都在org.apache.spark.storage.BlockFetch ...
- 【NPR】卡通渲染
写在前面 我的博客讲过好几篇卡通渲染了,比如[Unity Shader实战]卡通风格的Shader(一).[Unity Shader实战]卡通风格的Shader(二).[NPR]漫谈轮廓线的渲染.[S ...
- Hadoop:hadoop fs、hadoop dfs与hdfs dfs命令的区别
http://blog.csdn.net/pipisorry/article/details/51340838 'Hadoop DFS'和'Hadoop FS'的区别 While exploring ...
- Mysql SQL Mode详解
Mysql SQL Mode简介 MySQL服务器能够工作在不同的SQL模式下,并能针对不同的客户端以不同的方式应用这些模式.这样,应用程序就能对服务器操作进行量身定制以满足自己的需求.这类模式定义了 ...