前言
这一篇是howTomcatWork的书籍笔记内容。下面是根据书中的内容做的一部分个人笔记。
书籍地址:
链接:pan.baidu.com/s/1jazo55sA…提取码:lp96 --来自百度网盘超级会员V6的分享
个人评价
没啥好说的,tomcat作者写的书,看了下海淘居然要500多确实吓到了。虽然代码使用的是tomcat5的版本,但是可以基本理解tomcat的内部工作机制。也可以看到作者是如何用一个十多行的肉鸡服务器代码不断升级成为现在的tomcat的模样。
本文是个人根据看书记录的一些笔记,中间逻辑不一定连贯,因为有些内容过于基础没有记录的价值,所以挑了一些个人关注的点。
一个最简单的Servlet是如何工作的
- 创建Request 对象,并且解析HTTP的请求信息,通过Request对象封装了这些信息的具体细节
具体接口:
javax.servlet.ServletRequest
或javax.servlet.http.ServletRequest
- 创建Response对象,封装了客户需需要的真正数据,封装了响应体的相关信息
javax.servlet.ServletResponse
或javax.servlet.http.ServletResponse
- servlet的
service
方法,根据此方法对于请求头进行解析,同时创建response将数据回传给客户端
tomcat的基本结构
Tomcat把服务器在大体上可以拆分为两部分,一部分叫做容器,另一部分叫做连接器
连接器
作用:接收到每一个 HTTP 请求构造一个 ==request== 和 ==response== 对象
容器
作用:接受连接器的请求根据service
方法进行响应给对应的客户端
Tomcat 4 和 和 5的主要区别
- Tomcat 5 支持 Servlet 2.4 和 JSP 2.0 规范,而 Tomcat 4 支持 Servlet 2.3 和 JSP 1.2。
- 比起 Tomcat 4,Tomcat 5 有一些更有效率的默认连接器。
- Tomcat 5 共享一个后台处理线程,而 Tomcat 4 的组件都有属于自己的后台处理线程。 因此,就这一点而言,Tomcat 5 消耗较少的资源。
- Tomcat 5 并不需要一个映射组件(mapper component)用于查找子组件,因此简化了代码。
构建一个最简单的web程序
构建对象
下方的代码简单阅读即可,无需自己动手实验
HttpServer
用于构建一个服务器,同时建立serverSocket套接字等待链接
- 调用
httprequest.parse()
方法
代码如下:
public class HttpServer { /** * 关闭容器的请求路径 */ private static final String SHUTDOWN = "/SHUTDOWN"; /** * 静态资源根路径 */ public static final String WEBROOT = System.getProperty("user.dir") + File.separator + "webroot"; /** * 是否关闭标识 */ private boolean SHUTDOWN_FLAG = false; public static void main(String[] args) { new HttpServer().await(); } /** * 具体的server 方法,等待socket请求 */ public void await() { // 默认为8080端口 int port = 8080; String host = "127.0.0.1"; ServerSocket serverSocket = null; try { // 创建套接字 serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(-1); } while (!SHUTDOWN_FLAG) { try { // 等待套接字 Socket accept = serverSocket.accept(); HttpRequest httpRequest = new HttpRequest(accept.getInputStream()); // 处理请求数据 httpRequest.parse(); // 创建响应对象,处理响应信息 HttpResponse httpResponse = new HttpResponse(accept.getOutputStream()); // 设置静态资源 httpResponse.setRequest(httpRequest); httpResponse.setResource(); // 关闭的套接字 accept.close(); // 判断请求Url是否为 /shutdown SHUTDOWN_FLAG = httpRequest.getUri().equalsIgnoreCase(SHUTDOWN); } catch (IOException e) { e.printStackTrace(); continue; } } } } 复制代码
HttpRequest
以httpserver 的请求inputstream, 解析请求内容,分解请求uri
使用parse()
方法解析请求信息,设置到stringbuffer里面
使用parseUri(str)
截取请求信息的请求uri,设置到属性里面
public class HttpRequest { /** * 缓冲区的大小为 1M */ private static final int BUFFER_COUNT = 1024; /** * 请求路径 */ private String uri; /** * 请求流 */ private InputStream inputStream; public HttpRequest(InputStream inputStream) { this.inputStream = inputStream; } /** * 解析inputstream 对于内容进行解析 */ public void parse() { // 字符串缓冲池 StringBuffer stringBuffer = new StringBuffer(BUFFER_COUNT); byte[] byteBuffer = new byte[BUFFER_COUNT]; if (inputStream == null) { System.err.println("未找到套接字"); return; } int read = 0; try { // 读取数据到byte数组 read = inputStream.read(byteBuffer); } catch (IOException e) { e.printStackTrace(); System.exit(-1); } //读取byte数组的数据进入到stringbuffer for (int i = 0; i < read; i++) { stringBuffer.append((char)byteBuffer[i]); } // 打印stringbuffer System.err.println(stringBuffer.toString()); // 获取uri uri = parseUri(stringBuffer.toString()); } /** * 解析请求,获取请求Uri * @param requestString 需要处理的uri */ public String parseUri(String requestString){ // 建立index1 和 2 int index1, index2; // 获取到第一个空行 index1 = requestString.indexOf(' '); if(index1 != -1){ // 从index1 开始找 index2 = requestString.indexOf(' ', index1 + 1); if(index2 > index1){ // 获取请求路径 return requestString.substring(index1 + 1, index2); } } return null; } public String getUri() { return uri; } } 复制代码
HttpResonse
以 httpserver 的请求outputstream ,获取输入流,将数据返回给客户端
关键方法为setResouces
,获取请求Uri,同时使用file 读取文件
public class HttpResponse { /** * 组合httprequest * 根据request返回对应到信息 */ private HttpRequest request; /** * 输出流 */ private OutputStream outputStream; /** * 缓冲区大小 */ private static final int BUFFER_COUNT = 1024; public HttpResponse(OutputStream outputStream) { this.outputStream = outputStream; } /** * 设置静态资源 */ public void setResource() throws IOException { String errMsg = "404 msg"; // 字节缓存区 byte[] bytes = new byte[BUFFER_COUNT]; // 读取静态资源 File file = new File(HttpServer.WEBROOT, request.getUri()); if (file.exists()) { // 文件流 try { FileInputStream fileInputStream = new FileInputStream(file); // 读取字节 int ch = fileInputStream.read(bytes, 0, BUFFER_COUNT); // 输出 while (ch != -1) { // 写入流 outputStream.write(bytes, 0, ch); // 重复读取数据到缓冲区 ch = fileInputStream.read(bytes, 0, BUFFER_COUNT); } } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { outputStream.close(); } } } else { try { outputStream.write(errMsg.getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { outputStream.close(); } } } } /** * 设置request * * @param httpRequest */ public void setRequest(HttpRequest httpRequest) { this.request = httpRequest; } } 复制代码