Tomcat深入剖析学习01
最近由于兴趣需要,开始准备对tomcat服务器研究一番,选择了网上推荐较多的经典《深入剖析tomcat》,虽然选用的tomcat服务器版本较旧,但是原理方面的渐进式讲解十分精彩。所以选为学习材料。
如同动物进化一般,书本从实现一个最为简单的http服务器讲起,逐渐丰富其功能并不断解耦演进,过程中穿插了tomcat技术实现的原理,能够让读者很好的消化吸收。
Tomcat深入剖析学习01-一个简单的Web服务器
一.关键概念
HTTP协议:一个基于请求-响应式的协议,请求与响应的报文格式都已约定,便于统一解析
HTTP请求:包含1.请求方式-uri-协议版本(请求的第一行) 2.请求头 3.实体
eg:
HTTP响应:包含1.协议-状态码-描述 2.响应头 3实体
eg:
<head>
Socket套接字:Socket是网络服务访问点,程序可以通过Socket实例从网络中读取或者写入数据,达到通信的目的。http是约定的协议,那么socket是将http协议程序化使用的一种手段。
二.基础类
主要介绍两个类,首先是,Socket类用于创建一个远程访问点,通过与Socket实例可以向网络远程访问点接受或发送数据。
其次,不要忘记我们的任务是创建一个服务器,这时用到了ServerSocket类,通过这个类,可以创建一个服务访问点,可以对外提供服务。
1.java.net.Socket
创建socket方法与指定主机ip,端口绑定:public Socket (java.Lang.String host,int port)
创建socket后就可以获取其输入,输出流对象进行通信,下面是一种通信实现方法
1 //创建与指定host,ip绑定的socket 2 Socket socket = new Socket("127.0.0.1",8080); 3 //从socket获取输入输出流 4 OutputStream os = socket.getOutputStream(); 5 InputStream is = socket.getInputStream(); 6 //发出HTTP request请求,填写请求头部,此处实体为空 7 //PrintWriter类是向输出流格式化输出数据的类,若制定自动刷新,则其println,printf,format等方法时会刷新生效 8 Boolean autoflush = true; 9 PrintWriter out = new PrintWriter(socket.getOutputStream(),autoflush); 10 out.println("GET /index.html HTTP/1.1"); 11 out.println("Host:localhost:8080"); 12 out.println(); 13 14 //读取response 15 BufferedReader in = new BufferedReader(new InputStreamReader(is)); 16 boolean loop = true; 17 StringBuffer sb = new StringBuffer(8096); 18 while(loop){ 19 if(in.ready()){ 20 int i = 0; 21 while(i!=-1){ 22 i=in.read(); 23 sb.append((char)i); 24 } 25 loop = false; 26 } 27 } 28 System.out.println(sb.toString()); 29 socket.close();
2.java.net.ServerSocket
服务器套接字ServerSocket,当需要创建一个http服务器,并对外提供服务时,需要使用这个类,这个类提供了一系列方法,对应服务器的各种状态。
ServerSocekt创建方法 poublic ServerSocket(int port,int backLog,InetAddress bindingAddress)
注意与Socket类的初始化方法相比,多了一个backLog参数,指明服务器允许的连接请求服务队列长度;另外,传入的地址类型为InetAddress,一般可以采用InetAddress.getByName(String ip)获得ip字符串对应的 InetAddress类型。
eg:serverSocket = new ServerSocket(8080,1,InetAddress.getByName("127.0.0.1"));
这样在初始化一个serverSocket后,可以调用其accept()方法,阻塞式监听对应端口端口的请求。
服务器相关代码片段如下:
1 ServerSocket serverSocket = null; 2 int port = 8888; 3 try{ 4 serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1")); 5 }catch(IOException ex){ 6 ex.printStackTrace(); 7 System.exit(1); 8 } 9 //此处判断shutdown标志 10 while(!shutdown){ 11 Socket socket = null; 12 InputStream input = null; 13 OutputStream output = null; 14 try{ 15 //阻塞式等待一个请求,并返回请求端的socket对象 16 socket = serverSocket.accept(); 17 ...... 18 }
三.实现一个简单的http web服务器(代码)
HttpServer:
1 package com.liuwei.SimpleWebServer; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.io.OutputStream; 9 import java.io.PrintWriter; 10 import java.net.InetAddress; 11 import java.net.ServerSocket; 12 import java.net.Socket; 13 import java.net.UnknownHostException; 14 15 public class HttpServer { 16 17 //服务器的web项目所在目录 18 public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; 19 20 //shutdown command 21 public static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; 22 23 //the shutdown command received 24 private static boolean shutdown = false; 25 26 public static void main(String[] args) { 27 HttpServer server = new HttpServer(); 28 server.await(); 29 } 30 //服务器启动方法 31 public void await(){ 32 ServerSocket serverSocket = null; 33 int port = 8888; 34 try{ 35 serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1")); 36 }catch(IOException ex){ 37 ex.printStackTrace(); 38 System.exit(1); 39 } 40 //此处判断shutdown标志 41 while(!shutdown){ 42 Socket socket = null; 43 InputStream input = null; 44 OutputStream output = null; 45 try{ 46 //阻塞式等待一个请求,并返回请求端的socket对象 47 socket = serverSocket.accept(); 48 //获取socket对象中的输入输出流对象 49 input = socket.getInputStream(); 50 output = socket.getOutputStream(); 51 52 //create request object and parse 53 Request request = new Request(input); 54 request.parse(); 55 System.out.println(request.getUri()); 56 57 //create response object 58 Response response = new Response(output); 59 response.setRequest(request); 60 response.sendStaticResource(); 61 62 //socket close 63 socket.close(); 64 65 //check if requesturl is shutdown command 66 shutdown = request.getUri().equals(SHUTDOWN_COMMAND); 67 }catch(Exception e){ 68 e.printStackTrace(); 69 continue; 70 } 71 } 72 } 73 }
Request:
1 package com.liuwei.SimpleWebServer; 2 3 import java.io.InputStream; 4 5 //HTTP请求类 6 public class Request { 7 //请求对象中的输入流对象 8 private InputStream input; 9 10 private String uri; 11 //构造函数 12 public Request(InputStream input){ 13 this.input = input; 14 } 15 //解析请求的函数 16 public void parse(){ 17 StringBuffer request = new StringBuffer(); 18 byte[] buffer = new byte[2048]; 19 int i=-1; 20 try{ 21 i = input.read(buffer); 22 }catch(Exception e){ 23 e.printStackTrace(); 24 } 25 for(int j=0;j<i;j++){ 26 request.append((char)buffer[j]); 27 } 28 System.out.println(request.toString()); 29 this.uri = parseUri(request.toString()); 30 } 31 //分析请求的静态资源uri,从两个空格间获取 32 public String parseUri(String request){ 33 int index1,index2; 34 index1 = request.indexOf(" "); 35 if (index1 != -1){ 36 index2 = request.indexOf(" ", index1+1); 37 if (index2>index1){ 38 return request.substring(index1+1,index2); 39 } 40 } 41 return null; 42 } 43 public String getUri(){ 44 return uri; 45 } 46 }
Response:
package com.liuwei.SimpleWebServer; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; public class Response { private Request request; public OutputStream output; private static final int BufferLength = 1024; public Response(OutputStream output){ this.output = output; } public void setRequest(Request request){ this.request = request; } //发送静态资源方法 public void sendStaticResource() throws IOException { byte[] buffer = new byte[BufferLength]; FileInputStream fis = null; try{ //查找请求中资源是否在root文件夹中存在,存在则写到output中,未存在在output写404信息 File file = new File(HttpServer.WEB_ROOT,request.getUri()); if(file.exists()){ fis = new FileInputStream(file); int num = fis.read(buffer,0,BufferLength); if(num!=-1){ output.write(buffer,0,num); fis.read(buffer,0,BufferLength); } }else{ String errorMsg = "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(errorMsg.getBytes()); } }catch(Exception ex){ ex.printStackTrace(); }finally{ if(null!=fis){ fis.close(); } } } }
四.类功能说明
下图展示了此程序中涉及到的类及功能。
可见:HttpServer类承担了主流程的绝大部分实现,自定义的Reqeust,Response类提供了两个重要的可调用方法,分别是解析方法,返回uri指定资源的方法供server调用。
后续章节会在这个简单的httpserver程序基础上,进行功能解耦,功能添加等工作,最终成为一个最为接近tomcat服务器的样例。