【javaweb】自己动手实现简易的Tomcat

单线程版本:

转载自:https://blog.csdn.net/weixin_39033443/article/details/83901722

分以下几个步骤:
(1)提供Socket服务
(2)把请求和响应封装成request/response
(3)进行请求的转发

代码实现如下:
1、工程截图:

å¨è¿éæå¥å¾çæè¿°

2、封装请求对象:通过输入流,对HTTP协议进行解析,拿到了HTTP请求头的方法和URL:


/**
 * @author wangjie
 * @version 2018/11/9
 * 封装请求对象
 * 通过输入流,对http协议进行解析,拿到http请求头的方法和url
 */
public class MyRequest {
    private String url; // 请求url
    private String method; // 请求方法

    public MyRequest(InputStream inputStream) throws IOException{
        String httpRequest ="";
        byte[] httpRequestBytes =new byte[1024];
        int length =0;
        if((length=inputStream.read(httpRequestBytes)) >0){
            httpRequest=new String(httpRequestBytes,0,length);
        }

        String httpHead = httpRequest.split("\n")[0]; // 切割换行符“\n”
        url=httpHead.split("\\s")[1]; // 切割空格“\\s”
        method=httpHead.split("\\s")[0];
        System.out.println(this);
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
}

小结:

String httpHead = httpRequest.split("\n")[0]; // 切割换行符“\n”
String url=httpHead.split("\\s")[1]; // 切割空格“\\s”

3、封装响应对象:基于HTTP协议的格式进行输出写入。

/**
 * @author wangjie
 * @version 2018/11/9
 * 封装响应对象
 * 基于HTTP协议的格式进行输出写入。
 */
public class MyResponse {
    private OutputStream outputStream;

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

    public void write(String content)throws IOException {
        StringBuffer httpResponse = new StringBuffer();
        httpResponse.append("HTTP/1.1 200 OK\n")
                .append("Content-Type: text/html\n")
                .append("\r\n")
                .append("<html><body>")
                .append(content)
                .append("</body></html>");
        outputStream.write(httpResponse.toString().getBytes());
        outputStream.close();
    }
}

小结:

Response对象的write()方法,底层调用的是OutputStream的write()方法来往客户端进行写出响应

4、servlet请求处理基类:Tomcat是满足Servlet规范的容器,所以Tomcat需要提供API:doGet/doPost/service。

/**
 * @author wangjie
 * @version 2018/11/9
 * Servlet请求处理基类
 */
public abstract class MyServlet {
    public abstract void doGet(MyRequest myRequest,MyResponse myResponse);
    public abstract void doPost(MyRequest myRequest,MyResponse myResponse);
    public void service(MyRequest myRequest,MyResponse myResponse){ // 判断请求方法类型,进行请求转发
        if(myRequest.getMethod().equalsIgnoreCase("POST")){
            doPost(myRequest,myResponse);
        }else if(myRequest.getMethod().equalsIgnoreCase("GET")){
            doGet(myRequest,myResponse);
        }
    }
}

5、Servlet实现类:提供2个实现类,用于测试。

/**
 * @author wangjie
 * @version 2018/11/9
 * servlet实现类
 */
public class FindGirlServlet extends MyServlet{
    @Override
    public void doGet(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("get gril....");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    @Override
    public void doPost(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("post girl...");
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}
/**
 * @author wangjie
 * @version 2018/11/9
 */
public class HelloWorldServlet extends MyServlet {
    @Override
    public void doGet(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("get world...");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(MyRequest myRequest,MyResponse myResponse){
        try{
            myResponse.write("post world...");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

6、Servlet配置:对比之前在web开发中,会在web.xml中通过和指定哪个URL交给哪个servlet来处理。

/**
 * @author wangjie
 * @version 2018/11/9
 * servlet配置
 */
public class ServletMapping {
    private String servletName;
    private String url;
    private String clazz;

    public ServletMapping(String servletName, String url, String clazz){
        this.servletName=servletName;
        this.url=url;
        this.clazz=clazz;
    }

    public String getServletName() {
        return servletName;
    }
    public void setServletName(String servletName) {
        this.servletName = servletName;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getClazz() {
        return clazz;
    }
    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
}
/**
 * @author wangjie
 * @version 2018/11/9
 */
public class ServletMappingConfig {
    public static List<ServletMapping> servletMappingList =new ArrayList<>();
    //制定哪个URL交给哪个servlet来处理
    static{
        servletMappingList.add(new ServletMapping("findGirl","/girl","wj.mytomcat.FindGirlServlet"));
        servletMappingList.add(new ServletMapping("helloWorld","/world","wj.mytomcat.HelloWorldServlet"));
    }
}

7、启动类:
tomcat的处理流程:

1)把URL对应处理的Servlet关系形成,

2)解析HTTP协议,

3)封装请求/响应对象,

4)利用反射实例化具体的Servlet进行请求处理。

/**
 * @author wangjie
 * @version 2018/11/9
 * tomcat启动类
 */
public class MyTomcat {
    private int port=8088;
    private Map<String,String> urlServletMap =new HashMap<String,String>();
    public MyTomcat(int port){
        this.port=port;
    }

    public void start(){
//        初始化URL与对应处理的servlet的关系
        initServletMapping();

        ServerSocket serverSocket=null;
        try{
            serverSocket = new ServerSocket(port);
            System.out.println("MyTomcat is start...");

            while(true){
                Socket socket= serverSocket.accept();
                InputStream inputStream=socket.getInputStream();
                OutputStream outputStream=socket.getOutputStream();

                MyRequest myRequest= new MyRequest(inputStream);
                MyResponse myResponse =new MyResponse(outputStream);

//                请求分发
                dispatch(myRequest,myResponse);
                socket.close();
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (null != serverSocket){
                try{
                    serverSocket.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

    private void initServletMapping(){
        for(ServletMapping servletMapping:ServletMappingConfig.servletMappingList){
            urlServletMap.put(servletMapping.getUrl(),servletMapping.getClazz());
        }
    }

    public void dispatch(MyRequest myRequest,MyResponse myResponse){
        String clazz =urlServletMap.get(myRequest.getUrl());

        //反射:生成具体的Servlet实例
        try{
            Class<MyServlet> myServletClass =(Class<MyServlet>) Class.forName(clazz);
            MyServlet myServlet= myServletClass.newInstance();

            myServlet.service(myRequest,myResponse); // 调用Servlet的service()进行请求处理
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (InstantiationException e){
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        new MyTomcat(8088).start();
    }
}

8、测试:
运行项目后,在浏览器输入:localhost:8088/girl
在这里插入图片描述
在浏览器输入:localhost:8088/world
在这里插入图片描述
实践完成。

 

多线程版本:

 一个ClientRequestHandler负责接收一个clientSocket,从而处理一次客户端请求

package cn.huangyan.web.tomcat;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;

/*
 * 一个ClientRequestHandler负责接收一个clientSocket,从而处理一次客户端请求
 * */
public class ClientRequestHandler implements Runnable {

	Socket clientSocket = null;
	Map<String,String> urlServletMap = null;
	
	public ClientRequestHandler(Socket clientSocket,Map<String,String> urlServletMap) {
		this.clientSocket = clientSocket;
		this.urlServletMap = urlServletMap;
	}
	
	@Override
	public void run() {
		
		OutputStream os = null;
		InputStream is = null;
		System.out.println("当前线程是:"+Thread.currentThread().getName());
		try {
			is = clientSocket.getInputStream();
			os = clientSocket.getOutputStream();
			MyRequest request = new MyRequest(is);
			MyResponse response = new MyResponse(os);
			
			doDispatch(request,response);
			
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(clientSocket != null) {
				try {
					clientSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

	private void doDispatch(MyRequest request, MyResponse response) {
		String url = request.getUrl();
		String className = urlServletMap.get(url);
		try {
			Class clazz = Class.forName(className);
			MyServlet servlet = (MyServlet) clazz.newInstance();
			servlet.service(request, response);

		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}

	}
}

MyTomcat类

package cn.huangyan.web.tomcat;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class MyTomcat {

	private int port = 8080;
	private Map<String,String> urlServletMap  = new HashMap<>();

	public MyTomcat(int port) {
		this.port = port;
	}

	public void start() {
		initServletMapping();

		ServerSocket serverSocket = null;
		try {
			serverSocket = new ServerSocket(port);
			while(true) {
				Socket clientSocket = serverSocket.accept();
				
				// 每个客户端请求分配一个线程进行处理
				new Thread(new ClientRequestHandler(clientSocket, urlServletMap)).start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void initServletMapping() {
		for(ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
			urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazzName());
		}
	}

	public static void main(String[] args) {
		System.out.println("进来了");
		new MyTomcat(8080).start();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值