手写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");
                }

            }
        }
    }
}

码云地址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值