手写Tomcat

1,分析

1,首先要有一个类似web.xml的文件来注册Servlet

2,要有一个BaseServlet

3,request和response实际上就是输入输出流

4,容器启动需要加载配置文件,保存路径和servlet的映射信息

5,利用死循环,socket接收请求,处理请求

2,BIO版本

1)BaseServlet

public abstract class BaseServlet {



    public  void service(MyRequest request, MyResponse response)throws Exception{
        if ("GET".equalsIgnoreCase(request.getMethod()))
            doGet(request,response);
        else
            doPost(request,response);
    }

    protected abstract void doPost(MyRequest request, MyResponse response)throws Exception;

    protected abstract void doGet(MyRequest request, MyResponse response)throws Exception;
}

2)MyRequest

public class MyRequest {

    private String method;

    private String url;

    public MyRequest(InputStream in) {
        try {
            String context = "";
            byte[] buffer = new byte[1024];
            int len = 0;

            if ((len = in.read(buffer)) > 0) {
                context=new String(buffer,0,len);
            }
            String line =context.split("\\n")[0];
            String[]arr=line.split("\\s");
            this.method=arr[0];
            this.url=arr[1].split("\\?")[0];

            System.out.println(context);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getUrl() {
        return url;
    }

    public String getMethod() {

        return method;
    }
}

3)MyResponse

public class MyResponse {

    OutputStream outputStream;

    public MyResponse(OutputStream out) {
        this.outputStream = out;
    }

    public void write(String msg) throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append("HTTP/1.1 200 OK\n")
                .append("Content-Type: text/html;\n")
                .append("\r\n")
                .append(msg);
        outputStream.write(sb.toString().getBytes());
    }
}

4)web.properties

servlet.one.url=/firstServlet.do
servlet.one.className=com.yhd.servlet.FirstServlet

5)FirstServlet

public class FirstServlet extends BaseServlet {


    @Override
    public void doGet(MyRequest request, MyResponse response)throws Exception{
        this.doPost(request,response);
    }


    @Override
    public void doPost(MyRequest request, MyResponse response)throws Exception {
        response.write("This is the First Servlet");
    }
}

6)YhdTomcat

public class YhdTomcat {

    private int port = 8080;

    private ServerSocket server;

    private Map<String, BaseServlet> servletMap = new ConcurrentHashMap<>();

    private Properties webxml = new Properties();

    private void init() {
        try {
            String WEB_INT = this.getClass().getResource("/").getPath();
            FileInputStream fis = new FileInputStream(WEB_INT + "web.properties");
            webxml.load(fis);
            for (Object key : webxml.keySet()) {
                String k = key.toString();
                if (k.endsWith(".url")) {
                    String servletName = k.replaceAll("\\.url$", "");
                    String url = webxml.getProperty(k);
                    String className = webxml.getProperty(servletName + ".className");

                    BaseServlet obj = (BaseServlet) Class.forName(className).newInstance();
                    servletMap.put(url, obj);

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void process(Socket client) throws Exception {
        InputStream inputStream = client.getInputStream();
        OutputStream outputStream = client.getOutputStream();
        MyRequest request = new MyRequest(inputStream);
        MyResponse response = new MyResponse(outputStream);
        String url = request.getUrl();
        if (servletMap.containsKey(url))
            servletMap.get(url).service(request, response);
        else
            response.write("404 Not Found");
        outputStream.flush();
        outputStream.close();
        inputStream.close();


    }

    public void start() {
        init();
        try {
            server = new ServerSocket(this.port);
            System.out.println("tomcat is start , listening port is " + port);
            for (; ; ) {
                Socket client = server.accept();

                process(client);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new YhdTomcat().start();
    }
}

3,Netty版

1)MyRequest

public class MyRequest {

    private ChannelHandlerContext ctx;

    private HttpRequest req;

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

    public String getUrl() {
        return req.getUri();
    }

    public String getMethod() {
        return req.getMethod().name();
    }

    public Map<String, List<String>> getParameters() {
        QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
        return decoder.parameters();
    }

    public String getParameter(String name) {
        Map<String, List<String>> params = getParameters();
        List<String> param = params.get(name);
        if (null == param)
            return null;
        else
            return param.get(0);

    }

}

2)MyResponse

public class MyResponse {

    private ChannelHandlerContext ctx;

    private HttpRequest req;

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

    public void write(String out) throws Exception {
        try {
            if (out == null || out.length() == 0)
                return;
            // 设置 http协议及请求头信息
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(out.getBytes("UTF-8")));
            response.headers().set("Content-Type", "text/html;");
            ctx.write(response);
        } finally {
            ctx.flush();
            ctx.close();
        }
    }
}

3)MyTomcat

public class MyTomcat {

    private int port = 8080;

    private ServerSocket server;

    private Map<String, BaseServlet> servletMap = new ConcurrentHashMap<>();

    private Properties webxml = new Properties();

    private void init() {
        try {
            String WEB_INT = this.getClass().getResource("/").getPath();
            FileInputStream fis = new FileInputStream(WEB_INT + "web.properties");
            webxml.load(fis);
            for (Object key : webxml.keySet()) {
                String k = key.toString();
                if (k.endsWith(".url")) {
                    String servletName = k.replaceAll("\\.url$", "");
                    String url = webxml.getProperty(k);
                    String className = webxml.getProperty(servletName + ".className");

                    BaseServlet obj = (BaseServlet) Class.forName(className).newInstance();
                    servletMap.put(url, obj);

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start() {
        init();
        worker();
    }

    private void worker() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // Worker线程
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap server = new ServerBootstrap();
            // 链路式编程
            server.group(bossGroup, workerGroup)
                    // 主线程处理类,看到这样的写法,底层就是用反射
                    .channel(NioServerSocketChannel.class)
                    // 子线程处理类 , Handler
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        // 客户端初始化处理
                        @Override
                        protected void initChannel(SocketChannel client) throws Exception {
                            // 无锁化串行编程
                            //Netty对HTTP协议的封装,顺序有要求
                            // HttpResponseEncoder 编码器
                            client.pipeline().addLast(new HttpResponseEncoder());
                            // HttpRequestDecoder 解码器
                            client.pipeline().addLast(new HttpRequestDecoder());
                            // 业务逻辑处理
                            client.pipeline().addLast(new TomcatHandler());
                        }

                    })
                    // 针对主线程的配置 分配线程最大数量 128
                    .option(ChannelOption.SO_BACKLOG, 128)
                    // 针对子线程的配置 保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 启动服务器
            ChannelFuture f = server.bind(port).sync();
            System.out.println("Tomcat is start ,listening port is :" + port);
            f.channel().closeFuture().sync();
        }catch (Exception e){

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new MyTomcat().start();
    }


    private class TomcatHandler extends ChannelInboundHandlerAdapter{
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof HttpRequest){
                HttpRequest req = (HttpRequest) msg;

                // 转交给我们自己的request实现
                MyRequest request = new MyRequest(ctx,req);
                // 转交给我们自己的response实现
                MyResponse response = new MyResponse(ctx,req);
                // 实际业务处理
                String url = request.getUrl();

                if(servletMap.containsKey(url)){
                    servletMap.get(url).service(request, response);
                }else{
                    response.write("404 - Not Found");
                }

            }
        }
    }
}

码云地址

已标记关键词 清除标记
相关推荐
<p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;">我们很多时候都想知道Web容器Tomcat是如何工作的?它是如何处理我们传入http请求的?又是如何响应的?</p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;"> </p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;">Tomcat作为Web服务器深受市场欢迎,有必要对其进行深入的研究。在工作中,我们经常会把写好的代码打包放在Tomcat里并启动,然后在浏览器里就能愉快的调用我们写的代码来实现相应的功能了,那么Tomcat是如何工作的?</p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;"> </p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;">通过手写Tomcat服务器,让学员了解服务器的底层代码,方便学员后期的深入学习,而不是停留在表面API的调用层面。</p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;"> </p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;"><strong style="margin: 0px; padding: 0px;">课程讲义截图:</strong></p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;"> </p> <p style="font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; margin: 0px; padding: 0px; color: #313d54; font-size: 16px; background-color: #ffffff;"><strong style="margin: 0px; padding: 0px;"><img src="https://img-bss.csdnimg.cn/202104160434449629.gif" alt="" /></strong></p>
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页