这里要介绍下Tomcat的一个重要设计方法,Catalina设计方式。

Servlet容器是一个复杂系统,但是,它有三个基本任务,对每个请求,servlet容器会为其完成以下三个操作:

1.创建一个Request对象,用可能会在调用的Servlet中使用到的信息填充该request对象,如参数、头、cookie、查询字符串、URI等。

request对象是javax.servlet.ServletRequest接口或javax.servlet.http.ServletRequest接口的一个实例。

2.创建一个调用Servlet的response对象,用来向Web客户端发送响应。response对象是javax.servlet.ServletResponse接口或javax.servlet.http.ServletResponse接口的一个实例。

3.调用Servlet的service()方法,将request对象和response对象作为参数传入。Servlet从request对象中读取信息,并通过response对象发送响应信息。

-----------------------------------------------------------------------------------------------------

Catalina是一个成熟的软件,设计和开发得十分优雅。(其实我在公司项目中发现的使用方法也是这样的,十分的优雅的软件开发方式)

Catalina使得软件开发功能结构编程模块化的,基于上文提到的servlet容器的任务,可以将Catalina划分为两个模块:连接器(connector)和容器(container)

连接器负责将一个请求与容器相关联。它的工作包括为它接收到的每一个HTTP请求创建一个Request对象和一个Response对象。然后,它将处理过程交给容器。容器从连接器中接收到Request对象和Response对象,并负责调用相应的Servlet的service()方法。

简而言这模式就是connector负责接收请求,创建解析请求需要的Request对象和Response对象,然后将请求分发到容器中处理。

(普通的服务端结构一般也是这么设计的,IO部分负责接收请求,创建解析请求的对象,然后将请求丢进线程池中进行处理)

xxxx

--------------------------------------------

现在根据模块开发Servlet容器:

1)连接器   HttpConnector和HttpProcessor 连接器及其支持类 标示HTTP请求的类(HTTPRequest)及其支持类 负责HTTP响应的类,HttpResponse以及其支持类,外观类以及常量类

2)启动模块  启动模块包括一个类,就是startup.Bootstrap 类,负责启动应用程序。

3)核心模块

启动类

package ex03.pyrmont.startup;import ex03.pyrmont.connector.http.HttpConnector;public final class Bootstrap
{public static void main(String[] args){HttpConnector connector = new HttpConnector();connector.start();}
}

关于这个建议具有连接器和容器功能的Http服务器

http://www.cnblogs.com/wuxinliulei/p/4967625.html

---------------------------------------------------------------------------

重点介绍这个模型当中对HTTP请求信息的parse

HttpConnector类在接收到连接请求后,将请求派发给一个HttpProcessor对象处理

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;public class HttpConnector implements Runnable
{boolean stopped;private String scheme = "http";public String getScheme(){return scheme;}public void run(){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);}while (!stopped){// Accept the next incoming connection from the server socketSocket socket = null;try{socket = serverSocket.accept();} catch (Exception e){continue;}// Hand this socket off to an HttpProcessorHttpProcessor processor = new HttpProcessor(this);processor.process(socket);}}public void start(){Thread thread = new Thread(this);thread.start();}
}

将此次连接获取的Socket对象传入,调用HttpProcessor对象的process方法来处理Http请求

下面是HttpProcessor对象的process方法,重点关注SocketInputStream对象,SocketInputStream对象是对InputStream对象的一层包装,封装了一些对Http请求解析方便的方法。

public void process(Socket socket){SocketInputStream input = null;OutputStream output = null;try{input = new SocketInputStream(socket.getInputStream(), 2048);output = socket.getOutputStream();// create HttpRequest object and parserequest = new HttpRequest(input);// create HttpResponse objectresponse = new HttpResponse(output);response.setRequest(request);response.setHeader("Server", "Pyrmont Servlet Container");parseRequest(input, output);parseHeaders(input);// check if this is a request for a servlet or a static resource// a request for a servlet begins with "/servlet/"if (request.getRequestURI().startsWith("/servlet/")){ServletProcessor processor = new ServletProcessor();processor.process(request, response);} else{StaticResourceProcessor processor = new StaticResourceProcessor();processor.process(request, response);}// Close the socketsocket.close();// no shutdown for this application} catch (Exception e){e.printStackTrace();}}

process方法当中调用了parseRequest方法,该方法传入SocketInputStream对象和OutputStream对象,方法对输入流当中的http请求进行了解析,

这里牵涉到几个帮助类

1:HttpRequestLine类,定义在HttpProcessor方法的成员变量当中。private HttpRequestLine requestLine = new HttpRequestLine();

该类中对http请求内容中的请求行(http请求依次分为请求行、请求头、/r/n,请求体四部分),第一行内容进行了解析 即

POST /servlet/primitServlet Http/1.1    GET  /sample/hello.jsp   HTTP/1.1 这样的内容

HttpRequestLine类是一个可以复用的类(PS:但是HttpProcessor在这个框架当中没有对象池的复用,所以没有卵用),对httpMethod   httpUrl  protocol 三部分做了处理。其中还提供了字符串匹配的方法indexOf(char[] xxx) indexOf(String xxx)

/*** HTTP request line enum type.*/final class HttpRequestLine
{// -------------------------------------------------------------- Constantspublic static final int INITIAL_METHOD_SIZE = 8;public static final int INITIAL_URI_SIZE = 64;public static final int INITIAL_PROTOCOL_SIZE = 8;public static final int MAX_METHOD_SIZE = 1024;public static final int MAX_URI_SIZE = 32768;public static final int MAX_PROTOCOL_SIZE = 1024;// ----------------------------------------------------------- Constructorspublic HttpRequestLine(){this(new char[INITIAL_METHOD_SIZE], 0, new char[INITIAL_URI_SIZE], 0, new char[INITIAL_PROTOCOL_SIZE], 0);}public HttpRequestLine(char[] method, int methodEnd, char[] uri, int uriEnd, char[] protocol, int protocolEnd){this.method = method;this.methodEnd = methodEnd;this.uri = uri;this.uriEnd = uriEnd;this.protocol = protocol;this.protocolEnd = protocolEnd;}// ----------------------------------------------------- Instance Variablespublic char[] method;public int methodEnd;public char[] uri;public int uriEnd;public char[] protocol;public int protocolEnd;// ------------------------------------------------------------- Properties// --------------------------------------------------------- Public Methods/*** Release all object references, and initialize instance variables, in* preparation for reuse of this object.*/public void recycle(){methodEnd = 0;uriEnd = 0;protocolEnd = 0;}/*** Test if the uri includes the given char array.*/public int indexOf(char[] buf){return indexOf(buf, buf.length);}/*** Test if the value of the header includes the given char array.*/public int indexOf(char[] buf, int end){char firstChar = buf[0];int pos = 0;while (pos < uriEnd){pos = indexOf(firstChar, pos);if (pos == -1)return -1;if ((uriEnd - pos) < end)return -1;for (int i = 0; i < end; i++){if (uri[i + pos] != buf[i])break;if (i == (end - 1))return pos;}pos++;}return -1;}/*** Test if the value of the header includes the given string.*/public int indexOf(String str){return indexOf(str.toCharArray(), str.length());}/*** Returns the index of a character in the value.*/public int indexOf(char c, int start){for (int i = start; i < uriEnd; i++){if (uri[i] == c)return i;}return -1;}// --------------------------------------------------------- Object Methodspublic int hashCode(){// FIXMEreturn 0;}public boolean equals(Object obj){return false;}}

  

我们要注意到parseRequest方法的第一行代码 input.readRequestLine(requestLine) SocketInputStream的该方法完成了对HttpRequestLine对象的初始化,主要初始化了六个对象

public char[] method;  //method对象
public int methodEnd; //method对象的结束位置
public char[] uri;         //uri对象
public int uriEnd;        //uri对象的结束位置
public char[] protocol; //协议对象
public int protocolEnd;//协议对象的结束位置

注意read方法完成了对count的初始化,并且将第一行内容缓存到char[] buf当中

/*** Read byte.*/public int read() throws IOException{if (pos >= count){fill();if (pos >= count)return -1;}return buf[pos++] & 0xff;}

  /*** Fill the internal buffer using data from the undelying input stream.*/protected void fill() throws IOException{pos = 0;count = 0;int nRead = is.read(buf, 0, buf.length);if (nRead > 0){count = nRead;}}

  

public void readRequestLine(HttpRequestLine requestLine) throws IOException{// Recycling checkif (requestLine.methodEnd != 0)requestLine.recycle();// Checking for a blank lineint chr = 0;do{ // Skipping CR or LFtry{chr = read();} catch (IOException e){chr = -1;}} while ((chr == CR) || (chr == LF));if (chr == -1)throw new EOFException(sm.getString("requestStream.readline.error"));pos--;// Reading the method nameint maxRead = requestLine.method.length;int readStart = pos;int readCount = 0;boolean space = false;while (!space){// if the buffer is full, extend itif (readCount >= maxRead){if ((2 * maxRead) <= HttpRequestLine.MAX_METHOD_SIZE){char[] newBuffer = new char[2 * maxRead];System.arraycopy(requestLine.method, 0, newBuffer, 0, maxRead);requestLine.method = newBuffer;maxRead = requestLine.method.length;} else{throw new IOException(sm.getString("requestStream.readline.toolong"));}}// We're at the end of the internal bufferif (pos >= count){int val = read();if (val == -1){throw new IOException(sm.getString("requestStream.readline.error"));}pos = 0;readStart = 0;}if (buf[pos] == SP){space = true;}requestLine.method[readCount] = (char) buf[pos];readCount++;pos++;}requestLine.methodEnd = readCount - 1;// Reading URImaxRead = requestLine.uri.length;readStart = pos;readCount = 0;space = false;boolean eol = false;while (!space){// if the buffer is full, extend itif (readCount >= maxRead){if ((2 * maxRead) <= HttpRequestLine.MAX_URI_SIZE){char[] newBuffer = new char[2 * maxRead];System.arraycopy(requestLine.uri, 0, newBuffer, 0, maxRead);requestLine.uri = newBuffer;maxRead = requestLine.uri.length;} else{throw new IOException(sm.getString("requestStream.readline.toolong"));}}// We're at the end of the internal bufferif (pos >= count){int val = read();if (val == -1)throw new IOException(sm.getString("requestStream.readline.error"));pos = 0;readStart = 0;}if (buf[pos] == SP){space = true;} else if ((buf[pos] == CR) || (buf[pos] == LF)){// HTTP/0.9 style requesteol = true;space = true;}requestLine.uri[readCount] = (char) buf[pos];readCount++;pos++;}//请求航URL结束requestLine.uriEnd = readCount - 1;// Reading protocolmaxRead = requestLine.protocol.length;readStart = pos;readCount = 0;while (!eol){// if the buffer is full, extend itif (readCount >= maxRead){if ((2 * maxRead) <= HttpRequestLine.MAX_PROTOCOL_SIZE){char[] newBuffer = new char[2 * maxRead];System.arraycopy(requestLine.protocol, 0, newBuffer, 0, maxRead);requestLine.protocol = newBuffer;maxRead = requestLine.protocol.length;} else{throw new IOException(sm.getString("requestStream.readline.toolong"));}}// We're at the end of the internal bufferif (pos >= count){// Copying part (or all) of the internal buffer to the line// bufferint val = read();if (val == -1)throw new IOException(sm.getString("requestStream.readline.error"));pos = 0;readStart = 0;}if (buf[pos] == CR){// Skip CR.} else if (buf[pos] == LF){eol = true;} else{requestLine.protocol[readCount] = (char) buf[pos];readCount++;}pos++;}// HTTP/1.1requestLine.protocolEnd = readCount;}

  

private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException{// Parse the incoming request lineinput.readRequestLine(requestLine);String method = new String(requestLine.method, 0, requestLine.methodEnd);String uri = null;String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);// Validate the incoming request lineif (method.length() < 1){throw new ServletException("Missing HTTP request method");} else if (requestLine.uriEnd < 1){throw new ServletException("Missing HTTP request URI");}// Parse any query parameters out of the request URIint question = requestLine.indexOf("?");if (question >= 0){request.setQueryString(new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1));uri = new String(requestLine.uri, 0, question);} else{request.setQueryString(null);uri = new String(requestLine.uri, 0, requestLine.uriEnd);}// Checking for an absolute URI (with the HTTP protocol)if (!uri.startsWith("/")){int pos = uri.indexOf("://");// Parsing out protocol and host nameif (pos != -1){pos = uri.indexOf('/', pos + 3);if (pos == -1){uri = "";} else{uri = uri.substring(pos);}}}// Parse any requested session ID out of the request URIString match = ";jsessionid=";int semicolon = uri.indexOf(match);if (semicolon >= 0){String rest = uri.substring(semicolon + match.length());int semicolon2 = rest.indexOf(';');if (semicolon2 >= 0){request.setRequestedSessionId(rest.substring(0, semicolon2));rest = rest.substring(semicolon2);} else{request.setRequestedSessionId(rest);rest = "";}request.setRequestedSessionURL(true);uri = uri.substring(0, semicolon) + rest;} else{request.setRequestedSessionId(null);request.setRequestedSessionURL(false);}// Normalize URI (using String operations at the moment)String normalizedUri = normalize(uri);// Set the corresponding request properties((HttpRequest) request).setMethod(method);request.setProtocol(protocol);if (normalizedUri != null){((HttpRequest) request).setRequestURI(normalizedUri);} else{((HttpRequest) request).setRequestURI(uri);}if (normalizedUri == null){throw new ServletException("Invalid URI: " + 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 void parse()
{// Read a set of characters from the socketStringBuffer 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());
}

其实上面的处理方式是不严谨的,应该读到/r/n之后停止,而不是固定读取2048字节,然后处理这读出来的字节。

转载于:https://www.cnblogs.com/wuxinliulei/p/4960670.html

《深入剖析Tomcat》阅读(三)相关推荐

  1. 【深入剖析Tomcat笔记】第一篇 基础知识储备

    基础知识储备 最近突然在想,做了这么久的WEB相关,像tomcat.apache这些服务器究竟是什么东西,恰好碰到<How Tomcat Works>(中文版<深入剖析Tomcat& ...

  2. 深入剖析Tomcat(How Tomcat Works)

    <深入剖析Tomcat>前言(作者:Budi Kurniawan & Paul Deck) 欢迎阅读本书.本书剖析了Tomcat 4.1.12版本和Tomcat 5.0.18版本的 ...

  3. 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment

    24 UsageEnvironment使用环境抽象基类--Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类--Live555源码阅读 ...

  4. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  5. setup.s 解读——Linux-0.11 剖析笔记(三)

    题目:setup.s 解读--Linux-0.11 剖析笔记(三) 更新记录 版本 时间 修订内容 1.0 2018-4-14 增加了"获取显示模式"这一节,AL取值的表格 2.0 ...

  6. mybatis源码阅读(三):mybatis初始化(下)mapper解析

    转载自 mybatis源码阅读(三):mybatis初始化(下)mapper解析 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单. ...

  7. Tomcat运行三种模式:http-bio|http-nio|http-apr介绍

    转自<tomcat运行三种模式:http-bio|http-nio|http-apr介绍> Tomcat是一个小型的轻量级应用服务器,也是JavaEE开发人员最常用的服务器之一.不过,许多 ...

  8. tomcat中三种部署项目的方法(转)

    tomcat中三种部署项目的方法 第一种方法:在tomcat中的conf目录中,在server.xml中的,<host/>节点中添加:  <Context path="/h ...

  9. 《深入剖析Tomcat》源码

    <深入剖析Tomcat>翻译自<How Tomcat Works> 可以到官网下载:https://brainysoftware.com/download 官网下载比较慢,我就 ...

  10. 联邦学习论文阅读三:ChainFL

    联邦学习论文阅读三:ChainFL Secure and Efficient Federated Learning Through Layering and Sharding Blockchain 论 ...

最新文章

  1. 索尼因需量大增而增加PSVR产量
  2. 作为我的第一篇csdn博客吧
  3. SpringCloud 定义Eureka服务端、Eureka服务信息、Eureka发现管理、Eureka安全配置、Eureka-HA机制、 Eureka服务打包部署
  4. java是纯_让你真正了解Java(纯干货)
  5. pytorch 图像增强
  6. 中国女性灭菌装置行业市场供需与战略研究报告
  7. 语音识别数据库成为了人工智能的核心(转发)
  8. ORACLE 树形遍历查询根节点、父节点、子节点
  9. 【阿里云双11薅羊毛攻略】领限量手办详细步骤
  10. 110道 Elasticsearch面试题及答案(持续更新)
  11. ros::nodehandle常规操作
  12. 从零开始之uboot、移植uboot2017.01(四、uboot的重定位)
  13. 用单链表编程实现一个简易的高校学籍管理系统_SSM框架实现学生学籍管理系统...
  14. Vue生命周期(vue life cycle) 详解
  15. 计算机网络(标准化工作及相关组织)
  16. DNS被污染后怎么才能解决?
  17. centos 7.6 戴尔服务器 安装HP LaserJet 1020 Plus 打印机驱动
  18. 怎么用python编程实现二次差值多项式_双三次插值算法详解 含python实现
  19. Python 模块安装详细
  20. 【JokerのKCU105】SGMII。

热门文章

  1. 约瑟夫环(丢手绢问题)
  2. leetcode112 路径总和
  3. Linux进程之间通信 消息队列
  4. google python的风格规范
  5. C++If与Switch语句
  6. 关于 std::set/std::map 的几个为什么
  7. 最新研究显示人类智力逐渐下降且变得更愚蠢
  8. 程序员面试中遇到问题
  9. php 两变量值互换 方法
  10. 浅谈爬虫 《一》 ===python