这里要介绍下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 socket
Socket socket = null;
try
{
socket = serverSocket.accept();
} catch (Exception e)
{
continue;
}
// Hand this socket off to an HttpProcessor
HttpProcessor 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 parse
request = new HttpRequest(input); // create HttpResponse object
response = 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 socket
socket.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
{ // -------------------------------------------------------------- Constants public 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; // ----------------------------------------------------------- Constructors public 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 Variables public 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 Methods public int hashCode()
{
// FIXME
return 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 check
if (requestLine.methodEnd != 0)
requestLine.recycle(); // Checking for a blank line
int chr = 0;
do
{ // Skipping CR or LF
try
{
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 name int maxRead = requestLine.method.length;
int readStart = pos;
int readCount = 0; boolean space = false; while (!space)
{
// if the buffer is full, extend it
if (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 buffer
if (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 URI maxRead = requestLine.uri.length;
readStart = pos;
readCount = 0; space = false; boolean eol = false; while (!space)
{
// if the buffer is full, extend it
if (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 buffer
if (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 request
eol = true;
space = true;
}
requestLine.uri[readCount] = (char) buf[pos];
readCount++;
pos++;
}
//请求航URL结束
requestLine.uriEnd = readCount - 1; // Reading protocol maxRead = requestLine.protocol.length;
readStart = pos;
readCount = 0; while (!eol)
{
// if the buffer is full, extend it
if (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 buffer
if (pos >= count)
{
// Copying part (or all) of the internal buffer to the line
// buffer
int 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.1
requestLine.protocolEnd = readCount; }

  

private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException
{ // Parse the incoming request line
input.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 line
if (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 URI
int 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 name
if (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 URI
String 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 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());
}

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

《深入剖析Tomcat》阅读(三)的更多相关文章

  1. 深入剖析Tomcat会话机制

    1缓存机制 Tomcat默认将Session保存到内存中.但同时,Tomcat也提供了PersistentManager配合不同的Store实现的方式,使Session可以被保存到不同地方(Datab ...

  2. 08.ElementUI 2.X 源码学习:源码剖析之工程化(三)

    0x.00 前言 项目工程化系列文章链接如下,推荐按照顺序阅读文章 . 1️⃣ 源码剖析之工程化(一):项目概览.package.json.npm script 2️⃣ 源码剖析之工程化(二):项目构 ...

  3. 深入剖析tomcat 笔记——第8章 载入器

    深入剖析tomcat 笔记 目录:

  4. 25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment

    25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment 25 BasicUsageEnvironment0基本使用环境基类— ...

  5. 26 BasicUsageEnvironment基本使用环境——Live555源码阅读(三)UsageEnvironment

    26 BasicUsageEnvironment基本使用环境--Live555源码阅读(三)UsageEnvironment 26 BasicUsageEnvironment基本使用环境--Live5 ...

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

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

  7. [转]Apache HTTP Server 与 Tomcat 的三种连接方式介绍

    首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接.事实上 Tomcat 本身已经提供了 HTTP 服务,该服务默认的端口是 8080,装好 tomcat 后通过 8080 端 ...

  8. 深入剖析TOMCAT

    理解tomcat之搭建简易http服务器 做过java web的同学都对tomcat非常熟悉.我们在使用tomcat带来的便利的同时,是否想过tomcat是如何工作的呢?tomcat本质是一个http ...

  9. 《深入剖析Tomcat》源码

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

随机推荐

  1. HTML5另类塔防游戏 -《三国战线》公布

    关于本作 游戏介绍 本游戏是一款另类塔防游戏.本作以三国这段历史为题材,提供了从颍川之战到官渡之战.官渡之战到夷陵之战.夷陵之战到五丈原之战等15个关卡.在每一个关卡中,你会控制一名三国武将与出现的敌 ...

  2. LabVIEW系列——合并错误(VI)的用法

    Merge Errors.vi的功能:1.按顺序搜索错误输入1,2,3,以及错误数组输入中的错误,输出第一个错误.                        2.如果没有错误,也就是错误状态都为F ...

  3. android使用属性动画代替补间动画

    本文参考Android属性动画完全解析(上),初识属性动画的基本用法 android3.0之前一共有两种动画,分别是frame动画和tween动画,关于这两种动画如果不了解可以查看我之前的文章andr ...

  4. JavaScript异步操作

    //伪代码写法,只用来记录结构 function test(){ var dtd=$.Deferred(); if(成功){ dtd.resolve(data1,data2); //传递参数 } el ...

  5. dubbo注册服务IP解析异常及IP解析源码分析

    在使用dubbo注册服务时会遇到IP解析错误导致无法正常访问. 比如: 本机设置的IP为172.16.11.111, 但实际解析出来的是180.20.174.11 这样就导致这个Service永远也无 ...

  6. 用标准版的Eclipse搭建PHP环境

    用标准版的Eclipse搭建PHP环境 ——@梁WP 摘要:用标准版的Eclipse搭建PHP环境. 一.下载Eclipse的PHP插件 百度搜索phpeclipse,看到某条结果是带有SourceF ...

  7. oracle如何获取上个月的月份

    --转载   这个要用到add_months()函数 参数 负数 代表 往前 正数 代表 往后.select to_char(add_months(trunc(sysdate),-1),'yyyymm ...

  8. (转)C# NameValueCollection集合

    1.NameValueCollection类集合是基于 NameObjectCollectionBase 类. 但与 NameObjectCollectionBase 不同,该类在一个键下存储多个字符 ...

  9. Http协议、线程、线程池

    Socket模拟服务端运行代码: 1:启动服务端监听的服务,并接受客户端的连接 1.1 创建Socket Socket listenSocket=new Socket(AddressFamily.In ...

  10. doj常用包

    dojo.raise               抛出一个异常 dojo.errorToString将异常转换为字符串 dojo.render      系统环境对象 dojo.hostenv. ...