试解析Tomcat运行原理(一)--- socket通讯

系统 2005 0

  关于这篇文章也确实筹划了很久,今天决定开篇写第一篇,说起 tomcat 首先很容易联想到 IIS ,因为我最开始使用的就是 .net 技术,我第一次使用 asp 写学生成绩管理系统后,很茫然如何让别人都能看到或者说使用这个系统呢?由此认识了 IIS ,它是一个 web 容器,天生的多线程,及时响应用户提交的请求返回 html 页面,这就是我了解的最初的 web 容器的功能,由此我们来认识 tomcat 也并不困难,可以的话,在了解完 tomcat 后我们可以继续了解 jboss jetty 等,好我们进入主题。

  我们在平时开发的过程中是在使用 eclipse 时候才启动 tomcat ,对于一个 web 容器而言,简而言之,它是系统的一个守护进程,守护着对这台服务器某个端口发起的请求,基于这一点,它就需要一个监听程序,这个监听程序来获取来自这个端口的特定请求的数据, ok ,直接点讲,我们这里使用 Socket 来获取某个端口,通常是 80 端口的 http 请求,通过简单的 Java

  程序的死循环(粗糙的做法,后面逐步优化)来实现不断的获取 80 端口 http 请求,来达到监听 80 端口 http 请求的目的。j ava.net包 下面的 Socket ServerSocket 两个类就能实现我们对 8080 端口的监听,去除中间的逻辑代码,我们只看这个两个类的演绎的话如下:

 

      
        1
      
       ServerSocket serverSocket = 
      
        new
      
       ServerSocket(8080, 1, InetAddress.getByName("10.10.10.106"));
    

 

  对本机的 8080 端口进行监听

 

      
        1
      
       socket =
      
         serverSocket.accept();             


      
      
        2
      
       input =
      
         socket.getInputStream(); 


      
      
        3
      
       output = socket.getOutputStream(); 
    

 

  以上代码就是获取监听结果。

  这是最简单和最精简的Socket 通讯原理,基于这个核心点我们来开发一个简易的,可以提供静态页面访问的  custom tomcat ,准备一个 index.html 文件放到 /home/webroot 目录下,那么除去拓展上面代码外,我们还需要一个 Response 和一个 Request

  类设计如下:

  HttpServer : 主函数所在类,负载启动 ServerSocket 和 操作整合 Socket 监听到的数据,以及返回结果,即操作 Response Request

  Request:  封装 Socket 监听到的用户端请求,包括请求的 http uri 信息。

  Response : 封装需要推送到客户端的结果数据,即我们需要根据 http uri  去本机寻找相应的资源,写给客户端。

  言简意赅,进入代码,首先  Request 类代码:

 

      
         1
      
      
        public
      
      
        class
      
      
         Request 


      
      
         2
      
      
        {


      
      
         3
      
      
        private
      
      
         InputStream input;


      
      
         4
      
      
        private
      
      
         String uri;


      
      
         5
      
      
         6
      
      
        public
      
      
         Request(InputStream input) {


      
      
         7
      
      
        this
      
      .input =
      
         input;


      
      
         8
      
      
            }


      
      
         9
      
      
        10
      
      
        public
      
      
        void
      
      
         parse()


      
      
        11
      
      
            {


      
      
        12
      
               StringBuffer request = 
      
        new
      
       StringBuffer(2048
      
        );


      
      
        13
      
      
        int
      
      
         i;


      
      
        14
      
      
        byte
      
      [] buffer = 
      
        new
      
      
        byte
      
      [2048
      
        ];


      
      
        15
      
      
        try
      
      
        16
      
      
                { 


      
      
        17
      
                    i =
      
         input.read(buffer);


      
      
        18
      
      
                }


      
      
        19
      
      
        catch
      
      
        (IOException e) 


      
      
        20
      
      
                { 


      
      
        21
      
      
                     e.printStackTrace(); 


      
      
        22
      
                    i = -1
      
        ; 


      
      
        23
      
      
                }


      
      
        24
      
      
        25
      
      
        for
      
       (
      
        int
      
       j=0; j<i; j++
      
        ) 


      
      
        26
      
      
                {


      
      
        27
      
                   request.append((
      
        char
      
      
        ) buffer[j]);


      
      
        28
      
      
                } 


      
      
        29
      
      
                System.out.print(request.toString()); 


      
      
        30
      
               uri =
      
         parseUri(request.toString());


      
      
        31
      
      
            }


      
      
        32
      
      
        33
      
      
        private
      
      
         String parseUri(String requestString) 


      
      
        34
      
      
            {  


      
      
        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
      
      
        public
      
      
         String getUri() 


      
      
        46
      
      
            {


      
      
        47
      
      
        return
      
      
         uri;


      
      
        48
      
      
            }


      
      
        49
      
       }
    

 

  代码解释:类包括一个属性和两个方法, input 属性即是从 Socket 监听到的信息, Socket 会将监听到的信息放入一个 InputStream 中,我们使用 Reqeust 类的 Input 属性来接受。接收到输入流后,在 parse 中对这个输入流进行解析成字符串,即对 Http 请求进行拆解,得到完整的 Http URL ,所以这个方法是私有的,是类存在意义的核心所在,而提供的对外方法 parseUri 是负载将 parse 解析的 url 结果提供给外界,即,客户端发来请求那个文件,具体的是最终提供给 Response 类, Response 类得到这个文件名称后,去本地制定目录读取文件。 Tomcat 中通常就是 webapps 目录啦,很熟悉了吧,哈哈。

  Response 类如何实现这个读取文件的历史使命呢,代码如下 :

 

      
         1
      
      
        public
      
      
        class
      
      
         Response {


      
      
         2
      
      
         3
      
      
        private
      
      
        static
      
      
        final
      
      
        int
      
       BUFFER_SIZE = 1024
      
        ; 


      
      
         4
      
      
            Request request; 


      
      
         5
      
      
            OutputStream output;


      
      
         6
      
      
         7
      
      
        public
      
      
         Response(OutputStream output) 


      
      
         8
      
      
            { 


      
      
         9
      
      
        this
      
      .output =
      
         output;


      
      
        10
      
      
            }


      
      
        11
      
      
        12
      
      
        public
      
      
        void
      
      
         setRequest(Request request)


      
      
        13
      
      
            {


      
      
        14
      
      
        this
      
      .request =
      
         request;


      
      
        15
      
      
            }


      
      
        16
      
      
        17
      
      
        public
      
      
        void
      
       sendStaticResource() 
      
        throws
      
      
         IOException


      
      
        18
      
      
            {


      
      
        19
      
      
        byte
      
      [] bytes = 
      
        new
      
      
        byte
      
      
        [BUFFER_SIZE];


      
      
        20
      
               FileInputStream fis = 
      
        null
      
      
        ;


      
      
        21
      
      
        try
      
      
        22
      
      
                {


      
      
        23
      
                       File file = 
      
        new
      
      
         File(HttpServer.WEB_ROOT, request.getUri());


      
      
        24
      
      
        if
      
      
         (file.exists())


      
      
        25
      
      
                        { 


      
      
        26
      
                               fis = 
      
        new
      
      
         FileInputStream(file); 


      
      
        27
      
      
        int
      
       ch = fis.read(bytes, 0
      
        , BUFFER_SIZE); 


      
      
        28
      
      
        while
      
       (ch!=-1
      
        ) { 


      
      
        29
      
                                   output.write(bytes, 0
      
        , ch); 


      
      
        30
      
                                   ch = fis.read(bytes, 0
      
        , BUFFER_SIZE);


      
      
        31
      
      
                                }


      
      
        32
      
      
                        }


      
      
        33
      
      
        else
      
      
        34
      
      
                        {


      
      
        35
      
                           String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +


      
        36
      
                               "Content-Type: text/html\r\n" + 


      
        37
      
                               "Content-Length: 23\r\n" +


      
        38
      
                               "\r\n" +


      
        39
      
                               "<h1>File Not Found</h1>"
      
        ;


      
      
        40
      
      
                            output.write(errorMessage.getBytes());


      
      
        41
      
      
                        }


      
      
        42
      
      
                }


      
      
        43
      
      
        catch
      
      
        (Exception e) 


      
      
        44
      
      
                {


      
      
        45
      
      
                        System.out.println(e.toString());


      
      
        46
      
      
                }


      
      
        47
      
      
        finally
      
      
        {


      
      
        48
      
      
                    fis.close();


      
      
        49
      
      
                }


      
      
        50
      
      
        51
      
      
            }


      
      
        52
      
       }
    

 

  代码解释: Response 一共三个属性,一个方法。三个属性,一个是设置属性, BUFFER_SIZE 设置读写字节流大小,关于读写文件,我个人觉得和服务器的性能和程序性能息息相关,不宜设定过大或过小(此处有不同见解的同仁欢迎来喷,我对这块理解目前限于此)。 Reqeust 属性,对照前文呼应, Response 需要获取 Request 类的 uri 结果信息,所以这里放了一个 Request 属性,获取 uri Output ,就不用说了,也是这个类存在的核心意义,依照 Request 类提供的 uri 信息,在本地读写文件后,形成一个输出来,存放到 output 中,那么这项工作就由 sendStaticResource 这个共有方法完成啦。

  好,代码到这个,可以说我们大家已经看到一个 tomcat 模型了,有点万事俱备,只欠东风的感觉,客户端发起请求, Response Reqeust 有了,那么继续往上游考虑, Reqeust 依赖于客户端的请求,自然以来于 Socket 数据。我们在这里做得简便一点,将 ServerSocket Socket 封装到一个 HttpServer 类中来,代码如下:

 

      
         1
      
      
        public
      
      
        class
      
      
         HttpServer {


      
      
         2
      
      
         3
      
      
        public
      
      
        static
      
      
        final
      
       String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"
      
        ;


      
      
         4
      
      
        private
      
      
        static
      
      
        final
      
       String SHUTDOWN_COMMAND = "/SHUTDOWN"
      
        ;


      
      
         5
      
      
        private
      
      
        boolean
      
       shutdown = 
      
        false
      
      
        ;


      
      
         6
      
      
        public
      
      
        static
      
      
        void
      
      
         main(String[] args)


      
      
         7
      
      
            {


      
      
         8
      
               HttpServer httpServer = 
      
        new
      
      
         HttpServer();


      
      
         9
      
      
                httpServer.await();


      
      
        10
      
      
            }


      
      
        11
      
      
        12
      
      
        public
      
      
        void
      
      
         await()


      
      
        13
      
      
            {


      
      
        14
      
               ServerSocket serverSocket = 
      
        null
      
      
        ;


      
      
        15
      
               Integer port = 8080
      
        ; 


      
      
        16
      
      
        try
      
      
        17
      
      
                { 


      
      
        18
      
                   serverSocket =  
      
        new
      
       ServerSocket(port, 1, InetAddress.getByName("10.10.10.106"
      
        )); 


      
      
        19
      
      
                } 


      
      
        20
      
      
        catch
      
      
        (IOException e)     


      
      
        21
      
      
                { 


      
      
        22
      
      
                    e.printStackTrace(); 


      
      
        23
      
                   System.exit(1
      
        ); 


      
      
        24
      
      
                } 


      
      
        25
      
      
        26
      
      
        while
      
      (!
      
        shutdown)


      
      
        27
      
      
                {


      
      
        28
      
                   Socket socket = 
      
        null
      
      
        ;


      
      
        29
      
                   InputStream input = 
      
        null
      
      
        ;


      
      
        30
      
                   OutputStream output = 
      
        null
      
      
        ;


      
      
        31
      
      
        try
      
      
        32
      
      
                    { 


      
      
        33
      
                       socket =
      
         serverSocket.accept();


      
      
        34
      
      
        35
      
                       input =
      
         socket.getInputStream(); 


      
      
        36
      
                       output =
      
         socket.getOutputStream(); 


      
      
        37
      
                       Request request = 
      
        new
      
      
         Request(input); 


      
      
        38
      
      
                        request.parse(); 


      
      
        39
      
                       Response response = 
      
        new
      
      
         Response(output);


      
      
        40
      
      
                        response.setRequest(request); response.sendStaticResource();  socket.close(); 


      
      
        41
      
                       shutdown =
      
         request.getUri().equals(SHUTDOWN_COMMAND);


      
      
        42
      
      
                    }


      
      
        43
      
      
        catch
      
      
        (Exception e) 


      
      
        44
      
      
                    {


      
      
        45
      
                       e.printStackTrace();
      
        continue
      
      
        ;


      
      
        46
      
      
                    }


      
      
        47
      
      
                }


      
      
        48
      
      
            }


      
      
        49
      
       }
    

 

  代码解释:我们知道启动 tomcat 之后,只要服务正常,客户端任意时候发起一个 http 请求, tomcat 就会响应,那么这里我们肯定需要一个 while 循环来模拟不间断的监听,类 await 方法就是负责不断的获取 socket 监听到的结果,有立刻调动 Reqeust Response 进行响应,加入主函数,为的是我们这个是模拟的控制台程序,需要一个程序入口, main 函数就是程序入口。此外, HttpServer 类包括一个静态属性 SHUTDOWN_COMMAND ,输入为 true 则停止这个 main 函数,变量初始值为 false ,当客户端也就是 Request 响应得到客户端输入  http://10.10.10.108:8080/SHUTDOWN 时候,则变量在 while 中会置成 true ,紧接着停止 main ,结束应用程序进程。

  在 eclipse 中或者在命令行中启动这个 main 函数,命令行则是输入  java HttpServer.java eclipse 则是在 main 函数中右键  run as application 启动。 我们打开浏览器,输入  http://10.10.10.108:8080/index.html, 回车结果如下:

试解析Tomcat运行原理(一)--- socket通讯

  本地文件:

  好了,夜深啦,就此搁笔了,抛砖引玉,欢迎提议和讨论,这个系列会继续下去,直到一个完整的可以响应一个java action请求的custom tomcat产品出来。

  最后附上我的源代码:http://files.cnblogs.com/aspnetdream/Project.zip

  参考:《How tomcat works》 作者:Budi Kurniawan & Paul Deck

 

试解析Tomcat运行原理(一)--- socket通讯


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

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

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

【本文对您有帮助就好】

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

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