《深入剖析Tomcat》阅读(三)
?這里要介紹下Tomcat的一個(gè)重要設(shè)計(jì)方法,Catalina設(shè)計(jì)方式。
Servlet容器是一個(gè)復(fù)雜系統(tǒng),但是,它有三個(gè)基本任務(wù),對(duì)每個(gè)請(qǐng)求,servlet容器會(huì)為其完成以下三個(gè)操作:
1.創(chuàng)建一個(gè)Request對(duì)象,用可能會(huì)在調(diào)用的Servlet中使用到的信息填充該request對(duì)象,如參數(shù)、頭、cookie、查詢字符串、URI等。
? request對(duì)象是javax.servlet.ServletRequest接口或javax.servlet.http.ServletRequest接口的一個(gè)實(shí)例。
2.創(chuàng)建一個(gè)調(diào)用Servlet的response對(duì)象,用來(lái)向Web客戶端發(fā)送響應(yīng)。response對(duì)象是javax.servlet.ServletResponse接口或javax.servlet.http.ServletResponse接口的一個(gè)實(shí)例。
3.調(diào)用Servlet的service()方法,將request對(duì)象和response對(duì)象作為參數(shù)傳入。Servlet從request對(duì)象中讀取信息,并通過(guò)response對(duì)象發(fā)送響應(yīng)信息。
?-----------------------------------------------------------------------------------------------------
Catalina是一個(gè)成熟的軟件,設(shè)計(jì)和開(kāi)發(fā)得十分優(yōu)雅。(其實(shí)我在公司項(xiàng)目中發(fā)現(xiàn)的使用方法也是這樣的,十分的優(yōu)雅的軟件開(kāi)發(fā)方式)
Catalina使得軟件開(kāi)發(fā)功能結(jié)構(gòu)編程模塊化的,基于上文提到的servlet容器的任務(wù),可以將Catalina劃分為兩個(gè)模塊:連接器(connector)和容器(container)
連接器負(fù)責(zé)將一個(gè)請(qǐng)求與容器相關(guān)聯(lián)。它的工作包括為它接收到的每一個(gè)HTTP請(qǐng)求創(chuàng)建一個(gè)Request對(duì)象和一個(gè)Response對(duì)象。然后,它將處理過(guò)程交給容器。容器從連接器中接收到Request對(duì)象和Response對(duì)象,并負(fù)責(zé)調(diào)用相應(yīng)的Servlet的service()方法。
?
?
簡(jiǎn)而言這模式就是connector負(fù)責(zé)接收請(qǐng)求,創(chuàng)建解析請(qǐng)求需要的Request對(duì)象和Response對(duì)象,然后將請(qǐng)求分發(fā)到容器中處理。
(普通的服務(wù)端結(jié)構(gòu)一般也是這么設(shè)計(jì)的,IO部分負(fù)責(zé)接收請(qǐng)求,創(chuàng)建解析請(qǐng)求的對(duì)象,然后將請(qǐng)求丟進(jìn)線程池中進(jìn)行處理)
xxxx
--------------------------------------------
?
現(xiàn)在根據(jù)模塊開(kāi)發(fā)Servlet容器:
1)連接器 ? HttpConnector和HttpProcessor 連接器及其支持類 標(biāo)示HTTP請(qǐng)求的類(HTTPRequest)及其支持類 負(fù)責(zé)HTTP響應(yīng)的類,HttpResponse以及其支持類,外觀類以及常量類
2)啟動(dòng)模塊 ?啟動(dòng)模塊包括一個(gè)類,就是startup.Bootstrap 類,負(fù)責(zé)啟動(dòng)應(yīng)用程序。
3)核心模塊
啟動(dòng)類
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();} }?
關(guān)于這個(gè)建議具有連接器和容器功能的Http服務(wù)器
http://www.cnblogs.com/wuxinliulei/p/4967625.html
?
?
?
---------------------------------------------------------------------------
重點(diǎn)介紹這個(gè)模型當(dāng)中對(duì)HTTP請(qǐng)求信息的parse
HttpConnector類在接收到連接請(qǐng)求后,將請(qǐng)求派發(fā)給一個(gè)HttpProcessor對(duì)象處理
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對(duì)象傳入,調(diào)用HttpProcessor對(duì)象的process方法來(lái)處理Http請(qǐng)求
下面是HttpProcessor對(duì)象的process方法,重點(diǎn)關(guān)注SocketInputStream對(duì)象,SocketInputStream對(duì)象是對(duì)InputStream對(duì)象的一層包裝,封裝了一些對(duì)Http請(qǐng)求解析方便的方法。 ??
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方法當(dāng)中調(diào)用了parseRequest方法,該方法傳入SocketInputStream對(duì)象和OutputStream對(duì)象,方法對(duì)輸入流當(dāng)中的http請(qǐng)求進(jìn)行了解析,
這里牽涉到幾個(gè)幫助類?
1:HttpRequestLine類,定義在HttpProcessor方法的成員變量當(dāng)中。private HttpRequestLine requestLine = new HttpRequestLine();?
該類中對(duì)http請(qǐng)求內(nèi)容中的請(qǐng)求行(http請(qǐng)求依次分為請(qǐng)求行、請(qǐng)求頭、/r/n,請(qǐng)求體四部分),第一行內(nèi)容進(jìn)行了解析 即
POST /servlet/primitServlet Http/1.1 ? ?GET ?/sample/hello.jsp ? HTTP/1.1 這樣的內(nèi)容
HttpRequestLine類是一個(gè)可以復(fù)用的類(PS:但是HttpProcessor在這個(gè)框架當(dāng)中沒(méi)有對(duì)象池的復(fù)用,所以沒(méi)有卵用),對(duì)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的該方法完成了對(duì)HttpRequestLine對(duì)象的初始化,主要初始化了六個(gè)對(duì)象
public char[] method; ?//method對(duì)象
public int methodEnd; //method對(duì)象的結(jié)束位置
public char[] uri; ? ? ? ? //uri對(duì)象
public int uriEnd; ? ? ? ?//uri對(duì)象的結(jié)束位置
public char[] protocol; //協(xié)議對(duì)象
public int protocolEnd;//協(xié)議對(duì)象的結(jié)束位置
注意read方法完成了對(duì)count的初始化,并且將第一行內(nèi)容緩存到char[] buf當(dāng)中
/*** 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++;}//請(qǐng)求航URL結(jié)束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 + "'");}}
?----------------------------------------------------------------------------------------------------------------------------------------
為什么采用連接器的第三章的代碼沒(méi)有采用第二章的簡(jiǎn)單形式直接將相關(guān)內(nèi)容解析出來(lái)呢?
?
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()); }?
其實(shí)上面的處理方式是不嚴(yán)謹(jǐn)?shù)?#xff0c;應(yīng)該讀到/r/n之后停止,而不是固定讀取2048字節(jié),然后處理這讀出來(lái)的字節(jié)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/wuxinliulei/p/4960670.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的《深入剖析Tomcat》阅读(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: VHDL简易电子琴的设计
- 下一篇: VHDL移位寄存器的设计与实现