Tomcat详解系列(1) - 如何设计一个简单的web容器

Tomcat - 如何设计一个简单的web容器

在学习Tomcat前,很多人先入为主的对它的认知是巨复杂的;所以第一步,在学习它之前,要打破这种观念,我们通过学习如何设计一个最基本的web容器来看它需要考虑什么;进而在真正学习Tomcat时,多把重点放在它的顶层设计上,而不是某一块代码上, 思路永远比具体实现重要的多。@pdai

写在前面

我们在学习一项技术时,需要学习是它的知识体系,而不是碎片化的知识点。在构建知识体系时,我们往往需要先全局的看完一个教程或者一本书,这是构建的基础。这里我推荐大家看两本书:

特别是第一本:经典的《How Tomcat Works》的中文版,它从0基础逐步构建出Tomcat,适合新手;本节中很多内容源自这本书。

本系列在本之后,将转为直接分析Tomcat框架。

基础认知:如何实现服务器和客户端(浏览器)的交互

客户端和服务器端之间的交互式通过Socket来实现的,它术语应用层的协议。

HTTP协议

http协议相关的内容可以参看这里:网络协议 - HTTP 协议详解

Socket

Socket是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。放在两 个不同计算机上的两个应用可以通过连接发送和接受字节流。为了从你的应用发送一条信息到另 一个应用,你需要知道另一个应用的 IP 地址和套接字端口。在 Java 里边,套接字指的是java.net.Socket类。

要创建一个套接字,你可以使用 Socket 类众多构造方法中的一个。其中一个接收主机名称 和端口号:

public Socket (java.lang.String host, int port)

在这里主机是指远程机器名称或者 IP 地址,端口是指远程应用的端口号。例如,要连接 yahoo.com 的 80 端口,你需要构造以下的 Socket 对象:

new Socket ("yahoo.com", 80);

一旦你成功创建了一个 Socket 类的实例,你可以使用它来发送和接受字节流。要发送字节 流,你首先必须调用Socket类的getOutputStream方法来获取一个java.io.OutputStream对象。 要 发 送 文 本 到 一 个 远 程 应 用 , 你 经 常 要 从 返 回 的 OutputStream 对 象 中 构 造 一 个 java.io.PrintWriter 对象。要从连接的另一端接受字节流,你可以调用 Socket 类的 getInputStream 方法用来返回一个 java.io.InputStream 对象。

SeverSocket

Socket 类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候你构造的套接字,现在,假如你想实施一个服务器应用,例如一个 HTTP 服务器或者 FTP 服务器,你需要一种不同的做法。这是因为你的服务器必须随时待命,因为它不知道一个客户端应用什么时候会尝试去连接它。为了让你的应用能随时待命,你需要使用 java.net.ServerSocket 类。这是 服务器套接字的实现。

ServerSocketSocket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信

要创建一个服务器套接字,你需要使用 ServerSocket 类提供的四个构造方法中的一个。你 需要指定 IP 地址和服务器套接字将要进行监听的端口号。通常,IP 地址将会是 127.0.0.1,也 就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的 IP 地址被称为是绑定地址。 服务器套接字的另一个重要的属性是 backlog,这是服务器套接字开始拒绝传入的请求之前,传 入的连接请求的最大队列长度。

其中一个 ServerSocket 类的构造方法如下所示:

public ServerSocket(int port, int backLog, InetAddress bindingAddress);

一个简单web容器的设计和实现:对静态资源

准备,这个例子来源于《How Tomcat Works》, 可以从这里下载源码

注意:当你跑如下程序时,可能会由于浏览器新版本不再支持的HTTP 0.9协议,而造成浏览器页面没有返回信息。

组件设计

根据上述的基础,我们可以看到,我们只需要提供三个最基本的类,分别是:

  • Request - 表示请求,这里表示浏览器发起的HTTP请求
  • HttpServer - 表示处理请求的服务器,同时这里使用我们上面铺垫的ServerSocket
  • Reponse - 表示处理请求后的响应, 这里表示服务器对HTTP请求的响应结果

组件实现

从上图中我们可以看到,组织这几个类的入口在Server的启动方法中,即main方法中, 所以我们透过main方法从Server类进行分析:

  • Server是如何启动的?
public class HttpServer {
   

  // 存放静态资源的位置
  public static final String WEB_ROOT =
    System.getProperty("user.dir") + File.separator  + "webroot";

  // 关闭Server的请求
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

  // 是否关闭Server
  private boolean shutdown = false;

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

  public void await() {
   
    // 启动ServerSocket
    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);
    }

    // 循环等待一个Request请求
    while (!shutdown) {
   
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
   
        // 创建socket</
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值