How Tomcat Works(二)

系统 1648 0

我们这些可怜虫,只有沿着大神的思路,这样我们才能进步得更快;因为我们不是跟大神处于同一级别上。所以我这里是参考《How Tomcat Works》这本英文版的大作来理解tomcat的工作原理

本人认为,Tomcat容器源码是学习java编程以及设计模式等的绝佳范例,深入理解其源码对我辈开发人员的编程水平提高大有裨益!

我们可以从该书指定的官方网址下载相关示例源码 http://www.brainysoftware.com,本文顺着作者的思路,介绍一个简单的web服务器

我们知道,web服务器是使用http协议与客户端进行通信,所以读者有必要先了解http协议格式;基于java的web服务器会使用两个重要的类 java.net.Socket与java.net.ServerSocket(服务器端与客户端通过Socket通信)

关于http协议,网上的资料汗牛充栋,本人在这里加上简略的描述(http协议基于tcp协议)

http客户端请求包括如下部分:

Method-URI-Protocol/Version 方法-地址-版本
Request header 请求头
Entity body 请求实体

比如 http://www.outletss.com/  是本人以前帮客户做的网站,如果我们在浏览器打开该url地址,实际上客户端向服务器发送了如下格式的消息

      GET http://www.outletss.com/ HTTP/1.1
      
        
Host: www.outletss.com
Connection: keep-alive
Accept: text/html
      
      ,application/xhtml+xml,application/xml
      
        ;
      
      
        q=0.9,*/*;q=0.8
      
      
User-Agent: Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36
      
        
Accept-Encoding: gzip
      
      ,deflate,
      
        sdch
Accept-Language: zh-CN
      
      ,zh
      
        ;
      
      
        q=0.8
      
      
Cookie: lzstat_uv=12863370662865423613|2989608
      
        ;
      
      
         CKFinder_Path=Images%3A%2F%3A1; JSESSIONID=D7F9EC74149CB674D19A253B46273A77; lzstat_ss=1366758708_0_1375562495_2989608
      
    

http服务器端响应包括如下部分:

Protocol-Status code-Description 协议状态 描述代码
Response headers 响应头
Entity body 响应实体

然后服务器端向客户端响应了如下格式的消息

      HTTP/1.1 200
      
         OK
Connection: close
Date: Sat
      
      , 03 Aug 2013 15:00:30
      
         GMT
Server: Microsoft-IIS/
      
      6.0
      
        
X-UA-Compatible: IE
      
      =
      
        EmulateIE7
X-Powered-By: ASP.NET
Set-Cookie: JSESSIONID
      
      =0A5B07FF5661CA6F8D87937A54B4EEF5
      
        ;
      
      
         Path=/; HttpOnly
      
      
Content-Type: text/html
      
        ;
      
      
        charset=UTF-8
      
      
        Content-Language: zh-CN






<!DOCTYPE html PUBLIC 
      
      "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
      
        >
<html xmlns
      
      ="http://www.w3.org/1999/xhtml"
      
        >
//这里省略了html代码
</html>
      
    

基于java的Socket编程,可以参考java网络编程相关资料,Socket服务器端与客户端的网络交互与本地文件系统I/O具有一致的编程模型,基本上也是输入流与输出流的概念(如果你不懂输入流输出流的概念,基本上还要去修炼)

Socket分为客户端与服务器端,Socket表示客户端套接字,ServerSocket表示服务器端套接字,我们参考书中示例,看一个简单的服务器怎么实现

HttpServer类表示一个web服务器,示例代码如下:

      
        public
      
      
        class
      
      
         HttpServer {

  
      
      
        /**
      
      
         WEB_ROOT is the directory where our HTML and other files reside.
   *  For this package, WEB_ROOT is the "webroot" directory under the working
   *  directory.
   *  The working directory is the location in the file system
   *  from where the java command was invoked.
   
      
      
        */
      
      
        public
      
      
        static
      
      
        final
      
       String WEB_ROOT =
      
        
    System.getProperty(
      
      "user.dir") + File.separator  + "webroot"
      
        ;

  
      
      
        //
      
      
         shutdown command
      
      
        private
      
      
        static
      
      
        final
      
       String SHUTDOWN_COMMAND = "/SHUTDOWN"
      
        ;

  
      
      
        //
      
      
         the shutdown command received
      
      
        private
      
      
        boolean
      
       shutdown = 
      
        false
      
      
        ;

  
      
      
        public
      
      
        static
      
      
        void
      
      
         main(String[] args) {
    HttpServer server 
      
      = 
      
        new
      
      
         HttpServer();
    server.await();
  }

  
      
      
        public
      
      
        void
      
      
         await() {
    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
      
        );
    }

    
      
      
        //
      
      
         Loop waiting for a request
      
      
        while
      
       (!
      
        shutdown) {
      Socket socket 
      
      = 
      
        null
      
      
        ;
      InputStream input 
      
      = 
      
        null
      
      
        ;
      OutputStream output 
      
      = 
      
        null
      
      
        ;
      
      
      
        try
      
      
         {
        socket 
      
      =
      
         serverSocket.accept();
        input 
      
      =
      
         socket.getInputStream();
        output 
      
      =
      
         socket.getOutputStream();

        
      
      
        //
      
      
         create Request object and parse
      
      
        Request request = 
      
        new
      
      
         Request(input);
        request.parse();

        
      
      
        //
      
      
         create Response object
      
      
        Response response = 
      
        new
      
      
         Response(output);
        response.setRequest(request);
        response.sendStaticResource();

        
      
      
        //
      
      
         Close the socket
      
      
                socket.close();

        
      
      
        //
      
      
        check if the previous URI is a shutdown command
      
      
        shutdown =
      
         request.getUri().equals(SHUTDOWN_COMMAND);
      }
      
      
      
        catch
      
      
         (Exception e) {
        e.printStackTrace();
        
      
      
        continue
      
      
        ;
      }
    }
  }
}
      
    

在上面代码里面,首先创建一个ServerSocket实例,然后用一个while循环监听客户端请求,接收到客户端请求后,通过ServerSocket实例的accept方法返回Socket实例,将该Socket实例的输入流与输出流封装成Request实例与Response实例,并调用Response实例的void sendStaticResource()方法响应请求。

Request类代码如下:

      
        public
      
      
        class
      
      
         Request {

  
      
      
        private
      
      
         InputStream input;
  
      
      
        private
      
      
         String uri;

  
      
      
        public
      
      
         Request(InputStream input) {
    
      
      
        this
      
      .input =
      
         input;
  }

  
      
      
        public
      
      
        void
      
      
         parse() {
    
      
      
        //
      
      
         Read a set of characters from the socket
      
      
    StringBuffer request = 
      
        new
      
       StringBuffer(2048
      
        );
    
      
      
        int
      
      
         i;
    
      
      
        byte
      
      [] buffer = 
      
        new
      
      
        byte
      
      [2048
      
        ];
    
      
      
        try
      
      
         {
      i 
      
      =
      
         input.read(buffer);
    }
    
      
      
        catch
      
      
         (IOException e) {
      e.printStackTrace();
      i 
      
      = -1
      
        ;
    }
    
      
      
        for
      
       (
      
        int
      
       j=0; j<i; j++
      
        ) {
      request.append((
      
      
        char
      
      
        ) buffer[j]);
    }
    System.out.print(request.toString());
    uri 
      
      =
      
         parseUri(request.toString());
  }

  
      
      
        private
      
      
         String parseUri(String requestString) {
    
      
      
        int
      
      
         index1, index2;
    index1 
      
      = requestString.indexOf(' '
      
        );
    
      
      
        if
      
       (index1 != -1
      
        ) {
      index2 
      
      = requestString.indexOf(' ', index1 + 1
      
        );
      
      
      
        if
      
       (index2 >
      
         index1)
        
      
      
        return
      
       requestString.substring(index1 + 1
      
        , index2);
    }
    
      
      
        return
      
      
        null
      
      
        ;
  }

  
      
      
        public
      
      
         String getUri() {
    
      
      
        return
      
      
         uri;
  }

}
      
    

上面的void parse()方法是解析客户端的请求参数,这里是解析客户端请求的URL地址

Response类的代码如下:

      
        /*
      
      
        
  HTTP Response = Status-Line
    *(( general-header | response-header | entity-header ) CRLF)
    CRLF
    [ message-body ]
    Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

      
      
        */
      
      
        public
      
      
        class
      
      
         Response {

  
      
      
        private
      
      
        static
      
      
        final
      
      
        int
      
       BUFFER_SIZE = 1024
      
        ;
  Request request;
  OutputStream output;

  
      
      
        public
      
      
         Response(OutputStream output) {
    
      
      
        this
      
      .output =
      
         output;
  }

  
      
      
        public
      
      
        void
      
      
         setRequest(Request request) {
    
      
      
        this
      
      .request =
      
         request;
  }

  
      
      
        public
      
      
        void
      
       sendStaticResource() 
      
        throws
      
      
         IOException {
    
      
      
        byte
      
      [] bytes = 
      
        new
      
      
        byte
      
      
        [BUFFER_SIZE];
    FileInputStream fis 
      
      = 
      
        null
      
      
        ;
    
      
      
        try
      
      
         {
      File file 
      
      = 
      
        new
      
      
         File(HttpServer.WEB_ROOT, request.getUri());
      
      
      
        if
      
      
         (file.exists()) {
        fis 
      
      = 
      
        new
      
      
         FileInputStream(file);
        
      
      
        int
      
       ch = fis.read(bytes, 0
      
        , BUFFER_SIZE);
        
      
      
        while
      
       (ch!=-1
      
        ) {
          output.write(bytes, 
      
      0
      
        , ch);
          ch 
      
      = fis.read(bytes, 0
      
        , BUFFER_SIZE);
        }
      }
      
      
      
        else
      
      
         {
        
      
      
        //
      
      
         file not found
      
      
        String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
          "Content-Type: text/html\r\n" +
          "Content-Length: 23\r\n" +
          "\r\n" +
          "<h1>File Not Found</h1>"
      
        ;
        output.write(errorMessage.getBytes());
      }
    }
    
      
      
        catch
      
      
         (Exception e) {
      
      
      
        //
      
      
         thrown if cannot instantiate a File object
      
      
              System.out.println(e.toString() );
    }
    
      
      
        finally
      
      
         {
      
      
      
        if
      
       (fis!=
      
        null
      
      
        )
        fis.close();
    }
  }
}
      
    

这里最重要的是void sendStaticResource()方法,用于向输出流写入数据(这里是静态文件),响应客户端请求

本文介绍的是一个最简单的web服务器,Tomcat容器的复杂性远不止如此简单,待后文接着分析

--------------------------------------------------------------------------- 

本系列How Tomcat Works系本人原创 

转载请注明出处 博客园 刺猬的温驯 

本人邮箱: chenying998179 # 163.com ( #改为@

本文链接 http://www.cnblogs.com/chenying99/p/3229192.html

How Tomcat Works(二)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论