基于netty手寫Tomcat

netty 簡介

Netty一個基于NIO的客戶、服務器端的編程框架

1.環(huán)境準備

maven依賴

? <dependency>

? ? ? ? ? ? <groupId>io.netty</groupId>

? ? ? ? ? ? <artifactId>netty-all</artifactId>

? ? ? ? ? ? <version>4.1.42.Final</version>

? </dependency>

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 方法決定調(diào)用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

? ? //狀態(tài)碼為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.基于傳統(tǒng)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<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();

? 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 已啟動,監(jiān)聽端口是:" + this.port);

? ? ? //3.等待用戶請求,用一個死循環(huán)

? ? ? 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.從協(xié)議內(nèi)容中獲取url 映射相應的servlet

? ? ? String url = request.getUrl();

? ? ? if (servletMapping.containsKey(url)) {

? ? ? ? //7.調(diào)用實例化對象的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<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);

? ? }

? }

}

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協(xié)議及請求頭信息

? ? ? FullHttpResponse response = new DefaultFullHttpResponse(

? ? ? ? // 設置http版本為1.1

? ? ? ? HttpVersion.HTTP_1_1,

? ? ? ? // 設置響應狀態(tài)碼

? ? ? ? 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<String, ParentServlet> servletMapping = new HashMap<String, ParentServlet>();

? 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 {

? ? ? //創(chuàng)建對象

? ? ? server = new ServerBootstrap();

? ? ? //配置參數(shù)

? ? ? //鏈式編程

? ? ? server.group(bossGroup, workGroup)

? ? ? ? //主線程處理類,

? ? ? ? .channel(NioServerSocketChannel.class)

? ? ? ? //子線程處理類

? ? ? ? .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());

? ? ? ? ? ? //業(yè)務處理器

? ? ? ? ? ? client.pipeline().addLast(new TomcatHandler());

? ? ? ? ? }

? ? ? ? })

? ? ? ? //主線程 線程最大數(shù)量128

? ? ? ? .option(ChannelOption.SO_BACKLOG, 128)

? ? ? ? //子線程配置 保存長連接

? ? ? ? .childOption(ChannelOption.SO_KEEPALIVE, true);

? ? ? ChannelFuture f = server.bind(port).sync();

? ? ? System.out.println("Tomcat 已啟動,監(jiān)聽端口是:" + 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.調(diào)用實例化對象的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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容