手写一个tomcat

2020-03-28  本文已影响0人  兮兮码字的地方

tomcat是十分常用的轻量级的web服务容器,其原理其实不复杂,本质就是在jdk的socket,输入输出流,反射,线程池等技术基础上封装出的一套方便开发与客户端交互的服务器。但更常用于http协议的web项目开发。

下面我们实现一个自己的简易的tomcat服务。

1,服务总类

程序的入口,负责创建一个ServerSocket 服务端,监听客户端请求并创建Socket 客户端响应,为提升并发能力,创建连接池来分发任务。

package com.lly;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class MyWebServer {

    public static void startServer(int port) throws Exception{

        @SuppressWarnings("resource")

ServerSocket serverSocket = new ServerSocket(port);

        System.out.println("创建服务成功");

        Socket socket = null;

        int count =0;

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(32);

while (true) {

socket = serverSocket.accept();

System.out.println("收到客户端请求次数:"+(++count));

newFixedThreadPool.execute(new MyRunnable(socket));

}

    }

    public static void main(String[] args) {

        try {

        System.out.println("start WebServer");

            startServer(8080);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

2,处理每个服务的Runnable 线程类

接收服务总类分配过来的socket对象,并获取到输入输出流来创建request和response对象,通过request得到请求的相关信息,分配给相应的servlet来处理业务请求。

package com.lly;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

/**

* Handles requests received by the socket server

*/

public class MyRunnable implements Runnable {

Socket socket = null;

public MyRunnable() {

super();

}

public MyRunnable(Socket socket) {

super();

this.socket = socket;

}

@Override

public void run() {

String name = Thread.currentThread().getName();

System.out.println("当前线程:" + name);

InputStream inputStream = null;

OutputStream outputStream = null;

try {

inputStream = socket.getInputStream();

outputStream = socket.getOutputStream();

MyRequest request;

request = new MyRequest(inputStream);

MyResponse response = new MyResponse(outputStream);

String clazz = new MyMapping().getMapping().get(request.getRequestUrl());

if (clazz != null) {

Class<MyAbstractHttpServlet> myServletClass =  (Class<MyAbstractHttpServlet>)Class.forName(clazz);

MyAbstractHttpServlet myServlet;

myServlet = myServletClass.newInstance();

myServlet.service(request, response);

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

3,Request 类

负责根据输入流解析请求数据。

package com.lly;

import java.io.IOException;

import java.io.InputStream;

public class MyRequest {

    private String requestMethod="";

    private String requestUrl="";

    public MyRequest(InputStream inputStream) throws Exception{

        byte[] buffer = new byte[1024];

        String[] params = extracted(inputStream, buffer);

        if(params!=null && params.length>=2) {

        this.requestMethod = params[0];

        this.requestUrl = params[1];

        }

    }

private String[] extracted(InputStream inputStream, byte[] buffer) throws IOException {

String[] params = null;

try {

int len;

String str = "";

if((len = inputStream.read(buffer,0,1024))>0){

str += new String(buffer,0,len);

}

String data =  str.split("\n")[0];

params = data.split(" ");

} catch (Exception e) {

System.out.println(e.toString());

}

return params;

}

    public String getRequestMethod() {

        return requestMethod;

    }

    public String getRequestUrl() {

        return requestUrl;

    }

}

4,Response类

负责按照servlet服务类给的返回数据来给客户端响应数据。

package com.lly;

import java.io.OutputStream;

public class MyResponse {

private OutputStream outputStream;

public MyResponse(OutputStream outputStream) {

this.outputStream = outputStream;

}

public void write(String str) throws Exception {

StringBuilder builder = new StringBuilder();

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

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

append("\r\n").

append("<html>").

append("<body>").

append("<h1>" + str + "</h1>").

append("</body>").

append("</html>");

this.outputStream.write(builder.toString().getBytes());

this.outputStream.flush();

this.outputStream.close();

System.out.println("响应success...");

}

}

5,AbstractHttpServlet 

定义一套Servlet的规范,包含doGet和doPost两个抽象方法和一个service方法。

package com.lly;

public abstract class MyAbstractHttpServlet {

    public static final String METHOD_GET = "GET";

    public static final String METHOD_POST = "POST";

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

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

    /**

    * @param request

    * @param response

    */

    public void service(MyRequest request,MyResponse response) throws Exception{

        if(METHOD_GET.equals(request.getRequestMethod())){

            doGet(request,response);

        }else if(METHOD_POST.equals(request.getRequestMethod())){

            doPost(request,response);

        }

    }

}

6,Servlet 类

接收request,response对象,并调用相应的业务类处理后用response响应客户端。

package com.lly;

public class MyServlet extends MyAbstractHttpServlet {

@Override

public void doGet(MyRequest request, MyResponse response) throws Exception {

        response.write("<a href=\"https://user.qzone.qq.com/172496791?source=friendlist\">click me</a>");

}

@Override

public void doPost(MyRequest request, MyResponse response) throws Exception {

    doGet(request, response);

}

}

7,Mapping 类

负责映射服务端请求地址和servlet类的关系

package com.lly;

import java.util.HashMap;

public class MyMapping {

public static HashMap<String,String> mapping = new HashMap<String,String>();

    static {

        mapping.put("/myTomcat","com.lly.MyServlet");

    }

    public HashMap<String,String> getMapping(){

        return mapping;

    }

}

简单总结一下,tomcat负责创建socket服务端监听客户端的请求,为每个请求分配一个线程,并发的解析请求数据并传递给相应的servlet类处理。

servlet中可调用复杂的业务逻辑并通过response响应数据给客户端。

上一篇 下一篇

猜你喜欢

热点阅读