0
点赞
收藏
分享

微信扫一扫

How Tomcat Works读书笔记三-------连接器


几个概念

HttpServlet,Servlet

Servlet是一个接口,定义了一种网络服务,我们所有的servlet都要实现它(或它的子类)

HttpServlet是一个抽象类,它针对的就是http网络服务

当然如果以后再有其他的网络服务,可以再定义一个类,让它实现Servlet即可。

HttpServletRequest,ServletRequest


先说ServletRequest接口,它用来传递网络服务的请求,用在Servlet类的service方法。


那么HttpServletRequest接口,主要针对的就是http请求。


HttpServletRequest比ServletRequest多了一些针对于Http协议的方法。如getHeader,getMethod,getSession等。



StringManager

对于一个大型的系统,任何时候都要小心的处理错误信息。如果将所有的错误信息都保存在一个properties文件中,会无比繁杂,因此tomcat为系统里面的每一个包都建立一组properties文件(是一组 而不是一个 是为了支持多语言 LocalStrings.properties,LocalStrings_es.properties,LocalStrings_ja.properties就分别是英语 西班牙语 日语)



例如在ex03.pyrmont.connector.http包下的HttpProcessor类中 我们引用StringManager 如下


protected StringManager sm =

StringManager.getManager("ex03.pyrmont.connector.http");


 在要抛出异常的地方使用


throw new ServletException

(sm.getString("httpProcessor.parseHeaders.colon"));


现在我们看看httpProcessor.parseHeaders.colon包下的properties文件(部分)


httpProcessor.parseHeaders.colon=Invalid HTTP header format

httpProcessor.starting=Starting background thread

httpProcessor.stopping=Stopping background thread

requestStream.close.closed=Request stream has already been closed


这下知道StringManager的功能了吧。


另外,StringManager的产生是单例模式,大家可以看看源码


lozy load

在本章,http请求对象使用httprequst来表示(jdk中并不存在这个类 我们自己写的),它实现了HttpServletRequest接口。


在使用的时候httprequst会被转型成HttpServletRequest,并作为servlet的service方法的参数而调用。


所以我们得设置每个httprequest实例的成员变量供servlet实例使用,需要设定的值包括uri,查询字符串,参数,Cookie等等


因而,这里就存在一个问题,解析http的请求体(注意是请求体 请求方法get/post那一行和请求头会全部解析)涉及很多字符串操作,如果我们只解析会用到的值就能节省很多cpu周期。


因此只有当我们调用javax.servlet.http.HttpServletRequest的getParameter(),getParameterMap(),getParameter-Names(),getParameterValues()时才会解析请求体(并且即使多次调用那个四个方法 也只解析一次)!


具体做法就是在HttpRequest类(我们自己写的那个实现了HttpServletRequest接口的那个)里上述四个方法里面第一行代码就调用一个parseParameters()方法。parsed的作用就是保证parseParameters只运行一次


(另外我觉得请求字符串应该就是请求体的一部分)



第二章帮我们建立了一个简单的servlet容器,它可以加载静态资源,还可以处理一些简单的servlet。在这一章里,我们要进一步完善我们的容器,让它能取得request请求的一些参数。


因此我们这一节的servlet就会继承HttpServlet,传递的参数也会是HttpServletRequest。



应用代码

我们这一章的servlet代码如下

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;


public class ModernServlet extends HttpServlet {


public void init(ServletConfig config) {
System.out.println("ModernServlet -- init");
}


public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Modern Servlet</title>");
out.println("</head>");
out.println("<body>");


out.println("<h2>Headers</h2");
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
out.println("<br>" + header + " : " + request.getHeader(header));
}


//省略部分request信息
out.println("</body>");
out.println("</html>");


}
}



首先看看UML图


How Tomcat Works读书笔记三-------连接器_http协议

系统提炼出来了一个Bootstrap类,加载HttpConnector类,调用其start()方法

package ex03.pyrmont.connector.http;


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();
}
}



HttpConnector的run方法其实与书的第一章的HttpServer1类似,只是第一章中获得inputstream后,就让他生成request了,而在这里,我们创建了httpprocessor,并调用其process方法将serverSocket.accept()的socket传入。


HttpProcessor的process方法如下 



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();
}
}


看这个一行代码   

input = new SocketInputStream(socket.getInputStream(), 2048);
SocketInputStream是一个实现了InputStream接口的包装类,我们之所以用SocketInputStream,就是为了用它的readRequestLine()方法readHeader方法。前者能读出request请求的请求方法,uri和请求协议,后者能读出请求头。

构建HttpRequest

    parseRequest(input, output);
    parseHeaders(input);
这两个方法是process方法的核心,也可以说是整个第三章的核心。往简单的说,这两个方法干的事就是解析http请求填充HttpRequst对象,当然这里面有很多很多细节的东西。
这部分说简单很简单,说麻烦也巨麻烦,大体来说就是五个部分
1读取套接字的输入流
 就是下面这行代码
  input = new SocketInputStream(socket.getInputStream(), 2048);
 大家有兴趣可以看看readRequestLine的源代码,不过不必深究。
2解析请求行(就是 http请求的第一行 包括请求方法 uri 协议版本)
3解析请求头
4解析Cookie
5获取参数(就是前文提到的lazy load)

构建HttpResponse



在第二章的ServletResponse中,我们只实现了部分功能,print方法不能自动刷新。

public PrintWriter getWriter() throws IOException {
// autoflush is true, println() will flush,
// but print() will not.
writer = new PrintWriter(output, true);
return writer;
}


通过上面第二章的代码,我们知道writer是一个PrintWriter类的实例。


1 在本章,我们使用ResponseWriter来构建writer实例(ResponseWriter继承自PrintWriter)


2 第二章的output是OutputStream接口的实例,在第三章,我们换成ResponseStream(ResponseStream 继承 ServletOutputStream,而后者又实现了OutputStream接口)


3 同时还需要一个OutputStreamWriter作为ResponseWriter与ResponseStream之间的桥梁。


所以代码如下


public PrintWriter getWriter() throws IOException {
ResponseStream newStream = new ResponseStream(this); // this 指代HttpResopnse
newStream.setCommit(false);
OutputStreamWriter osr =
new OutputStreamWriter(newStream, getCharacterEncoding());
writer = new ResponseWriter(osr);
return writer;
}

静态资源处理器与servlet处理器与上一章的内容基本一致,不再赘述。

参考资料


​​http://zhidao.baidu.com/link?url=BGdS8iiz12FmAlVLsoGj6n3LwclvQ7rD-8GOJ6U8uguM8IGZrDUooTkjnySAInrMkqA6a7XofgNu4J3ynbOPC_​​

举报

相关推荐

0 条评论