简单的Tomcat源码实现

简单的Tomcat源码实现

源码下载链接](https://download.csdn.net/download/yang134679/11092413)

我们先来看一段代码

package com.ys.tomcat.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class MyTomcatPre {
    public static void main(String[] args) throws Exception{
        //定义服务端的端口号
        ServerSocket serverSocket = new ServerSocket(9999);
        //TomCat启动
        System.out.println("TomCat已经启动");
        //永无休止的接收服务
        while(true){
            //建立一个socket请求,一旦有客户端连上了进行接收
            Socket socket = serverSocket.accept();
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            //缓冲
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
            //将接收到的信息打印到控制台
            String msg = null;
            while ((msg = bufferedReader.readLine()) != null){
                if (msg.length() == 0){
                    break;
                }
                System.out.println(msg);
            }
        }
    }
}

这是我们设置的一个ServerSocket 服务,端口号9999,当浏览器向其发出请求,会在控制台打印出对应的http请求。
我们启动测试类
在这里插入图片描述
在浏览器发起请求
在这里插入图片描述
得到以下结果
在这里插入图片描述
有了基本服务器知识后,进行简易源码实现
项目主体结构

在这里插入图片描述

当我们tomcat启动时收到请求后,需要对请求进行处理,之前我们使用servlet的doget和dopost方法进行请求,这里我们进行实现的还原
主类(MyTomcat)源码

package com.ys.tomcat;

import com.ys.tomcat.servlet.HttpServlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;

public class MyTomcat {
    //使用HashMap存储,初始化servlet信息
    public static final HashMap<String,HttpServlet> servletMapping = new HashMap<>();
    public static void main(String[] args) throws  Exception{
    //第一步,初始化servlet
        //资源路径
        String basePath;
        //获取basePath
        basePath = MyTomcat.class.getResource("/").getPath();
        //1.获取解析器
        SAXReader reader = new SAXReader();
        //2。获取document文档对象
        Document document = reader.read(new File(basePath + "web.xml"));
        //3.获取根元素
        Element root = document.getRootElement();
        //4.获取根元素下的子元素
        List<Element> childElements = root.elements();
        //5.遍历子元素
        for (Element element: childElements) {
            //6.判断元素名称为servlet的元素
            if("servlet".equals(element.getName())){
                //7.获取servlet—name元素
                Element servletName = element.element("servlet-name");
                //8.获取servlet-class元素
                Element servletClass = element.element("servlet-class");
                //检测输出
                System.out.println(servletName.getText()+"\t"+servletClass.getText());
                //9.将web.xml中的servlet-name和servlet-class值进行存储
                servletMapping.put(servletName.getText(),
                        (HttpServlet)Class.forName(servletClass.getText()).newInstance());
            }
        }
    //第二阶段--暴露接口,处理请求
        //定义服务端的端口号
        ServerSocket serverSocket = new ServerSocket(8080);
        //TomCat启动
        System.out.println("TomCat已经启动");
        //永无休止的接收服务
        while(true){
            //建立一个socket请求,一旦有客户端连上了进行接收
            Socket socket = serverSocket.accept();
            //接收一个开启一个线程
            Thread thread = new ServletThread(socket);
            //开启线程
            thread.start();
        }
    }
}

首先我们利用dom4j这个jar包对web.xml文件进行解析,保证在main方法中首先启动servlet服务
web.xml源码

<?xml version="1.0" encoding="utf-8"?>

<web-app xmlns="http://www.example.org/web-app_2_5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
                version="2.5">
    <servlet>
        <servlet-name>mytomcat</servlet-name>
        <servlet-class>com.ys.tomcat.servlet.MyServlet</servlet-class>
    </servlet>
</web-app>

但是我们首先要封装一个Request类,这个类我们用来获取请求头信息中的请求方法和请求的url信息

package com.ys.tomcat.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
 * http请求封装类
 */
public class Request {
    //请求类型  get/post
    private String method;
    //请求资源路径 url
    private String url;

    public Request(InputStream inputStream) throws IOException{
        //缓冲,转码
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
        //创建一个String数组保存请求头信息,先读取一行信息,然后按照“ ”切割
        String[] input = reader.readLine().split(" ");
        //数组第一个是请求方法
        this.method = input[0];
        //数组的第二位是url
        this.url = input[1];


    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

同时我们也需要对Response响应做数据封装
Response源码

package com.ys.tomcat.http;

import java.io.OutputStream;

/**
 * 请求封装类
 */
public class Response {
    //response是输出响应,用输出流
    public OutputStream outputStream;
    //状态行
    //Http版本,状态码,状态信息
    //响应报文头部
    //用于说明服务器要使用的附加资源
    //空行
    //请求头和主体数据间必须有换行
    public static String responseHeader = "Http/1.1 200 \r\n"
                                        +"Content-Type:text/html \r\n"
                                        +"\r\n";

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

读取到了servlet配置之后,我们还原servlet
SuperServlet源码

package com.ys.tomcat.servlet;

import com.ys.tomcat.http.Request;
import com.ys.tomcat.http.Response;

import java.io.IOException;

public interface SuperServlet {
	//初始化方法
	void init();
	//service方法
	void service(Request request, Response response) throws IOException;
	//销毁方法
	void destroy();
}

HttpServlet源码(仿照原jdk中HttpServlet),service中判断调用的方法,再调用具体实现类中

package com.ys.tomcat.servlet;

import com.ys.tomcat.http.Request;
import com.ys.tomcat.http.Response;

import java.io.IOException;



public abstract class HttpServlet implements SuperServlet{
	//初始化方法
	@Override
	public void init() {
		
	}
	//销毁方法
	@Override
	public void destroy() {
		
	}
	//核心业务方法
	@Override
	public void service(Request request, Response response) throws IOException {
		//判断是否哪种请求方法get/post
		if("get".equalsIgnoreCase(request.getMethod())){
			doGet(request,response);
		}else{
			doPost(request,response);
		}
	}
	//等待实例类调用
	public abstract void doGet(Request request, Response response);
	
	public abstract void doPost(Request request, Response response);
}

MyServlet源码,在这里具体处理调用方法

package com.ys.tomcat.servlet;
/**
 * 自定义servlet
 */

import com.ys.tomcat.http.Request;
import com.ys.tomcat.http.Response;

import java.io.OutputStream;

public class MyServlet extends HttpServlet{
    @Override
    public void doPost(Request request, Response response) {
        try{
            System.out.println("请求的method(类型)"+request.getMethod());
            System.out.println("请求的url"+request.getUrl());

            //返回response请求头+请求信息
            String res = Response.responseHeader + "connection succession...";
            //
            OutputStream outputStream = response.outputStream;
            //
            outputStream.write(res.getBytes());
            //冲刷
            outputStream.flush();
            //关闭流对象
            outputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public void doGet(Request request, Response response) {
        doPost(request,response);
    }
}

处理好servlet之后,我们第二部就要等待访问连接了,主类中一旦有请求,就会创建一个ServletThread线程,进行服务
在这里插入图片描述

ServletThread源码

package com.ys.tomcat;

import com.ys.tomcat.http.Request;
import com.ys.tomcat.http.Response;
import com.ys.tomcat.servlet.HttpServlet;

import java.io.OutputStream;
import java.net.Socket;


/**
 *自定义的线程执行tomcat操作(对servlet进行处理)
 *
 */
public class ServletThread extends Thread{
	//创建socket对象接受数据
	protected Socket socket;

	//初始化传值
	public ServletThread(Socket socket){
		this.socket = socket;
	}
	
	@Override
	public void run() {
		try{
			System.out.println("执行的线程"+Thread.currentThread());
			//将输入流对象传递给request对象
			Request request = new Request(socket.getInputStream());
			//拿到响应封装
			Response response = new Response(socket.getOutputStream());
			//service来处理(通过请求路径来实例化servlet—servletMapping)
			HttpServlet httpServlet = MyTomcat.servletMapping.get(request.getUrl().replace("/",""));

			//判断取出的HttpServlet对象是否为空
			if(httpServlet != null){//存在对应的servlet
				//调用servlet执行方法
				httpServlet.service(request,response);
			}else{//如果找不到对应的servlet信息
				//返回response请求头+请求信息
				String res = Response.responseHeader + "connection default...";
				//
				OutputStream outputStream = socket.getOutputStream();
				//
				outputStream.write(res.getBytes("utf-8"));
				//冲刷
				outputStream.flush();
				//关闭流对象
				outputStream.close();
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			//关闭socket连接
			if(socket != null){
				try{
					socket.close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
	}
}

至此一个简单的tomcat实现原理就完成了,需要源码请访问添加链接描述

我们启动项目,进行访问,连接成功
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/yang134679/article/details/89055006