基于netty手写Tomcat

netty 简介

Netty一个基于NIO的客户、服务器端的编程框架

1.环境准备

maven依赖

 

            io.netty

            netty-all

            4.1.42.Final

 

12345

RequestMethodEnum 请求方式

public enum RequestMethodEnum {

  GET("GET"),

  POST("POST");

  public String code;

  RequestMethodEnum(String code) {

    this.code=code;

  }

}

12345678

ParentServlet 父类servlet

public abstract class ParentServlet {

  public void service(ParentRequest request, ParentResponse response) throws Exception {

    //service 方法决定调用doGet、doPost;

    if (RequestMethodEnum.GET.code.equalsIgnoreCase(request.getMethod())) {

      doGet(request, response);

    } else {

      doPost(request, response);

    }

  }

  protected abstract void doPost(ParentRequest request, ParentResponse response) throws Exception;

  protected abstract void doGet(ParentRequest request, ParentResponse response) throws Exception;

}

12345678910111213141516

FirstServlet

public class FirstServlet extends ParentServlet {

  @Override

  protected void doPost(ParentRequest request, ParentResponse response) throws Exception {

    response.write("This is the first");

  }

  @Override

  protected void doGet(ParentRequest request, ParentResponse response) throws Exception {

    this.doPost(request,response);

  }

}

1234567891011

SecondServlet

public class SecondServlet extends ParentServlet {

  @Override

  protected void doPost(ParentRequest request, ParentResponse response) throws Exception {

    response.write("this is the second");

  }

  @Override

  protected void doGet(ParentRequest request, ParentResponse response) throws Exception {

    this.doPost(request,response);

  }

}

1234567891011

ParentRequest

public class ParentRequest {

  private String method;

  private String url;

  public String getUrl() {

    return url;

  }

  public String getMethod() {

    return method;

  }

}

1234567891011121314

ParentResponse

public class ParentResponse {

  private OutputStream out;

  public ParentResponse (OutputStream out) {

    this.out = out;

  }

  public void write(String s) throws Exception{

    //输出也要遵循HTTP

    //状态码为200

    StringBuilder sb = new StringBuilder();

    sb.append("HTTP/1.1 200 OK \n")

      .append("Content-Type: text/html;\n")

      .append("\r\n")

      .append(s);

    out.write(sb.toString().getBytes());

  }

}

1234567891011121314151617

web.properties

servlet.first.url=/first

servlet.first.className=com.aiden.servlet.FirstServlet

servlet.second.url=/second

servlet.second.className=com.aiden.servlet.SecondServlet

1234

2.基于传统I/O手写Tomcat

修改ParentRequest

public class ParentRequest {

  private String method;

  private String url;

  public ParentRequest(InputStream in) {

    try {

      String content = "";

      byte[] buff = new byte[1024];

      int len = 0;

      if ((len = in.read(buff)) > 0) {

        content = new String(buff,0,len);

      }

      String line = content.split("\\n")[0];

      String [] arr = line.split("\\s");

      this.method = arr[0];

      System.out.println(method);

      this.url = arr[1].split("\\?")[0];

    } catch (IOException e) {

      e.printStackTrace();

    }

  }

  public String getUrl() {

    return url;

  }

  public String getMethod() {

    return method;

  }

}

12345678910111213141516171819202122232425262728293031

编写tomcatStart类

public class TomcatStart {

  private int port = 8080;

  private ServerSocket server;

  private Map servletMapping = new HashMap();

  private Properties webProperties = new Properties();

  private void init() {

    try {

      String WEB_INF = this.getClass().getResource("/").getPath();

      FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");

      webProperties.load(fis);

      for (Object k : webProperties.keySet()) {

        String key = k.toString();

        if (key.endsWith(".url")) {

          String servletName = key.replaceAll("\\.url$", "");

          String url = webProperties.getProperty(key);

          String className = webProperties.getProperty(servletName + ".className");

          //单实例  多线程

          ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();

          servletMapping.put(url, obj);

        }

      }

    } catch (Exception e) {

      e.printStackTrace();

    }

  }

  public void start() {

    //1.加载配置类,初始化servletMapping

    init();

    try {

      //2.绑定端口启动

      server = new ServerSocket(this.port);

      System.out.println("Tomcat 已启动,监听端口是:" + this.port);

      //3.等待用户请求,用一个死循环

      while (true) {

        Socket client = server.accept();

        //4.http 请求

        process(client);

      }

    } catch (IOException e) {

      e.printStackTrace();

    }

  }

  private void process(Socket client) throws IOException {

    InputStream is = null;

    OutputStream os = null;

    try {

      is = client.getInputStream();

      os = client.getOutputStream();

      //5.Request(inputstream) Response (outputstream)

      ParentRequest request = new ParentRequest(is);

      ParentResponse response = new ParentResponse(os);

      //6.从协议内容中获取url 映射相应的servlet

      String url = request.getUrl();

      if (servletMapping.containsKey(url)) {

        //7.调用实例化对象的service方法

        servletMapping.get(url).service(request, response);

      } else {

        response.write("404 - Not Found");

      }

    } catch (Exception e) {

      e.printStackTrace();

    } finally {

      if (os != null) {

        os.flush();

        os.close();

      }

      if (is != null) {

        is.close();

      }

      client.close();

    }

  }

  public static void main(String[] args) {

    //启动

    new TomcatStart().start();

  }

}

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182

3.基于netty手写Tomcat

修改ParentRequest

public class ParentRequest {

  private ChannelHandlerContext ctx;

  private HttpRequest req;

  public ParentRequest(ChannelHandlerContext ctx, HttpRequest req) {

    this.ctx = ctx;

    this.req = req;

  }

  public String getUrl() {

    return req.uri();

  }

  public String getMethod() {

    return req.method().name();

  }

  public Map> getParameters() {

    QueryStringDecoder decoder = new QueryStringDecoder(req.uri());

    return decoder.parameters();

  }

  public String getParameter(String name) {

    Map> params = getParameters();

    List param = params.get(name);

    if (null == param) {

      return null;

    } else {

      return param.get(0);

    }

  }

}

123456789101112131415161718192021222324252627282930313233

修改ParentResponse

public class ParentResponse {

  //SocketChannel的封装

  private ChannelHandlerContext ctx;

  private HttpRequest req;

  public ParentResponse(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(

        // 设置http版本为1.1

        HttpVersion.HTTP_1_1,

        // 设置响应状态码

        HttpResponseStatus.OK,

        // 将输出值写出 编码为UTF-8

        Unpooled.wrappedBuffer(out.getBytes("UTF-8")));

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

      ctx.write(response);

    } finally {

      ctx.flush();

      ctx.close();

    }

  }

}

12345678910111213141516171819202122232425262728293031323334

修改TomcatStart

public class TomcatStart {

  private int port = 8080;

  private Map servletMapping = new HashMap();

  private Properties webProperties = new Properties();

  private void init() {

    try {

      String WEB_INF = this.getClass().getResource("/").getPath();

      FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");

      webProperties.load(fis);

      for (Object k : webProperties.keySet()) {

        String key = k.toString();

        if (key.endsWith(".url")) {

          String servletName = key.replaceAll("\\.url$", "");

          String url = webProperties.getProperty(key);

          String className = webProperties.getProperty(servletName + ".className");

          //单实例  多线程

          ParentServlet obj = (ParentServlet) Class.forName(className).newInstance();

          servletMapping.put(url, obj);

        }

      }

    } catch (Exception e) {

      e.printStackTrace();

    }

  }

  public void start() {

    //1.加载配置类,初始化servletMapping

    init();

    // Netty  NIO Reactor模型 Boss Worker

    //Boss 线程

    EventLoopGroup bossGroup = new NioEventLoopGroup();

    //Work线程

    EventLoopGroup workGroup = new NioEventLoopGroup();

    ServerBootstrap server = null;

    try {

      //创建对象

      server = new ServerBootstrap();

      //配置参数

      //链式编程

      server.group(bossGroup, workGroup)

        //主线程处理类,

        .channel(NioServerSocketChannel.class)

        //子线程处理类

        .childHandler(new ChannelInitializer() {

          @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 已启动,监听端口是:" + this.port);

      f.channel().closeFuture().sync();

    } catch (Exception e) {

      e.printStackTrace();

    } finally {

      bossGroup.shutdownGracefully();

      workGroup.shutdownGracefully();

    }

  }

  public class TomcatHandler extends ChannelInboundHandlerAdapter {

    @Override

    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

      if (msg instanceof HttpRequest) {

        System.out.println("hello request");

        HttpRequest req = (HttpRequest) msg;

        ParentRequest request = new ParentRequest(ctx, req);

        ParentResponse response = new ParentResponse(ctx, req);

        String url = request.getUrl();

        if (servletMapping.containsKey(url)) {

          //7.调用实例化对象的service方法

          servletMapping.get(url).service(request, response);

        } else {

          response.write("404 - Not Found");

        }

      }

    }

  }

  public static void main(String[] args) {

    //启动

    new TomcatStart().start();

  }

}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798

4.访问

http://localhost:8080/first

你可能感兴趣的:(基于netty手写Tomcat)