Tomcat源码学习记录--web服务器初步认识

  Tomcat作为开源的轻量级WEB服务器,虽然不是很适合某些大型项目,但是它开源,读其源代码可以很好的提高我们的编程功底和设计思维。Tomcat中用到了很多比较好的设计模式,其中代码风格也很值得我们去效仿。前阵子看了Tomcat源码分析这本书,特此过来分享分享自己的学习过程记录。说得不好,大神不要喷我。

  也不废话了,直入主题上代码。Tomcat是什么,Tomcat是一个web服务器,能够接收请求,作出响应。接收请求,作出响应让我们联想到Socket编程。我们可以起一个线程服务ServerSocket来监听本机的8080端口(可配置),然后就可以在浏览器上访问http://localhost:8080/index.html,这个时候就可以通过socket的inputstream获取到浏览器封装的HTTP请求了,然后就可以针对这个请求来大做文章。以下是服务端的代码

 1 package cn.tim.server.core;
 2 
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.OutputStream;
 7 import java.net.InetAddress;
 8 import java.net.ServerSocket;
 9 import java.net.Socket;
10 
11 
12 /**
13  * HTTP服务器,主类
14  * @author TIM
15  *
16  */
17 public class HttpServer {
18     
19     
20     /**
21      * 端口
22      */
23     public int PORT = 8080;
24     
25     
26     /**
27      * 关闭指令
28      */
29     public final static String SHUTDOWN = "SHUTDOWN";
30     
31     
32     /**
33      * webroot根目录
34      */
35     public static final String WEB_ROOT = 
36         System.getProperty("user.dir") + File.separator + "WebRoot";
37     
38     
39     public static void main(String[] args) {
40         
41         new HttpServer().await();
42         
43     }
44     
45     
46     /**
47      * 线程监听
48      */
49     private void await() {
50         
51         ServerSocket server = null;
52         try {
53             server = new ServerSocket(PORT,1, 
54                     InetAddress.getByName("127.0.0.1"));
55         } catch (Exception e) {
56             e.printStackTrace();
57         }
58         
59         boolean shutdown = false;
60         while(!shutdown) {
61             Socket client = null;
62             InputStream in = null;
63             OutputStream out = null;
64             try {
65                 // 获取到请求socket
66                 client = server.accept();
67                 in = client.getInputStream();
68                 out = client.getOutputStream();
69                 
70                 // 生成request同时解析请求
71                 Request request = new Request(in);
72                 request.parse();
73                 
74                 // 生成response
75                 Response response = new Response(out);
76                 response.setRequest(request);
77                 // 根据资源定位符发送对应资源
78                 response.sendStaticResource();
79                 client.close();
80                 
81                 shutdown = request.getUri().equals(SHUTDOWN);
82             } catch (IOException e) {
83                 // TODO Auto-generated catch block
84                 e.printStackTrace();
85                 continue;
86             }
87             
88         }
89         
90     }
91     
92 }

  既然服务端HttpServer都出来了,Request都干些什么,request顾名思义,请求肯定是封装请求的一个JAVA类,肯定要能够解析HTTP请求,例如访问静态资源,就得获取到静态资源的资源定位符uri。

  HTTP请求Request类:

1 GET /index.html HTTP/1.1
2 Accept: text/html, application/xhtml+xml, */*
3 Accept-Language: zh-CN
4 User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; MALCJS)
5 Accept-Encoding: gzip, deflate
6 Host: localhost:8080
7 DNT: 1
8 Connection: Keep-Alive
9 Cookie: principal=user:admin__password:admin
 1 package cn.tim.server.core;
 2 
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 
 6 /**
 7  * 封装请求
 8  * @author TIM
 9  *
10  */
11 public class Request {
12     
13     
14     /**
15      * 请求输入流
16      */
17     private InputStream in;
18     
19     
20     /**
21      * 资源定位符
22      */
23     private String uri;
24     
25     
26     /**
27      * 初始化request,传入socket输入流
28      * @param in
29      */
30     public Request(InputStream in) {
31         this.in = in;
32     }
33 
34     
35     /**
36      * 根据请求字符串解析请求
37      */
38     public void parse() {
39         
40         try {
41             byte[] bytes = new byte[2048];
42             int i = in.read(bytes);
43             
44             StringBuffer buffer = new StringBuffer(2048);
45             for(int j=0; j<i; j++) {
46                 buffer.append((char)bytes[j]);
47             }
48             System.out.println(buffer.toString());
49             uri = parseUri(buffer.toString());
50             System.out.println(uri);
51         } catch (IOException e) {
52             // TODO Auto-generated catch block
53             e.printStackTrace();
54         }
55         
56     }
57     
58     
59     /**
60      * 解析出资源定位符uri,实际上就是用字符串分拆获取到GET /index.html HTTP/1.1中的/index,html
61      * @return
62      */
63     private String parseUri(String requestString) {
64         
65         int index1 = requestString.indexOf(" ");
66         if(index1 != -1) {
67             int index2 = requestString.indexOf(" ", index1+1);
68             if(index2>index1) {
69                 return requestString.substring(index1+1, index2);
70             }
71         }
72         return null;
73     }
74     
75 
76     public InputStream getIn() {
77         return in;
78     }
79     
80     
81     public String getUri() {
82         return uri;
83     }
84     
85 }

  获取到资源定位符,接下来就是根据资源定位符来作出相应,当然实际的Tomcat处理方式肯定是很复杂的,我们只模仿其中简单的方式,访问静态资源。

  Response类:

 1 package cn.tim.server.core;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.OutputStream;
 8 
 9 public class Response {
10     
11     
12     /**
13      * 输出
14      */
15     private OutputStream out;
16     
17     
18     /**
19      * 缓冲大小
20      */
21     public final static int BUFFER_SIZE = 2048;
22     
23     
24     /**
25      * 请求,根据请求作出对应的响应
26      */
27     private    Request request;
28     
29     
30     public Response(OutputStream out) {
31         this.out = out;
32     }
33 
34     
35     /**
36      * 发送静态资源
37      */
38     public void sendStaticResource() {
39         
40         byte[] bytes = new byte[BUFFER_SIZE];
41         InputStream in = null;
42         try {
43             File file = new File(HttpServer.WEB_ROOT, request.getUri());
44             // 请求的资源存在
45             if(file.exists()) {
46                 in = new FileInputStream(file);
47                 int ch;
48                 if((ch=in.read(bytes, 0, BUFFER_SIZE))!=-1) {
49                     out.write(bytes, 0, ch);
50                 }
51             } 
52             // 请求资源不存在报404
53             else {
54                 String errorMessage = "HTTP/1.1 404 File Not Found
" +
55                   "Content-Type: text/html
" +
56                   "Content-Length: 23
" +
57                   "
" +
58                   "<h1>File Not Found</h1>";
59                 out.write(errorMessage.getBytes());
60             }
61         } catch (Exception e) {
62             // TODO Auto-generated catch block
63             e.printStackTrace();
64         } finally {
65             if(in!=null)
66                 try {
67                     in.close();
68                 } catch (IOException e) {
69                     // TODO Auto-generated catch block
70                     e.printStackTrace();
71                 }
72         }
73         
74     }
75     
76     
77     public void setRequest(Request request) {
78         this.request = request;
79     }
80 
81 }

  这样,一个简单的Web服务器就实现了,可以访问静态资源,直接在浏览器上访问,没有找到对应的资源还可以报404错误。今天就写到这里,继续努力。。。

  

原文地址:https://www.cnblogs.com/TimBing/p/3627872.html