自动手写tomcat的实现


声明:该文章为转载的一个泡友的原创,泡友...泡友...泡友........你在那里呀 ??

1. tomcat 的基本原理

tomcat按以下的步骤对外提供服务

  1. 定义web.xml文件
  2. 读取web.xml文件,将配置信息读取到某个地方
  3. 定义ServerSocket,持续监听服务
  4. 服务到来后处理请求
  5. 请求处理完成后返回结果

2. 手写Demo

2.1 代码结构

2.2 定义web.xml 文件

为简单起见,使用web.properties定义,代码如下:

servlet.one.url=/login
servlet.one.classname=com.northleaf.servlet.LoginServlet

2.3 定义Request ,用于封装传递进来的请求

此类中同时封装了其它的几个字段:method,url

public class Request {
    private String method;
    private String url;

  // 此处省略get方法
    public Request(InputStream inputStream) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        //将读取到的第一行显按空格拆分出方法与url
        String[] methodAndUrl = bufferedReader.readLine().split(" ");
        this.method= methodAndUrl[0];
        this.url=methodAndUrl[1];
    }
}

2.4 定义Response,用于封装返回给客户端的请求

在此代码中,定义一个http协议头,有些浏览器,比如chrome,无法识别不带协议头的的报文

public class Response {


    public OutputStream outputStream;

    public static final String responseHeader="HTTP/1.1 200 \r\n"
            + "Content-Type: text/html\r\n"
            + "\r\n";

    public Response(OutputStream outputStream) throws IOException {
        this.outputStream= outputStream;
    }

}

2.5 定义Servlet 父类

此类中模仿httpservlet类,添加service,doget、dopost方法

public abstract class Servlet {

    public void service(Request request, Response response) {

        //判断是调用doget 还是 dopost
        if ("get".equalsIgnoreCase(request.getMethod())) {
            this.doGet(request, response);
        } else {
            this.doPost(request, response);
        }

    }

    public abstract void doGet(Request request, Response response);

    public abstract void doPost(Request request, Response response);

}

2.6 定义一个具体的Servlet类,继承其父类

public class LoginServlet extends Servlet {
    @Override
    public void doGet(Request request, Response response) {
        this.doPost(request,response);
    }

    @Override
    public void doPost(Request request, Response response)  {
        try {
            OutputStream outputStream = response.outputStream;
            String res = Response.responseHeader+"Hello,welcome to here !";
            outputStream.write(res.getBytes());
            outputStream.flush();
            outputStream.close();
        }catch (Exception ex){
            ex.printStackTrace();
        }

    }
}

2.7 定义SocketProcess类

此类为一个线程,用于处理接收到的客户端Socket

public class SocketProcess extends Thread{

    protected Socket socket;

    public SocketProcess(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            Request request = new Request(socket.getInputStream());
            Response response = new Response(socket.getOutputStream());

            Servlet servlet = MyTomcat.servletMapping.get(request.getUrl());

            if (servlet != null) {
               servlet.service(request,response);
            }else{
                String res = Response.responseHeader+"Hello World";
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(res.getBytes());
                outputStream.flush();
                outputStream.close();
            }

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注:如果在web.properties文件中没有找到客户端请求的url,则直接返回“Hello World”

2.8 定义tomcat类

此类为核心类,分几步走:

第一步: 定义变量,用于存储相关的内容

    private static final int port = 8099;
    private static final Properties properties = new Properties();
    public static final HashMap<String, Servlet> servletMapping = new HashMap<>();

第一步: 定义init方法,读取配置文件

此方法重点是将web.properties中的内容读到取ServletMapping中

 private void init() {

        InputStream io = null;

        String basePath;

        try {
            //获取basePath
            basePath = MyTomcat.class.getResource("/").getPath();
            System.out.println(basePath);
            io = new FileInputStream(basePath + "web.properties");
            properties.load(io);
            io.close();

            //初始化ServletMapping
            //返回属性key的集合
            Set<Object> keys = properties.keySet();
            for (Object key : keys) {
                if (key.toString().contains("url")) {
                    System.out.println(key.toString() + "=" + properties.get(key));
                    //根据key值获取className
                    Object classname = properties.get(key.toString().replace("url", "classname"));

                    servletMapping.put(properties.get(key.toString()).toString(),
                            (Servlet) Class.forName(classname.toString()).newInstance());
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (io != null) {
                try {
                    io.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

第三步:启动服务,并监听

 private void start() {
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("Tomcat 服务已启动,地址:localhost ,端口:" + port);
            //持续监听
            do {
                Socket socket = serverSocket.accept();
                System.out.println(socket);
                //处理任务
                Thread thread = new SocketProcess(socket);
                thread.start();
            } while (true);

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

注: 此文档为咕泡公开课的笔记整理

猜你喜欢

转载自blog.csdn.net/zty1317313805/article/details/80840189