《深入剖析Tomcat》阅读(三)

 这里要介绍下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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459542.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美图秀秀计算机教程,美图秀秀怎么抠图 美图秀秀抠图详细教程

怎么抠图&#xff1f;这是很多人在进行图片处理过程中经常处理的问题。对于那些专业人士来说&#xff0c;他们大多数用的是PS软件。但是对于绝大多数没有接触过PS的小白来说&#xff0c;怎么抠图成为了一大难题。其实&#xff0c;用过美图秀秀这款软件的朋友来说&#xff0c;它…

磁盘及文件系统的管理

分区是为了创建文件系统MBR&#xff1a;创建分区后&#xff0c;为了能够快速的存取文件就有了文件系统inode&#xff1a;中存储了文件属组&#xff0c;等与文件数据没有关系的文件属性信息&#xff0c;但是没有文件名每次访问某个目录的文件时是会进行缓存的&#xff0c;在一定…

今天升级win10.vs调试程序各种崩溃

今天升级win10.vs调试程序各种崩溃。感觉代码没问题。崩溃时有时没有。不知道是win10的问题&#xff0c;好真是我的代码问题。 问题1&#xff1a; 尝试读取或写入受保护的内存。这通常指示其他内存已损坏 不过。当我写这个文章时。想要重现一下。却又不出现了。 转载于:https:/…

计算机控制分离性原理是什么,(第12讲)状态观测器和分离原理.ppt

《(第12讲)状态观测器和分离原理.ppt》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《(第12讲)状态观测器和分离原理.ppt(26页珍藏版)》请在人人文库网上搜索。1、现代控制理论,(第10讲 2007年12月) 状态观测器 带观测器的闭环系统 分离原理 自动化教研室 谭功全,状态…

Cocos2d-x 3.8.1+Cocos Studio 2.3.2捉虫记之控制场景文件中的骨骼动画

Cocos2d-x 3.8.1Cocos Studio 2.3.2捉虫记之控制场景文件中的骨骼动画引子这段时间一直努力在把早期版本的拇指接龙游戏&#xff08;Cocos2d-x 2.2.3CocoStudio 1.4.0.1&#xff09;升级到当前相对稳定的高大上环境——Cocos2d-x 3.8.1Cocos Studio 2.3.2。行程中遇到不少麻烦&…

用JSmooth制作java jar文件的可运行exe文件教程【图文】

这是我之前在个人博客3yj上面写的一篇文章&#xff0c;如今转载过来&#xff0c;原文地址 &#xff08;这不是广告哦&#xff09; 几年前&#xff0c;刚接触java的是&#xff0c;就想用一些方法把自己的劳动果实保护起来&#xff0c;曾经也用过非常多这种工具&#xff0c;有一个…

全国计算机vb考试经典程序设计,全国计算机二级《VB语言程序设计》考试要点...

全国计算机二级《VB语言程序设计》考试要点VB语言程序设计是计算机二级考试的科目之一&#xff0c;考生们在备考是要熟悉科目的知识要点&#xff0c;有针对性地进行备考。下面百分网小编为大家搜索整理了关于二级《VB语言程序设计》考试要点&#xff0c;欢迎参考练习&#xff0…

MipMap

MipMap首先从MIPMAP的原理说起&#xff0c;它是把一张贴图按照2的倍数进行缩小。直到1X1。把缩小的图都存储起来。在渲染时&#xff0c;根据一个像素离眼睛为之的距离&#xff0c;来判断从一个合适的图层中取出texel颜色赋值给像素。在D3D和OGL都有相对应的API控制接 透过它的工…

计算机一级繁体字转换,繁体字转换器

有的朋友偏爱繁体字书写对联&#xff0c;繁体字对联看着也更端庄大气&#xff0c;今天出国留学网小编给大家整理提供了关于春节对联繁体字的精彩内容&#xff0c;欢迎阅读。通用春节对联大全带横批1. 上联&#xff1a;大順大財大吉利下联&#xff1a;新春新喜新世紀【橫批】&am…

ln 命令

ln是linux中又一个非常重要命令&#xff0c;它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录&#xff0c;用到相同的文件时&#xff0c;我们不需要在每一个需要的目录下都放一个必须相同的文件&#xff0c;我们只要在某个固定的目录&#xff0…

行内元素中间出现空隙

设置父级为font-size:0;转载于:https://www.cnblogs.com/yaser/p/4379572.html

windows2016服务器优化,Windows Server 2012 服务器优化图文方法

这篇文章主要介绍了Windows Server 2012 服务器优化图文方法,需要的朋友可以参考下1、显示桌面图片按下WinR键输入&#xff1a;rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,02、关闭IE增强的安全配置1.开启【服务器管理员】单击"服务器管理员"2.在左边窗格切…

StringBuffer与StringBuilder

有些时候&#xff0c;需要由较短的字符串构建字符串。比如&#xff0c;按键或来自文件中的单词。采用字符串连接的方式达到此目的效率比较低。每次连接字符串的时候&#xff0c;都会构建一个新的String对象&#xff0c;既耗时&#xff0c;又浪费空间。使用StringBuilder类就可以…

ExtJs控件属性配置详细(转)

序言&#xff1a; 1.本文摘自网络&#xff0c;看控件命名像是4.0以前的版本&#xff0c;但控件属性配置仍然可以借鉴&#xff08;不足之处&#xff0c;以后项目用到时再续完善&#xff09;。 Ext.form.TimeField: 配置项&#xff1a; maxValue&#xff1a;列表中允许…

网站服务器中病毒该如何处理,网站被中了木马无法删除怎么办? 解决网站中病毒的办法...

紧急预警“XXCMS整站系统(XXCMS)”官方安装包被植入后门这是我们 前几天在站长网上公布的紧急预警! 但是还是有人中招了&#xff0c;服务器中了马&#xff0c;导致网站被挂了黑链&#xff0c;我们外星人源码安全小姐紧急响应&#xff0c;马上为其清除木马后门&#xff0c;查杀病…

ThinkPHP 的URL重写时遇到No input file specified的解决方法

因为在Fastcgi模式下&#xff0c;php不支持rewrite的目标网址的PATH_INFO的解析 ThinkPHP运行在URL_MODEL2时&#xff0c;会出现 No input file specified.的情况&#xff0c; 这时可以修改网站目录的.htaccess文件&#xff1a; RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] …

sql语句中----删除表数据drop、truncate和delete的用法

说到删除表数据的关键字,大家记得最多的可能就是delete了 然而我们做数据库开发,读取数据库数据.对另外的两兄弟用得就比较少了 现在来介绍另外两个兄弟,都是删除表数据的,其实也是很容易理解的 老大------drop 出没场合:drop table tb --tb表示数据表的名字,下同 绝招:删除内…

wowbl最优势的服务器,CWOW中BL为什么远远多于LM一个新手之见

眨眨眼,在WOW的日子也有半年多了,虽然称不上什么高手,但偶尔也有几次"高手"般的表现,不多说废话了,切如正题.CWOW中BL人数上是绝对性优势的压倒LM,为什么会有这样的现象呢?大家仔细观察就会知道,每当建立新人物时,第一默认任务就是亡灵,而对于大多数新手来说他们也不…

linux 文件夹的颜色代表什么意思

linux 文件夹的颜色代表什么意思 绿色 蓝色 黑色代表什么意思 蓝色表示目录&#xff1b; 绿色表示可执行文件&#xff1b; 红色表示压缩文件&#xff1b; 浅蓝色表示链接文件&#xff1b; 灰色表示其它文件&#xff1b; 红色闪烁表示链接的文件有问题了&#xff1b; 黄色是设备…

设计模式之禅之六大设计原则-依赖倒置原则

依赖倒置原则依赖倒置原则的原始定义是&#xff1a;● 高层模块不应该依赖低层模块,两者都应该依赖其抽象;● 抽象不应该依赖细节;● 细节应该依赖抽象。那什么是抽象?什么又是细节呢?---->在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类…