设为首页 收藏本站
查看: 690|回复: 0

[经验分享] Tomcat 源码解析 (二)自己写服务器

[复制链接]

尚未签到

发表于 2015-8-12 09:03:48 | 显示全部楼层 |阅读模式
  首先原谅我上次《How Tomcat Works 1》的粗制滥造, 这次给必要的代码都给上必要注释。
  第二章是说明简单的servlet容器是如何工作的。这一章带有2个servlet容器应用,可以处
  理静态资源和简单的servlet请求。尤其是你将会学到如何创建request和response对象,然
  后把它们传递给被请求的servlet的service方法。在servlet容器里边还有一个servlet,你
  可以从一个web浏览器中调用它。
   一个基于java Web的服务器, 两个重中之重的类便是java.net.Socket 和 java.net.ServerSocket,
  Socket即套接字,为了发送数据到另一台机器, 首先要知道那台机器的IP及套接字端口。
  有更深入兴趣的可以继续深入了解Socket用法。
  上面的Socket代表客户端套接字,那么同样的就应该有服务端的套接字:ServerSocket
  我们这边使用的构造方法是 public ServerSocket(int port, int backLog, InetAddress bindingAddress);
  port就是端口, backLog为输入连接指示(对连接的请求)的最大队列长度,我们这里设置为1,bindingAddress便是绑定地址,我们这里通过InetAddress.getByName("127.0.0.1")来获取。
  另外我们还需要了解一下HTTP协议的基本概念:
  一个HTTP请求包括三个组成部分:  
  方法—统一资源标识符(URI)—协议/版本  
  请求的头部  
  主体内容
   下面是给一个栗子:
  POST /examples/default.jsp HTTP/1.1
Accept: text/plain; text/html
Accept-Language: en-gb
Connection: Keep-Alive
Host: localhost
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
Content-Length: 33
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
lastName=Franks&firstName=Michael
   其中第一句包含了 method(POST), uri(/examples/default.jsp) 和version(HTTP/1.1)
  然后最后一句便是请求主题(Body)
  没错, 中间那长长的一坨就是http请求的头部(实际上请求主题可以很长,这边我们简化了)
  更详细的可以看这篇文章 http 协议详解
   我们要知道,servlet实现服务基本上要有3件事情要做:
  1:Servlet;
  通过service()方法接受和处理request, response
  对于此, 我们创建类HttpServer:



1 package chap1_ASimpleWebServer;
2 import java.io.File;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.net.InetAddress;
7 import java.net.ServerSocket;
8 import java.net.Socket;
9
10
11 public class HttpServer {
12     // getProperty("user.dir")就是返回当前目录, File.separator就是'\';
13     // 以博主机子为例, WEB_ROOT的内容就是"D:/workspace/howTomcatWorks/chap1_ASimpleWebServer/webroot"
14     public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
15     
16     // 如果要关闭服务器, 则在浏览器地址栏中输入 http://localhost:8080/SHUTDOWN
17     private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
18     
19     private boolean shutdown = false;
20     
21     // 类似Severlet.service(), 现在只是简单的了解下概念:
22     public void await() {
23         ServerSocket serverSocket = null;
24         int port = 8080;
25         try {
26             // port就是端口, backLog为输入连接指示(对连接的请求)的最大队列长度,我们这里设置为1,
27             // bindingAddress便是绑定地址,我们这里通过InetAddress.getByName("127.0.0.1")来获取。
28             serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
29         } catch (IOException e) {
30             e.printStackTrace();
31             System.exit(1);
32         }
33         // loop waiting for a request
34         while (!shutdown) {
35             Socket socket = null;
36             InputStream input = null;
37             OutputStream output = null;
38             try {
39                 // 先是通过ServerSocket实例 得到 Socket实例
40                 socket = serverSocket.accept();
41                 // 再通过Socket实例来获取InputStream和OutputStream的实例
42                 input = socket.getInputStream();
43                 output = socket.getOutputStream();
44                 // create Request object & parse
45                 // 通过input创建request
46                 Request request = new Request(input);
47                 request.parse();
48                 // create Response object
49                 // 通过request创建response
50                 Response response = new Response(output);
51                 response.setRequest(request);
52                 response.sendResource();
53                 // close socket
54                 socket.close();
55                 // check if the previous URI is a shutdown command
56                 shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
57             } catch (IOException e) {
58                 e.printStackTrace();
59                 continue;
60             }
61         }
62     }
63     
64     public static void main(String[] args) {
65         System.out.println(WEB_ROOT);
66         HttpServer server = new HttpServer();
67         server.await();
68     }
69 }
  

  2:Request;
  包含http头等信息, 是javax.Servlet.http.ServletRequest 接口的实例



1 package chap1_ASimpleWebServer;
2 import java.io.IOException;
3 import java.io.InputStream;
4
5 /*
6  * 在这一章中,我们仅仅关注HTTP请求的第一部分,请求行。请求行从
7  * 一个方法标记开始,接下去是请求的URI和协议版本,最后是用回车换
8  * 行符(CRLF)结束。请求行里边的元素是通过一个空格来分隔的。例如,
9  * 使用GET方法来请求index.html文件的请求行如下所示:
10  *
11  * GET /index.html HTTP/1.1
12  */
13 public class Request {
14     private InputStream input;
15     private String uri;
16     
17     public Request (InputStream input) {
18         this.input = input;
19     }
20     
21     public void parse() {
22         // 新建StringBuffer request, 用于接收字节转化的字符
23         StringBuffer request = new StringBuffer(2048);
24         int len;
25         // 新建字节数组buffer, 用于接收套接字的InputStream字节流
26         byte[] buffer = new byte[2048];
27         try {
28             len = input.read(buffer);
29         } catch (IOException e) {
30             e.printStackTrace();
31             len = -1;
32         }
33         for (int i = 0; i < len; i++) {
34             // 这里不要忘了强制转型(byte -> char)
35             request.append((char)buffer);
36         }
37         System.out.println(request.toString());
38         // 得到URI的值
39         uri = parseUri(request.toString());
40     }
41     
42     // 从HTTP请求行里截取URI, 即 "GET /index.html HTTP/1.1" 中的 "/index.html"
43     private String parseUri(String requestString) {
44         int index1, index2;
45         index1 = requestString.indexOf(' ');
46         if (index1 != -1) {
47             index2 = requestString.indexOf(' ', index1 + 1);
48             if (index2 > index1) {
49                 return requestString.substring(index1 + 1, index2);
50             }
51         }
52         return null;
53     }
54     
55     // 返回URI
56     public String getUri() {
57         return uri;
58     }
59 }
  
  3:Response
  通过service()处理赋值后,生成对于客户的响应,对应于Request,, 它是javax.Servlet.http.ServletResponse接口的实例
  



1 package chap1_ASimpleWebServer;
2 import java.io.File;
3 import java.io.FileInputStream;
4 import java.io.IOException;
5 import java.io.OutputStream;
6
7
8 public class Response {
9     // 设置默认响应信息长度
10     private static final int BUFFER_SIZE = 1024;
11     Request request;
12     OutputStream output;
13     
14     public Response (OutputStream output) {
15         this.output = output;
16     }
17     
18     public void setRequest(Request request) {
19         this.request = request;
20     }
21     
22     // 用来发送一个静态资源,例如一个HTML文件。它首先通过传递上一级
23     // 目录的路径和子路径给File累的构造方法来实例化java.io.File类
24     public void sendResource() throws IOException {
25         // 创建byte数组bytes用于接收文件流数据
26         byte[] bytes = new byte[BUFFER_SIZE];
27         FileInputStream fis = null;
28         try {
29             // 根据当前目录和URI获取文件
30             File file = new File(HttpServer.WEB_ROOT, request.getUri());
31             if (file.exists()) {
32                 fis = new FileInputStream(file);
33                 int len = fis.read(bytes, 0, BUFFER_SIZE);
34                 while (len != -1) {
35                     // 写到output流中
36                     output.write(bytes, 0, len);
37                     len = fis.read(bytes, 0, BUFFER_SIZE);
38                 }
39             } else {
40                 // 如果文件不存在,页面提示File Not Found信息(404)
41                 String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
42                                       "ContentType: text/html\r\n" +
43                                       "ContentLength: 23\r\n\r\n" +
44                                       "<head>File Not Found</head>";
45                 output.write(errorMessage.getBytes());
46             }
47         } catch (Exception e) {
48             e.printStackTrace();
49         } finally {
50             // 最后别忘记关闭文件流
51             if (fis != null) {
52                 fis.close();
53             }
54         }
55     }
56 }
  现在在项目文件夹下建立一个webroot文件夹:
DSC0000.png
  先运行主程序HttpServer, 然后打开浏览器,输入地址:http://localhost:8080/index.html:
DSC0001.png
   另外, 控制台结果显示了HTTP信息:
DSC0002.png
  到此为止, 我们已经简单的了解到了一个简单web服务器是如何工作的。这一次仅仅由三个类组
  成,所以并不是功能是十分局限的。下一次是真的要将要讨论动态内容的处理过程了 DSC0003.gif 。。。。。。。
  
  
  
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-97720-1-1.html 上篇帖子: Tomcat源码分析之—具体启动流程分析 下篇帖子: 记一个菜鸟在Linux上部署Tomcat的随笔
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表