zoukankan      html  css  js  c++  java
  • 试解析Tomcat运行原理(一)--- socket通讯

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

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

      程序的死循环(粗糙的做法,后面逐步优化)来实现不断的获取80端口http请求,来达到监听80端口http请求的目的。java.net包下面的SocketServerSocket两个类就能实现我们对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监听到的数据,以及返回结果,即操作ResponseRequest

      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
    " +
    36                         "Content-Type: text/html
    " + 
    37                         "Content-Length: 23
    " +
    38                         "
    " +
    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属性,获取uriOutput,就不用说了,也是这个类存在的核心意义,依照Request类提供的uri信息,在本地读写文件后,形成一个输出来,存放到output中,那么这项工作就由sendStaticResource这个共有方法完成啦。

      好,代码到这个,可以说我们大家已经看到一个tomcat模型了,有点万事俱备,只欠东风的感觉,客户端发起请求,ResponseReqeust有了,那么继续往上游考虑,Reqeust依赖于客户端的请求,自然以来于Socket数据。我们在这里做得简便一点,将ServerSocketSocket封装到一个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监听到的结果,有立刻调动ReqeustResponse进行响应,加入主函数,为的是我们这个是模拟的控制台程序,需要一个程序入口,main函数就是程序入口。此外,HttpServer类包括一个静态属性SHUTDOWN_COMMAND,输入为true则停止这个main函数,变量初始值为false,当客户端也就是Request响应得到客户端输入 http://10.10.10.108:8080/SHUTDOWN时候,则变量在while中会置成true,紧接着停止main,结束应用程序进程。

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

      本地文件:

      

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

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

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

  • 相关阅读:
    Intellij IDEA 使用Spring-boot-devTools
    Intellij IDEA 使用Spring-boot-devTools
    Swift-Realm数据库的使用详解
    Maven快速上手
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    2019数据技术嘉年华主会场,数据英雄荟萃一堂共论道
    在 AWTK 中 如何让文本滚动起来
    数据库高可用架构了解一下
    session和token的区别
  • 原文地址:https://www.cnblogs.com/aspnetdream/p/4217581.html
Copyright © 2011-2022 走看看