关于这篇文章也确实筹划了好久,今天决定开篇写第一篇,提及tomcat首先很容易联想到IIS,由于我最开始使用的就是.net技术,我第一次使用asp写学生成绩管理系统后,很茫然如何让别人都能看到或者说使用这个系统呢?由此认识了IIS,它是一个web容器,天生的多线程,及时响应用户提交的请求返回html页面,这就是我了解的最初的web容器的功能,由此咱们来认识tomcat也并不困难,能够的话,在了解完tomcat后咱们能够继续了解jboss、jetty等,好咱们进入主题。html
咱们在平时开发的过程当中是在使用eclipse时候才启动tomcat,对于一个web容器而言,简而言之,它是系统的一个守护进程,守护着对这台服务器某个端口发起的请求,基于这一点,它就须要一个监听程序,这个监听程序来获取来自这个端口的特定请求的数据,ok,直接点讲,咱们这里使用Socket来获取某个端口,一般是80端口的http请求,经过简单的Javajava
程序的死循环(粗糙的作法,后面逐步优化)来实现不断的获取80端口http请求,来达到监听80端口http请求的目的。java.net包下面的Socket和ServerSocket两个类就能实现咱们对8080端口的监听,去除中间的逻辑代码,咱们只看这个两个类的演绎的话以下:web
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();
以上代码就是获取监听结果。tomcat
这是最简单和最精简的Socket通信原理,基于这个核心点咱们来开发一个简易的,能够提供静态页面访问的 custom tomcat,准备一个index.html文件放到/home/webroot目录下,那么除去拓展上面代码外,咱们还须要一个Response和一个Request。服务器
类设计以下:多线程
HttpServer: 主函数所在类,负载启动ServerSocket和 操做整合Socket监听到的数据,以及返回结果,即操做Response和Request。app
Request: 封装Socket监听到的用户端请求,包括请求的http uri信息。eclipse
Response: 封装须要推送到客户端的结果数据,即咱们须要根据http uri 去本机寻找相应的资源,写给客户端。webapp
言简意赅,进入代码,首先 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,回车结果以下:
本地文件:
好了,夜深啦,就此搁笔了,抛砖引玉,欢迎提议和讨论,这个系列会继续下去,直到一个完整的能够响应一个java action请求的custom tomcat产品出来。
最后附上个人源代码:http://files.cnblogs.com/aspnetdream/Project.zip
参考:《How tomcat works》 做者:Budi Kurniawan & Paul Deck