Tomcat深入剖析学习01

最近由于兴趣需要,开始准备对tomcat服务器研究一番,选择了网上推荐较多的经典《深入剖析tomcat》,虽然选用的tomcat服务器版本较旧,但是原理方面的渐进式讲解十分精彩。所以选为学习材料。

如同动物进化一般,书本从实现一个最为简单的http服务器讲起,逐渐丰富其功能并不断解耦演进,过程中穿插了tomcat技术实现的原理,能够让读者很好的消化吸收。

Tomcat深入剖析学习01-一个简单的Web服务器

一.关键概念

HTTP协议:一个基于请求-响应式的协议,请求与响应的报文格式都已约定,便于统一解析

HTTP请求:包含1.请求方式-uri-协议版本(请求的第一行) 2.请求头 3.实体

  eg:

    GET /sample.jsp HTTP/1.1                                                                    -------------------------------------->         第一行:包含请求方式GET;uri:/sample.jsp;协议版本:HTTP/1.1  注意三项之间用空格分割
    Accept:image/gif.image/jpeg,*/*                                                             -------------------------------------->         请求头开始           
    Accept-Language:zh-cn
    Connection:Keep-Alive
    Host:localhost
    User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
    Accept-Encoding:gzip,deflate                                                                -------------------------------------->          请求头结束
                                                                                                                             -------------------------------------->          注:请求头与实体之间为一行只包含CRLF的空行分割
    username=jinqiao&password=1234                                                      -------------------------------------->          实体

HTTP响应:包含1.协议-状态码-描述 2.响应头  3实体

  eg: 

    HTTP/1.1 200 OK                                                                                  -------------------------------------->          第一行:包含协议版本HTTP/1.1;状态码200;描述OK
    Server:Apache Tomcat/5.0.12                                                               -------------------------------------->          响应头开始
    Date:Mon,6Oct2003 13:23:42 GMT
    Content-Length:112                                                                               -------------------------------------->          响应头结束
                                                                                                                             -------------------------------------->          注:响应头与实体之间为一行只包含CRLF的空行分割
    <html>                                                                                                    -------------------------------------->          实体(此处为html代码)
      <head>
      <title>HTTP响应示例<title>
      </head>
    <body>
      Hello HTTP!
    </body>
    </html>

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服务器的样例。

转载于:https://www.cnblogs.com/liuweiblog/p/7133065.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值