手写Tomcat源码简易篇

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 手写Tomcat源码简易篇

Http协议

HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)

HTTP协议的主要特点:


1、支持客户/服务器模式。


2、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。


3、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。


4、无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。


5、无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。


HTTPS协议的主要特点:


1、内容加密:采用混合加密技术,中间者无法直接查看明文内容


2、验证身份:通过证书认证客户端访问的是自己的服务器


3、保护数据完整性:防止传输的内容被中间人冒充或者篡改


4、SSL证书需要购买申请,功能越强大的证书费用越高


5、SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗


6、HTTPS连接缓存不如HTTP高效,流量成本高


7、HTTPS协议握手阶段比较费时,对网站的响应速度有影响,影响用户体验。


Http协议的组成部分


这里在手写tomcat源码的时候用到对于http协议的解析,所以将http协议的组成如图所示,在源码中对于http协议的解析也是按照如图格式来解析的。

fd125ce9483d4796915374a995efe5b3.jpg

模拟源码

Tomcat组件

Server:

服务器,用来接收其他计算机(客户端)发来的请求数据并进行解析,完成相关业务处理,然后将处理结果返回给计算机。

Service:

Web服务,表示动作状态。

Servlet:

应用程序、服务程序。

Connector:

连接器,用于接收请求并最终将请求返回给客户端。

Container:

容器,包含下面要讲到的Engine、Context、Host、Wrapper。

Engine:

引擎,负责请求 的处理。

Context:

上下文容器,也可以看成Web应用。

Host:

虚拟主机。

Wrapper:

封装器,代表一个Servlet。

目录结构


022d634fa8784a1aa6d9d40c5993d5d9.png


tomcat类


直接执行tomcat类中的main方法,然后在浏览器中访问任意本地8080端口就可以实现页面的输出。这里仅仅是在浏览器页面上打印hello这个英文单词。进行简易的代码模拟实现。

public class tomcat {
    public void start(){
        try {
            ExecutorService executorService = Executors.newFixedThreadPool(8);
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true) {
                Socket accept = serverSocket.accept();
                executorService.execute(new SocketProcessor(accept));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        tomcat tomcat=new tomcat();
        tomcat.start();
    }
}

Request类,多余的实现类在这里删除了,源代码运行要实现。

public class Request implements HttpServletRequest {
    private String method;
    private String url;
    private String protocol;
    private Socket socket;
    public Socket getSocket() {
        return socket;
    }
    public Request(String method, String url, String protocol, Socket socket) {
        this.method = method;
        this.url = url;
        this.protocol = protocol;
        this.socket=socket;
    }
    public String getMethod() {
        return method;
    }
    public StringBuffer getRequestURL() {
       return new StringBuffer(url);
    }
    public String getProtocol() {
        return protocol;
    }
}

Response类

public class Response implements HttpServletResponse {
    private int status=200;
    private String message="ok";
    private Map<String,String> headers=new HashMap<>();
    private Request request;
    private OutputStream socketOutputStream;
    private byte SP=' ';
    private byte CR='\r';
    private byte LF='\n';
    private ResponseServletOutputStream responseServletOutputStream=new ResponseServletOutputStream();
    public Response(Request request) {
        this.request = request;
        try {
            this.socketOutputStream=request.getSocket().getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void complete(){
        try {
            sendResponseLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            sendResponseHeader();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            sendResponseBody();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void sendResponseBody() throws IOException {
       socketOutputStream.write(getOutputStream().getBytes());
    }
    private void sendResponseHeader() throws IOException {
        for (Map.Entry<String, String> stringStringEntry : headers.entrySet()) {
            String key=stringStringEntry.getKey();
            String value=stringStringEntry.getValue();
            socketOutputStream.write(key.getBytes());
            socketOutputStream.write(":".getBytes());
            socketOutputStream.write(value.getBytes());
            socketOutputStream.write(CR);
            socketOutputStream.write(LF);
        }
        socketOutputStream.write(CR);
        socketOutputStream.write(LF);
    }
    private void sendResponseLine() throws IOException {
        socketOutputStream.write(request.getProtocol().getBytes());
        socketOutputStream.write(SP);
        socketOutputStream.write(status);
        socketOutputStream.write(SP);
        socketOutputStream.write(message.getBytes());
        socketOutputStream.write(CR);
        socketOutputStream.write(LF);
    }
    @Override
    public void addHeader(String s, String s1) {
        headers.put(s,s1);
    }
    @Override
    public void setStatus(int i, String s) {
        status=i;
        message=s;
    }
    @Override
    public int getStatus() {
        return status;
    }
    @Override
    public ResponseServletOutputStream getOutputStream() throws IOException {
       return responseServletOutputStream;
    }
}

MyServlet类

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(req.getMethod());
        resp.addHeader("Content-Length","5");
        resp.addHeader("Content-Type","text/plain;charset=utf-8");
        resp.getOutputStream().write("hello".getBytes());
    }
}

SocketProcess类

public class SocketProcessor implements Runnable{
    private Socket socket;
    public SocketProcessor(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        processSocket(socket);
    }
    private void processSocket(Socket socket) {
        try {
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            inputStream.read(bytes);
            int pos=0;
            int begin=0,end=0;
            for(;pos<bytes.length;pos++,end++){
                if(bytes[pos]==' ')
                    break;
            }
            StringBuilder method=new StringBuilder();
            for(;begin<end;begin++){
                method.append((char)bytes[begin]);
            }
            pos++;
            begin++;
            end++;
            for(;pos<bytes.length;pos++,end++){
                if(bytes[pos]==' ')
                    break;
            }
            StringBuilder url=new StringBuilder();
            for(;begin<end;begin++){
               url.append((char)bytes[begin]);
            }
            pos++;
            begin++;
            end++;
            for(;pos<bytes.length;pos++,end++){
                if(bytes[pos]=='\r')
                    break;
            }
            StringBuilder protocl=new StringBuilder();
            for(;begin<end;begin++){
                protocl.append((char)bytes[begin]);
            }
            Request request=new Request(method.toString(),url.toString(),protocl.toString(),socket);
            Response response=new Response(request);
            MyServlet myServlet=new MyServlet();
            myServlet.service(request,response);
            response.complete();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
}

ResponseServletOutputStream类

public class ResponseServletOutputStream extends ServletOutputStream {
   private byte[] bytes=new byte[1024];
   private int pos=0;
    public byte[] getBytes() {
        return bytes;
    }
    public int getPos() {
        return pos;
    }
    @Override
    public void write(int b) throws IOException {
    }
    @Override
    public void write(byte[] b) throws IOException {
        for (byte b1 : b) {
            bytes[pos]=b1;
            pos++;
        }
    }
    @Override
    public boolean isReady() {
        return false;
    }
    @Override
    public void setWriteListener(WriteListener writeListener) {
    }
}
相关文章
|
6月前
|
安全 Java 应用服务中间件
阿里技术官架构使用总结:Spring+MyBatis源码+Tomcat架构解析等
分享Java技术文以及学习经验也有一段时间了,实际上作为程序员,我们都清楚学习的重要性,毕竟时代在发展,互联网之下,稍有一些落后可能就会被淘汰掉,因此我们需要不断去审视自己,通过学习来让自己得到相应的提升。
|
3月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
6月前
|
设计模式 算法 前端开发
Tomcat的源码剖析, 启动过程你会吗?
基于JMX Tomcat会为每个组件进行注册过程,通过Registry管理起来,而Registry是基于JMX来实现的,因此在看组件的init和start过程实际上就是初始化MBean和触发MBean的start方法,会大量看到形如: Registry.getRegistry(null, null).invoke(mbeans, "init", false); Registry.getRegistry(null, null).invoke(mbeans, "start", false); 这样的代码,这实际上就是通过JMX管理各种组件的行为和生命期。
36 0
|
Java 应用服务中间件 Maven
最通俗易懂的 - Tomcat 核心源码仿写 第二版代码
Tomcat 核心源码仿写的第二版代码,升级为Maven项目,并将单线程改为多线程
72 0
|
Java 应用服务中间件 数据格式
Tomcat 核心源码解析 及 仿写
介绍Tomcat的核心功能原理,并仿写Tomcat核心功能源码 包括: 1、如何生成Servlet实例对象并管理 2、如何与浏览器建立连接并获取http报文,解析报文获取请求,并响应 最后有效果演示和源码gitee地址
119 0
|
Java 应用服务中间件 Maven
Tomcat - 源码构建Tomcat 8.5.55 启动
Tomcat - 源码构建Tomcat 8.5.55 启动
114 0
|
Java 应用服务中间件 容器
Tomcat8源码解析(二)
2.Tomcat启动阶段 daemon.start(),tomcat的启动阶段分析 getServer().start(),方法启动Server,源码分析 services[i].start(),启动
84 0
|
前端开发 应用服务中间件 容器
Tomcat8源码解析(一)
Tomcat8源码解析 Tomcat总体架构 Tomcat源码搭建 tomcat软件和源码文件下载链接:https://tomcat.apache.org/download-80.cgi 创建一个to
135 0
|
Java 应用服务中间件 容器
springboot如何启动内置tomcat?(源码详解)
springboot如何启动内置tomcat?(源码详解)
springboot如何启动内置tomcat?(源码详解)
|
XML Java 应用服务中间件
Tomcat 安装使用&源码构建
Tomcat 安装使用&源码构建
120 0