深入剖析TOMCAT
1 Catalina模型
首先我们先了解一下tomcat的大致工作原理。tomcat的核心是servlet容器,我们称它为Catalina(为什么叫这个名字?我也不知道 ̄へ ̄)。模型图如1.1
图1.1
Connector是用来“连接”容器里边的请求的。它的工作是为接收到每一个 HTTP 请求构造一个 request 和 response 对象。然后它把流程传递给容器。容器从连接器接收到 requset 和 response 对象之后调用 servlet 的 service 方法用于响应。谨记,这个描述仅仅是冰山一角而已。这里容器做了相当多事情。例如,在它调用 servlet 的 service 方法之前,它必须加载这个 servlet,验证用户(假如需要的话),更新用户会话等等。以此为思路,我们就开始我们的构造http服务器之旅吧。
2 服务器搭建
首先我们明确一下我们的服务器的功能点。
1. 需要有一个类去接收http请求;
2. 需要一个自定义Request类和Response类,把接收到的请求构造成这两个类;
3. 根据请求的格式来确定处理方式:返回静态资源 or 进入Servlet ?
4. 需要一个Servlet类执行业务逻辑
UML图如下2.1
图2.1
2.1 HttpServer
首先构造HttpServer类
2
3
4 private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
5 private static boolean shutdown = false;
6
7 public static void main(String[] args) {
8 HttpServer server = new HttpServer();
9 server.await();
10 }
11
12 public static void await() {
13 ServerSocket serverSocket = null;
14 int port = 8080;
15 try {
16 serverSocket = new ServerSocket(port, 1,
17 InetAddress.getByName("127.0.0.1"));
18 } catch (IOException e) {
19 e.printStackTrace();
20 System.exit(1);
21 }
22 // Loop waiting for a request
23 while (!shutdown) {
24 Socket socket = null;
25 InputStream input = null;
26 OutputStream output = null;
27 try {
28 socket = serverSocket.accept();
29 input = socket.getInputStream();
30 output = socket.getOutputStream();
31 // create Request object and parse
32 Request request = new Request(input);
33 request.parseUrl();
34 // create Response object
35 Response response = new Response(output);
36 response.setRequest(request);
37
38 if (request.getUri().startsWith("/v2/")) {
39 ServletProcessor processor = new ServletProcessor();
40 processor.process(request, response);
41 }
42 else {
43 StaticResourceProcessor processor =
44 new StaticResourceProcessor();
45 processor.process(request, response);
46 }
47 // Close the socket
48 socket.close();
49 //check if the previous URI is a shutdown command
50 shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
51 } catch (Exception e) {
52 e.printStackTrace();
53 System.exit(1);
54 }
55 }
56 }
57 }
这里我们需要关注一下ServerSocket这个类的用法。Socket 类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候,你都会第一时间想到这个类。而ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服 务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。 ServletSocket套接字的其中一个构造函数为
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
port代表端口号,backLog代表这个套接字可支持的最大连接数量,bindingAddress代表服务器绑定的地址。一旦你有一个 ServerSocket 实例,你可以通过调用 ServerSocket 类的 accept 方法j。这个监听当前地址的当前端口上的请求,方法只会在有连接请求时才会返回,并且返回值是一个 Socket 类的实例。
2.2 Request Response
servlet 的 service 方法从 servlet 容器中接收一个 javax.servlet.ServletRequest 实例 和一个 javax.servlet.ServletResponse 实例。这就是说对于每一个 HTTP 请求,servlet 容器 必须构造一个 ServletRequest 对象和一个 ServletResponse 对象并把它们传递给正在服务的 servlet 的 service 方法。
2
3 private InputStream input;
4 private String uri;
5
6 public Request(InputStream input) {
7 this.input = input;
8 }
9
10 public String getUri(){
11 return uri;
12 }
13
14 public void parseUrl() {
15 StringBuffer request = new StringBuffer(2048);
16 int i;
17 byte[] buffer = new byte[2048];
18
19 try {
20 i = input.read(buffer);
21 } catch (IOException e) {
22 e.printStackTrace();
23 i = -1;
24 }
25
26 for (int j = 0; j < i; j++) {
27 request.append((char) buffer[j]);
28 }
29
30 System.out.print(request.toString());
31 uri = parseUri(request.toString());
32 }
33
34 private static String parseUri(String requestString) {
35 int index1, index2;
36 index1 = requestString.indexOf(' ');
37 if (index1 != -1) {
38 index2 = requestString.indexOf(' ', index1 + 1);
39 if (index2 > index1)
40 return requestString.substring(index1 + 1, index2);
41 }
42 return null;
43 }
44
45 @Override
46 public Object getAttribute(String name) {
47 return null;
48 }
49
50 @Override
51 public Enumeration getAttributeNames() {
52 return null;
53 }
54
55 @Override
56 public String getCharacterEncoding() {
57 return null;
58 }
59
60 @Override
61 public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
62
63 }
64
65 @Override
66 public int getContentLength() {
67 return 0;
68 }
69
70 @Override
71 public String getContentType() {
72 return null;
73 }
74
75 @Override
76 public ServletInputStream getInputStream() throws IOException {
77 return null;
78 }
79
80 @Override
81 public String getParameter(String name) {
82 return null;
83 }
84
85 @Override
86 public Enumeration getParameterNames() {
87 return null;
88 }
89
90 @Override
91 public String[] getParameterValues(String name) {
92 return new String[0];
93 }
94
95 @Override
96 public Map getParameterMap() {
97 return null;
98 }
99
100 @Override
101 public String getProtocol() {
102 return null;
103 }
104
105 @Override
106 public String getScheme() {
107 return null;
108 }
109
110 @Override
111 public String getServerName() {
112 return null;
113 }
114
115 @Override
116 public int getServerPort() {
117 return 0;
118 }
119
120 @Override
121 public BufferedReader getReader() throws IOException {
122 return null;
123 }
124
125 @Override
126 public String getRemoteAddr() {
127 return null;
128 }
129
130 @Override
131 public String getRemoteHost() {
132 return null;
133 }
134
135 @Override
136 public void setAttribute(String name, Object o) {
137
138 }
139
140 @Override
141 public void removeAttribute(String name) {
142
143 }
144
145 @Override
146 public Locale getLocale() {
147 return null;
148 }
149
150 @Override
151 public Enumeration getLocales() {
152 return null;
153 }
154
155 @Override
156 public boolean isSecure() {
157 return false;
158 }
159
160 @Override
161 public RequestDispatcher getRequestDispatcher(String path) {
162 return null;
163 }
164
165 @Override
166 public String getRealPath(String path) {
167 return null;
168 }
169
170 @Override
171 public int getRemotePort() {
172 return 0;
173 }
174
175 @Override
176 public String getLocalName() {
177 return null;
178 }
179
180 @Override
181 public String getLocalAddr() {
182 return null;
183 }
184
185 @Override
186 public int getLocalPort() {
187 return 0;
188 }
189 }
2
3 private static final int BUFFER_SIZE = 1024;
4 Request request;
5 OutputStream output;
6 PrintWriter writer;
7
8 public Response(OutputStream output) {
9 this.output = output;
10 }
11
12 public void setRequest(Request request) {
13 this.request = request;
14 }
15
16 public void sendStaticResource() throws IOException {
17 byte[] bytes = new byte[BUFFER_SIZE];
18 FileInputStream fis = null;
19 try {
20 File file = new File(HttpServer.WEB_ROOT, request.getUri());
21 if (file.exists()) {
22 fis = new FileInputStream(file);
23 int ch = fis.read(bytes, 0, BUFFER_SIZE);
24 while (ch != -1) {
25 output.write(bytes, 0, ch);
26 ch = fis.read(bytes, 0, BUFFER_SIZE);
27 }
28 } else {
29 String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
30 "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" +
31 "<h1>File Not Found</h1>";
32 output.write(errorMessage.getBytes());
33 }
34 } catch (Exception e) {
35 System.out.println(e.toString());
36 } finally {
37 if (fis != null)
38 fis.close();
39 }
40 }
41
42 @Override
43 public String getCharacterEncoding() {
44 return null;
45 }
46
47 @Override
48 public String getContentType() {
49 return null;
50 }
51
52 @Override
53 public ServletOutputStream getOutputStream() throws IOException {
54 return null;
55 }
56
57 @Override
58 public PrintWriter getWriter() throws IOException {
59 writer = new PrintWriter(output, true);
60 return writer;
61
62 }
63
64 @Override
65 public void setCharacterEncoding(String charset) {
66
67 }
68
69 @Override
70 public void setContentLength(int len) {
71
72 }
73
74 @Override
75 public void setContentType(String type) {
76
77 }
78
79 @Override
80 public void setBufferSize(int size) {
81
82 }
83
84 @Override
85 public int getBufferSize() {
86 return 0;
87 }
88
89 @Override
90 public void flushBuffer() throws IOException {
91
92 }
93
94 @Override
95 public void resetBuffer() {
96
97 }
98
99 @Override
100 public boolean isCommitted() {
101 return false;
102 }
103
104 @Override
105 public void reset() {
106
107 }
108
109 @Override
110 public void setLocale(Locale loc) {
111
112 }
113
114 @Override
115 public Locale getLocale() {
116 return null;
117 }
118 }
2.3 PrimitiveServlet类
2
3 @Override
4 public void init(ServletConfig config) throws ServletException {
5 System.out.println("init");
6 }
7
8 @Override
9 public ServletConfig getServletConfig() {
10 return null;
11 }
12
13 @Override
14 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
15 System.out.println("from service");
16 PrintWriter out = res.getWriter();
17 out.println("Hello. Roses are red.");
18 out.print("Violets are blue.");
19 }
20
21 @Override
22 public String getServletInfo() {
23 return "this is v2 info";
24 }
25
26 @Override
27 public void destroy() {
28 System.out.println("destroy");
29 }
30 }
2.4 ServletProcessor 和 StaticResourceProcessor
2
3 public void process(Request request, Response response) {
4 String uri = request.getUri();
5 String servletName = uri.substring(uri.lastIndexOf("/") + 1);
6 Class myClass = null;
7 try {
8 myClass = Class.forName("tomcat.v2." + servletName);
9 } catch (ClassNotFoundException e) {
10 System.out.println(e.toString());
11 }
12 Servlet servlet = null;
13 try {
14 servlet = (Servlet) myClass.newInstance();
15 servlet.service((ServletRequest) request, (ServletResponse) response);
16 } catch (Exception e) {
17 e.printStackTrace();
18 } catch (Throwable e) {
19 e.printStackTrace();
20 }
21 }
22 }
2
3 public void process(Request request, Response response){
4 try{
5 response.sendStaticResource();
6 }catch (IOException e){
7 e.printStackTrace();
8 }
9 }
10 }
最后我们还需要建一个辅助类Constants指定静态资源的存放路径
1 public class Constants {
2 public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
3 }
3 启动服务器
启动main函数来启动我们的http服务器。在浏览器输入http://localhost:8080/test,得到的结果如图3.1
图3.1
这个url访问的是我的电脑中的一个文件test,它的存储路径为 /Users/wangyu/Documents/workspace/Tomcat/webroot/test,即当前项目的webroot目录下。
我们还可以输入一个servlet地址查看效果。在浏览器输入http://localhost:8080/v2/PrimitiveServlet,得到的结果如图3.2
图3.2
这和我们的PrimitiveServlet的输出是一致的。
4 结语
当然,tomcat的功能比我们的简易http服务器强大的多。但是通过这个最简单的http服务器,是否让你对http服务器有更深的一点了解了呢?
参考资料:
1 深入剖析Tomcat
深入剖析TOMCAT的更多相关文章
- 深入剖析tomcat 笔记——第8章 载入器
深入剖析tomcat 笔记 目录:
- 深入剖析Tomcat会话机制
1缓存机制 Tomcat默认将Session保存到内存中.但同时,Tomcat也提供了PersistentManager配合不同的Store实现的方式,使Session可以被保存到不同地方(Datab ...
- 《深入剖析Tomcat》源码
<深入剖析Tomcat>翻译自<How Tomcat Works> 可以到官网下载:https://brainysoftware.com/download 官网下载比较慢,我就 ...
- 《深入剖析Tomcat》读书笔记(一)
一.Tomcat Tomcat,全名Apache Tomcat,最初是由Sun发起,后来捐赠给ASF,是Apache Jakarta下的一个子项目.Tomcat是对Servlet API定义的容器的一 ...
- 《深入剖析Tomcat》读书笔记(二)
三.容器Container Container 是容器的父接口,所有子容器都必须实现这个接口.Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine. ...
- 《深入剖析Tomcat》阅读(三)
这里要介绍下Tomcat的一个重要设计方法,Catalina设计方式. Servlet容器是一个复杂系统,但是,它有三个基本任务,对每个请求,servlet容器会为其完成以下三个操作: 1.创建一个R ...
- 《深入剖析Tomcat》阅读(二)
Tomcat是基于Sun公司标准的开源Servlet容器. Servlet是什么? Servlet(Server Applet),全称Java Servlet,未有中文译文.是用Java编写的服务器端 ...
- 深入剖析Tomcat类加载机制
1JVM类加载机制 JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构.其中引导类.扩展类.系统类三个加载器是JVM内置的. 它们的作用分别是: 1)引导类加载器:使用n ...
- 深入剖析tomcat的类加载机制
1JVM类加载机制 JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构.其中引导类.扩展类.系统类三个加载器是JVM内置的. 它们的作用分别是: 1)引导类加载器:使用n ...
随机推荐
- ABP中的AutoMapper
在我们的业务中经常需要使用到类型之间的映射,特别是在和前端页面进行交互的时候,我们需要定义各种类型的Dto,并且需要需要这些Dto和数据库中的实体进行映射,对于有些大对象而言,需要赋值太多的属性,这样 ...
- Fiddler使其在HttpURLConnection下正常抓包
像陌陌这样使用HttpURLConnection进行通讯的APP还是无能为力 还需要对fiddler进行如下设置: 点击"Rules->CustomizeRules"; 在这 ...
- u盘传输文件时提示过大
(无需格式化U盘) 1.拷贝大文件时提示目标文件系统过大,无法复制 2.查U盘,发现剩余空间足够: 3.分析原因,是由于U盘的格式问题导致的,当期的磁盘格式是FAT32类型的,无拷贝过大的文件: 4. ...
- MyBatis代码生成器(maven插件方式和控制台命令运行方式)
代码生成器的作用: 1.生成domain 2.生成mapper接口 3.生成mapper映射文件 准备工作:导入MyBatis所需要的包 第一步:在src/main/resources(必须)目录下创 ...
- Educational Codeforces Round 61 (Div.2)
A.(c1=0&&c3>0)||(c1!=c4) #include<cstdio> #include<cstring> #include<algor ...
- 以前我对你不够好,我也很难受——CSS篇
1)文字下划线.删除线.定划线 Text-decoration:underline /*下划线*/ Text-decoration:overline /*顶划线*/ Text-decorati ...
- robot framework 如何处理循环条件下面的变量自增
下面举了一个基础栗子,可以运行的.${num}就是我需要的自增变量.有人也许会问为什么不用${i},不是我不想用,而是我${i}有其他用处,必须另外定义一个变量,需要注意的是定义变量的时候,应该在循环 ...
- OS X系统特有文件目录说明
os x特有的目录 OS X系统中,除了标准的unix目录外,还增加了特有的目录. /Applications 应用程序目录,默认所有的GUI应用程序都安装在这里: /Library 系统的数据文件. ...
- Ext之页面多次请求问题(下拉框发送无关请求)
extjs 下拉框在拉取本地数据,然后又要展示后台数据时,出现过此问题(加载页面,自动发送无关的请求导致后台出现错误日志) { xtype:'combo', id:'state', width:130 ...
- MySQL 启动、登录、退出和目录结构
一.启动 MySQL 服务器启动方式有两种: (1)通过服务的方式自动启动 (2)手动启动的方式 1.windows 服务方式启动 操作步骤: 也可以在 cmd 窗口 输入 services.msc ...