基于BIO、Netty手写简易Tomcat


源码地址:https://gitee.com/zy4350/simpleTomcat.git

Tomcat简介

  • 什么是tomcat
  • tomcat工作原理

基于BIO实现SimpleTomCat

定义接口规范

  • 定义Servlet抽象类

     
    /**
     * @author zheng yong
     * @date 2022/10/13、13:25
     */
    public abstract class Servlet {
    
        public void service(Request request, Response response) throws Exception {
            if ("GET".equalsIgnoreCase(request.getMethod())) {
                doGet(request, response);
            } else {
                doPost(request, response);
            }
        }
    
        public abstract void doGet(Request request, Response response) throws IOException;
    
        public abstract void doPost(Request request, Response response) throws IOException;
    }
    
    
  • 定义Request、Response接口

    /**
     * @author zheng yong
     * @date 2022/10/13、13:26
     */
    public interface Request {
    
        String getUrl();
    
        String getMethod();
    }
    
    
    
    public interface Response {
    
        void write(String resposenMeg) throws IOException;
    
    }
    

Tomcat 代码实现

  • BIOTomcat实现

    public class BIOTomcat {
    
    
        private int port = 8080;
    
        private ServerSocket server;
    
        private Properties webxml = new Properties();
    
        // servlet 容器
        private Map<String, Servlet> servletContainer = new ConcurrentHashMap<>();
    
    
        // tomcat 启动入口
        public void start() throws Exception {
    
            // 1、 加载web.properties(web.xml)文件,解析配置
            init();
    
    
            // 2、启动服务端socket,等待用户请求
            server = new ServerSocket(this.port);
            System.out.println("simple bio tomcat is started. this port:" + this.port);
    
            while (true) {
    
                Socket client = server.accept();
    
                // 3、获得请求信息,解析http协议的内容
                process(client);
            }
    
        }
    
        // 1、初始化配置信息
        private void init() throws Exception {
            // 加载配置文件
            String WEB_INF = this.getClass().getResource("/").getPath();
            FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
    
            //
            webxml.load(fis);
    
            for (Object k : webxml.keySet()) {
                String key = k.toString();
    
                if (key.endsWith(".url")) {
                    String sevlectName = key.replaceAll("\\.url$", "");
                    String url = webxml.getProperty(key);
                    String className = webxml.getProperty(sevlectName + ".className");
    
                    Servlet servlet = (Servlet) Class.forName(className).newInstance();
                    servletContainer.put(url, servlet);
                }
            }
    
        }
    
    
        private void process(Socket client) throws Exception {
            InputStream is = client.getInputStream();
            OutputStream os = client.getOutputStream();
            Request request = new BIORequest(is);
            Response response = new BIOResponse(os);
    
            String url = request.getUrl();
    
            if (servletContainer.containsKey(url)) {
                servletContainer.get(url).service(request, response);
            } else {
                response.write("404 - Not Found.");
            }
    
            os.flush();
            os.close();
    
            is.close();
            client.close();
        }
    
    }
    
    
  • BIO实现Request

    package org.yongz.tomcat.bio.http;
    
    import org.yongz.tomcat.Interface.Request;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * @author zheng yong
     * @date 2022/10/13、13:24
     */
    public class BIORequest implements Request {
        private String url;
        private String method;
    
        public BIORequest(InputStream inputStream) throws IOException {
            // 打印http协议的具体内容
            String httpContent = "";
            byte[] buff = new byte[1024];
            int len = 0;
            if ((len = inputStream.read(buff)) > 0) {
                httpContent += new String(buff, 0, len);
            }
            System.out.println(httpContent);
    
            // 解析出url\请求类型
            String httpHeadLine = httpContent.split("\\n")[0];
    
            String[] arr = httpHeadLine.split("\\s");
    
            this.url = arr[1].split("\\?")[0];
            this.method = arr[0];
        }
    
        @Override
        public String getUrl() {
            return this.url;
        }
    
        @Override
        public String getMethod() {
            return this.method;
        }
    }
    
    
    
  • BIO实现Response

    package org.yongz.tomcat.bio.http;
    
    import org.yongz.tomcat.Interface.Response;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.nio.charset.StandardCharsets;
    
    /**
     * @author zheng yong
     * @date 2022/10/13、13:25
     */
    public class BIOResponse implements Response {
    
        private OutputStream os;
    
        public BIOResponse(OutputStream os) {
            this.os = os;
        }
    
        @Override
        public void write(String resposenMeg) throws IOException {
            StringBuffer sb = new StringBuffer();
            sb.append("HTTP/1.1 200 ok\n")
                    .append("Content-Type:text/html;\n")
                    .append("\r\n")
                    .append(resposenMeg);
            os.write(sb.toString().getBytes(StandardCharsets.UTF_8));
        }
    }
    
    
  • 编写Servlet以及配置文件web.properties(同tomcat的web.xml)

    
    /**
     * @author zheng yong
     * @date 2022/10/13、13:30
     */
    public class OneServlet extends Servlet {
    
    
        @Override
        public void doGet(Request request, Response response) throws IOException {
            doPost(request, response);
        }
    
        @Override
        public void doPost(Request request, Response response) throws IOException {
            response.write("this is oneSevlet for BIO.");
        }
    }
    
    

    web.properties

    servlet.one.className=org.yongz.tomcat.bio.servlet.OneServlet
    servlet.one.url=/oneServlet.do
    
    
  • 测试
    在这里插入图片描述

基于Netty(NIO)实现SimpleTomCat

接口规范BIO接口规范定义

  • 环境准备
         <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.6.Final</version>
        </dependency>
  • NettyTomcat实现

    package org.yongz.tomcat.nio;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.http.HttpRequest;
    import io.netty.handler.codec.http.HttpRequestDecoder;
    import io.netty.handler.codec.http.HttpResponseEncoder;
    import org.yongz.tomcat.Interface.Servlet;
    import org.yongz.tomcat.nio.http.NIORequest;
    import org.yongz.tomcat.nio.http.NIOResponse;
    
    import java.io.FileInputStream;
    import java.net.ServerSocket;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @author zheng yong
     * @date 2022/10/13、13:33
     */
    public class NIOTomcat {
    
    
        private int port = 8080;
    
        private ServerSocket server;
    
        private Properties webxml = new Properties();
    
        // servlet 容器
        private Map<String, Servlet> servletContainer = new ConcurrentHashMap<>();
    
        // boss线程
        private NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // worker线程
        private NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    
        // tomcat 启动入口
        public void start() {
    
            try {
                // 1、 加载web.properties(web.xml)文件,解析配置
                init();
    
    
                // 2、创建netty服务器对象
                ServerBootstrap server = new ServerBootstrap();
    
    
                // boss线程
                bossGroup = new NioEventLoopGroup();
                // worker线程
                workerGroup = new NioEventLoopGroup();
                // 3、 配置服务端参数
                server.group(bossGroup, workerGroup)
                        // 配置主线程的处理逻辑
                        .channel(NioServerSocketChannel.class)
                        // 子线程的回调处理,Handler
                        .childHandler(new ChannelInitializer() {
                            @Override
                            protected void initChannel(Channel client) throws Exception {
                                // 处理回调的逻辑
                                // 责任链模式
                                // 处理响应结果的封装
                                client.pipeline().addLast(new HttpResponseEncoder());
                                // 用户请求过来,要解码
                                client.pipeline().addLast(new HttpRequestDecoder());
                                // 用户自己的业务逻辑
                                client.pipeline().addLast(new NIOTomcatHandler());
                            }
                        })
                        // 配置主线程分配的最大线程数
                        .option(ChannelOption.SO_BACKLOG, 128)
                        // 保持长连接
                        .childOption(ChannelOption.SO_KEEPALIVE, true);
    
                // 启动服务,绑定端口
                ChannelFuture future = server.bind(this.port).sync();
                System.out.println("simple nio tomcat is started. this port:" + this.port);
    
    
                future.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    
        // 1、初始化配置信息
        private void init() throws Exception {
            // 加载配置文件
            String WEB_INF = this.getClass().getResource("/").getPath();
            FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
    
            //
            webxml.load(fis);
    
            for (Object k : webxml.keySet()) {
                String key = k.toString();
    
                if (key.endsWith(".url")) {
                    String sevlectName = key.replaceAll("\\.url$", "");
                    String url = webxml.getProperty(key);
                    String className = webxml.getProperty(sevlectName + ".className");
    
                    Servlet servlet = (Servlet) Class.forName(className).newInstance();
                    servletContainer.put(url, servlet);
                }
            }
    
        }
    
        /**
         * @author zheng yong
         * @date 2022/10/13、15:51
         */
        public class NIOTomcatHandler extends ChannelInboundHandlerAdapter {
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
                if (msg instanceof HttpRequest) {
                    HttpRequest req = (HttpRequest) msg;
    
                    NIORequest request = new NIORequest(ctx, req);
                    NIOResponse response = new NIOResponse(ctx, req);
                    String url = request.getUrl();
                    if (servletContainer.containsKey(url)) {
                        Servlet servlet = servletContainer.get(url);
                        servlet.service(request, response);
                    } else {
                        response.write("404 - Not found.");
                    }
                }
    
            }
        }
    
    }
    
    
  • NIO实现Request

    package org.yongz.tomcat.nio.http;
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.HttpRequest;
    import org.yongz.tomcat.Interface.Request;
    
    /**
     * @author zheng yong
     * @date 2022/10/13、13:24
     */
    public class NIORequest implements Request {
        private ChannelHandlerContext ctx;
        private HttpRequest req;
    
        public NIORequest(ChannelHandlerContext ctx, HttpRequest req) {
    
            this.ctx = ctx;
            this.req = req;
        }
    
    
        @Override
        public String getUrl() {
            return this.req.getUri();
        }
    
        @Override
        public String getMethod() {
            return this.req.getMethod().name();
        }
    }
    
    
    
  • NIO实现Response

package org.yongz.tomcat.nio.http;

import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledDirectByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.util.internal.StringUtil;
import org.yongz.tomcat.Interface.Response;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author zheng yong
 * @date 2022/10/13、13:25
 */
public class NIOResponse implements Response {
    private ChannelHandlerContext ctx;
    private HttpRequest req;

    public NIOResponse(ChannelHandlerContext ctx, HttpRequest req) {
        this.ctx = ctx;
        this.req = req;
    }


    @Override
    public void write(String resposenMeg) throws IOException {

        try {
            if (StringUtil.isNullOrEmpty(resposenMeg)) {
                return;
            }

            DefaultFullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.OK,
                    Unpooled.wrappedBuffer(resposenMeg.getBytes(StandardCharsets.UTF_8))
            );

            response.headers().set("Content-Type","text/html");

            ctx.write(response);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ctx.flush();
            ctx.close();
        }

    }
}

  • 编写Servlet以及配置文件web.properties(同tomcat的web.xml)

    package org.yongz.tomcat.nio.servlet;
    
    import org.yongz.tomcat.Interface.Request;
    import org.yongz.tomcat.Interface.Response;
    import org.yongz.tomcat.Interface.Servlet;
    
    import java.io.IOException;
    
    /**
     * @author zheng yong
     * @date 2022/10/13、13:30
     */
    public class OneNIOServlet extends Servlet {
    
    
        @Override
        public void doGet(Request request, Response response) throws IOException {
            doPost(request, response);
        }
    
        @Override
        public void doPost(Request request, Response response) throws IOException {
            response.write("this is OneNIOServlet for NIO.");
        }
    }
    
    

    web.properties

    servlet.two.nio.className=org.yongz.tomcat.nio.servlet.OneNIOServlet
    servlet.two.nio.url=/oneNIOServlet.do
    
  • 测试
    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值