zoukankan      html  css  js  c++  java
  • Tomcat 源码解析 (二)自己写服务器

          首先原谅我上次《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[i]); 
    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文件夹:

             先运行主程序HttpServer, 然后打开浏览器,输入地址:http://localhost:8080/index.html

             另外, 控制台结果显示了HTTP信息:

             到此为止, 我们已经简单的了解到了一个简单web服务器是如何工作的。这一次仅仅由三个类组

    成,所以并不是功能是十分局限的。下一次是真的要将要讨论动态内容的处理过程了。。。。。。。

  • 相关阅读:
    反射模块与模块之间的通信
    WCF传输协议
    IIs7 报错
    MVC3 ActionResult 返回类型
    三条数据 判断其中最大与最小
    dos批处理命令详解
    十拿九稳过倒桩之(倒桩技巧)
    九项路考(1)铁饼神功
    山鸽子
    九项路考(2)
  • 原文地址:https://www.cnblogs.com/thoupin/p/3022637.html
Copyright © 2011-2022 走看看