《HowTomcatWorks》读书笔记_第一章

4 篇文章 0 订阅
4 篇文章 0 订阅

写在前面

《How Tomcat Works》读书笔记系列博客主要对该书中的源码进行注解,顺便梳理了Tomcat处理http请求的流程,以及流程中涉及到的组件。本人非常喜欢这本书讲解源码的方式,从实现一个简单的服务器开始,到慢慢扩充其他功能组件,让读者不会一下子陷入到源码的海洋中而晕头转向。虽然该书是对Tomcat4&Tomcat5的上古版本进行讲解,但是不妨碍我们学习tomcat中的编码实践、设计思想和架构,阅读该书或者本系列博客时,建议配合《How Tomcat Works》的源码,博客中也只是贴出部分源码,完整源码下载地址:https://brainysoftware.com/source/9780975212806.zip

简介

第一章提供了一个非常简单的web"服务器",服务器之所以加引号,就是因为它简单到只有三个类:

  • HttpServer: 接受客户端请求
  • Request:简单的请求类,通过InputStream读取解析请求的uri
  • Response::简单的响应类,通过FileInputstream读取静态资源,并传入到Socket的OutputStream中,回传给客户端

源码

HttpServer

public class HttpServer {

  /** 
   * 静态文件存放目录
   */
  public static final String WEB_ROOT =
    System.getProperty("user.dir") + File.separator  + "webroot";

  // shutdown command
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

  // the shutdown command received
  private boolean shutdown = false;

  public static void main(String[] args) {
    HttpServer server = new HttpServer();
    server.await();
  }

  public void await() {
    ServerSocket serverSocket = null;
    int port = 8080;
    try {
      serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
      e.printStackTrace();
      System.exit(1);
    }

    // 循环等待客户端请求,BIO单线程模型
    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
        socket = serverSocket.accept();
        input = socket.getInputStream();
        output = socket.getOutputStream();

        // 传入字节输入流,读取http请求报文
        Request request = new Request(input);
        request.parse();

        // 传入字节输出流,发送响应数据(这里是静态文件)
        Response response = new Response(output);
        response.setRequest(request);
        response.sendStaticResource();

        // Close the socket
        socket.close();

        //check if the previous URI is a shutdown command
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        continue;
      }
    }
  }
}

HttpServer采用的是BIO的单线程模型,一个客户端请求到来,服务器从serverSocket.accept()方法返回,并且其他的客户端请求会陷入阻塞,直到服务器处理完当前请求,才会处理下一请求。
作为众多Java的Socket网络编程教程中的入门级demo,这里不做多余的阐述。

Request

public class Request {

  private InputStream input;
  private String uri;

  public Request(InputStream input) {
    this.input = input;
  }

  public void parse() {
    // Read a set of characters from the socket
    StringBuffer request = new StringBuffer(2048);
    int i;
    byte[] buffer = new byte[2048];
    try {
      i = input.read(buffer);
    }
    catch (IOException e) {
      e.printStackTrace();
      i = -1;
    }
    for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
    }
    System.out.print(request.toString());
    uri = parseUri(request.toString());
  }

  // 对uri进行简单的处理
  private String parseUri(String requestString) {
    int index1, index2;
    index1 = requestString.indexOf(' ');
    if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }

  public String getUri() {
    return uri;
  }

}

需要注意的是:Request类读取输入流数据,并没有采用循环的形式,而是用了一个长度为2048的byte[]数组读取一次数据。在http请求报文数据较多的情况下,会产生数据截断。

Response

public class Response {

  private static final int BUFFER_SIZE = 1024;
  Request request;
  OutputStream output;

  public Response(OutputStream output) {
    this.output = output;
  }

  public void setRequest(Request request) {
    this.request = request;
  }

  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    try {
      File file = new File(HttpServer.WEB_ROOT, request.getUri());
      if (file.exists()) {
        fis = new FileInputStream(file);
        int ch = fis.read(bytes, 0, BUFFER_SIZE);
        while (ch!=-1) {
          output.write(bytes, 0, ch);
          ch = fis.read(bytes, 0, BUFFER_SIZE);
        }
      }
      else {
        // file not found
        String errorMessage = "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(errorMessage.getBytes());
      }
    }
    catch (Exception e) {
      // thrown if cannot instantiate a File object
      System.out.println(e.toString() );
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }
}

流程图

在这里插入图片描述

注:流程图就是对源码处理请求的过程做了另一种形式的转化,主要是加深对服务器处理请求过程的理解

运行程序

浏览器中输入:http://localhost:8080/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值