阅读How Tomcat Works笔记

    科技2024-11-11  11

    一 概述

    1 Servlet容器

    Servlet容器为一个Servlet请求提供服务基本要做三件事

    创建request对象填充可能被servlet使用的信息,如参数、头部、cookies。一个request对象是javax.servlet.ServletRequest或javax.servlet.http.ServletRequest接口的实例创建一个response对象给客户端发送响应,一个response对象是javax.servlet.ServletResponse或javax.servlet.http.ServletResponse接口的实例调用servlet的service方法,传入request和response对象,servlet会从request中取值,给response写值

    2 Catalina

    2.1 Connector

    连接器连接容器中的请求,为接收到的每一个HTTP请求构造一个request和response对象,然后把流程传递给容器

    2.2 Container

    容器接收到request和response对象调用servlet的service方法进行响应。容器还要做相当多的事情,比如加载servlet、验证用户、更新会话等

    3 Tomcat4和Tomcat5

    Tomcat4

    每一个组件有自己的后台处理线程使用Servlet2.3和JSP1.2规范

    Tomcat5

    使用Servlet2.4和JSP2.0规范更有效率的默认连接器所有组件共享一个后台处理线程,占有更少的资源简化了代码,不需要一个映射组件(mapper component)查找子组件

    二 一个简单的Web服务器

    1 超文本传输协议

    HTTP协议是一种请求和响应协议,使用可靠的TCP连接,TCP连接默认80端口。允许Web服务器和浏览器通过互联网发送和接受数据。再HTTP中,始终是客户端通过建立连接和发送请求来开启一个事务

    1.1 HTTP请求

    HTTP请求包括四部分

    请求方式 统一资源标识符URI 协议/版本号 统一资源标识符相对于服务器的根目录解释,始终用/开头请求头 请求头包括客户端环境和请求主体的有用信息请求空行请求主体(GET请求没有、POST请求有)

    1.2 HTTP响应

    请求方式 统一资源标识符URI 协议/版本号响应的头响应空行响应主体

    2 Socket

    Socket是网络连接的端点,使一个应用可以从网络中读取和写入数据,使两台不同计算机上的不同应用通过连接发送和接收字节流。向一个应用发送信息需要知道应用的IP地址和套接字端口。Socket代表一个客户端套接字。

    2.1 使用Socket访问Tomcat主页

    public class TestSocket { public static void main(String[] args) { try { //创建套接字 Socket socket = new Socket("127.0.0.1", 8080); //OutPutStream发送字节流 OutputStream out = socket.getOutputStream(); //PrintWriter发送文本到远程应用 PrintWriter writer = new PrintWriter(out, true); //访问Tomcat主页 writer.println("GET /index.jsp HTTP/1.1"); writer.println("host:localhost:8080"); writer.println("Connection:close"); writer.println(); //缓冲字符输入流,获得请求结果 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); StringBuffer stringBuffer = new StringBuffer(); boolean loop = true; while (loop){ if (bufferedReader.ready()){ int i = 0; while (i != -1){ //读取单个字符,读到流的末尾返回-1 i = bufferedReader.read(); stringBuffer.append((char)i); } loop = false; } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(stringBuffer.toString()); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }

    启动本地Tomcat,运行程序在浏览器访问localhost:8080

    3 ServerSocket

    ServerSocket代表一个服务端套接字。服务端套接字等待来自客户端的连接请求,当获得一个请求时,创建一个Socket对象与客户端进行通信。构建ServerSocket的三个参数分别是port(监听端口),backlog(可接受的连接的队列的最大长度),bindAddr(服务器将绑定的本地的InetAddress,通常使用InetAddress.geBytName(host)获得)。

    3.1 使用ServerSocket获得请求的资源

    这是我的项目工程目录

    项目资源

    HttpServer

    public class HttpServer { public static String WEB_ROOT = null; public static final String SHUTDOWN = "/shutdown"; private boolean shutdown = false; public static void main(String[] args) { //获取当前类所在的模块编译之后的根路径 File file1 = new File(HttpServer.class.getResource("/").getPath()); String path = file1.getAbsolutePath(); String[] split = path.split("\\\\"); //System.getProperty("user.dir") 获得项目根路径 //split[5] 获得模块名 WEB_ROOT = System.getProperty("user.dir") + File.separator + split[5] + File.separator + "web"; HttpServer httpServer = new HttpServer(); System.out.println("启动服务器..."); try { //启动服务器 httpServer.await(WEB_ROOT); } catch (IOException e) { e.printStackTrace(); } } private void await(String webRoot) throws IOException { 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 (!shutdown){ //收到请求时才往下执行,否则阻塞 Socket socket = serverSocket.accept();; new Thread(new Runnable() { @Override public void run() { OutputStream output = null; InputStream input = null; try { System.out.println("收到请求..."); input = socket.getInputStream(); output = socket.getOutputStream(); Request request = new Request(input); request.parse(); Response response = new Response(output); response.setRequest(request); response.getResponseOfTheRequest(); socket.close(); shutdown = request.getUri().equals(SHUTDOWN); } catch (IOException e) { e.printStackTrace(); } } }).start(); } System.out.println("关闭服务器"); serverSocket.close(); } }

    Request

    public class Request { //客服端的请求输入流,包含客户端的请求信息 private InputStream input; private String uri; public Request(InputStream is) { this.input = is; } /** * 解析客服端发送的请求 */ public void parse(){ StringBuffer request= new StringBuffer(2048); byte[] bytes = new byte[2048]; int i; try { i = input.read(bytes); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j = 0; j < i; j++) { request.append((char)bytes[j]); } System.out.println(request.toString()); uri = parseUri(request.toString()); } /** * 获得请求uri */ public String parseUri(String request) { int index1,index2; index1 = request.indexOf(" "); if (index1 != -1){ index2 = request.indexOf(" ", index1 + 1); if (index2 > index1){ return request.substring(index1+1,index2); } } return null; } /** * 调用过parse方法uri才有值 */ public String getUri(){ return uri; } }

    Response

    public class Response { public static String WEB_ROOT = null; private static final int BUFFER_SIZE = 1024; OutputStream outputStream; Request request; public Response(OutputStream outputStream){ this.outputStream = outputStream; } public void setRequest(Request request){ this.request = request; } /** * 向浏览器写出请求的响应 */ public void getResponseOfTheRequest(){ File file1 = new File(HttpServer.class.getResource("/").getPath()); String path = file1.getAbsolutePath(); String[] split = path.split("\\\\"); WEB_ROOT = System.getProperty("user.dir") + File.separator + split[5] + File.separator + "web"; FileInputStream fis = null; byte[] bytes = new byte[BUFFER_SIZE]; int i; try { File file = new File(WEB_ROOT, request.getUri()); System.out.println("请求uri:"+request.getUri()); if (file.exists()){ fis = new FileInputStream(file); int ch = fis.read(bytes,0,BUFFER_SIZE); System.out.println("请求成功..."); System.out.println("请求响应结果..."); String msg = "HTTP/1.1 200 OK\r\n" + "Content-Type:text/html\r\n" + "\r\n"; outputStream.write(msg.getBytes()); while (ch != -1){ outputStream.write(bytes,0,ch); ch = fis.read(bytes,0,BUFFER_SIZE); } }else { String errorMsg = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type:text/html\r\n" + "Content-Length:23\r\n" + "\r\n" + "<h1>File Not Found</h1>"; System.out.println("请求失败..."); System.out.println("请求响应结果..."); outputStream.write(errorMsg.getBytes()); } }catch (Exception e){ System.out.println(e.getMessage()); }finally { if (fis!=null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }

    启动项目HttpServer

    Processed: 0.027, SQL: 8