手写一个简单的Tomcat

首先tomcat的原理是什么?

手写一个简单的Tomcat_第1张图片

上面的图可以简化为:

手写一个简单的Tomcat_第2张图片

这张图固然缺少了很多东西,但是却是tomcat实现功能的一个基本逻辑

简单的来说,就是用户发出一个请求,后台处理url,通过组合根地址,找到本地的资源地址,然后通过读取这个资源,将它的流输出到页面,这就是tomcat的简单原理

那如何实现呢?

简单的来说:Tomcat由三个基本类组成

  • HttpServer
  • Request
  • Response

Request负责处理url

Response负责处理响应的信息

HttpServer负责阻塞IO,监听消息

既然职责已经分好了,那我们就用代码来实现这个简单的Tomcat吧

首先是HttpServer类:

public class HttpServer{	
	//检验是否运行
	public boolean shutdown = false;
	//阻塞接口的方法
	public void acceptWait(){
		
			SocketServer sever = null;
			
			//配置server
			try{
				//配置服务器的端口,线程连接数,以及地址
				server = new SocketServer(8080,1,"127.0.0.1");
			}catch(Exception ex){
				ex.printStack();
			}
			
			while(!shutdown){
			
				try{
				//创建socket对象
				Socket socket = SocketServer.accept();
				//获取流对象
				InputStream input = socket.getInputStream();
				OutputStream output = socket.getOutStream();
				//根据输入流构建请求对象
				Request request = new Request(input);
				//解析对应的url
				request.parse();

				//根据对应的输入流构建响应对象
				Response response = new Response(output);
			   //设置其本身的请求对象
			   response.setRequest(request);
				//响应静态资源
				response.sendStaticResource();

				//判断url是否是/shutdown,如果是,则关闭容器
				if(null!=request){
					shutdown = request.getUrl().equals("/shutdown");
				}
	
				}catch(Exception ex){	
					ex.printStack();
					continue;
				}
			
			}
			
	}
	
	//运行HttpServer组件
	public static void main(String  args[]){
		HttpServer server = new HttpServer();
		server.acceptWait();

	}

}

下面构建Request对象

/*
	request对象的基本职能就是接受一个输入流,
	从socket中去解析对应的url,并且返回即可
*/
public class Request{
	private String url;
	private InputStream is;
	//构建方法
	public Request(InputStream is){
		this.is = is;
	}	
	
	//解析输入的url
	public void parse(){
			
			//1.首先需要从socket中的输入流获取信息并且转换为String
			//1.1首先准备一个固定长度的StringBuffer和缓冲数组
			StringBuffer request = new StringBuffer(Response.BUFFER_SIZE);
			byte[] buffer = new byte[Response.BUFFER_SIZE];
			
			//1.2从输入流中读取对应的字节长度
			try{
				int ch = is.read(buffer,0,ReSponse.BUFFER_SIZE);
			}catch(Exception ex){
				ex.printStack();
				ch = -1;
			}
			
			//1.3假如有效
			for(int i = 0; i < ch;i++){
				request.append((char)buffer[i]);
			}
			
			//2.其次从String中获取对应的url信息
			url = parseUrl(request.toString());
	}
	
	//Url的解析方法:这里要对socket的header进行解析
	public void parseUrl(String requestString){
		//设定索引
		int index1,index2;
		
		//需要解释一下的是,从socket输入流获取的信息其实长下面这样的
		//而我们只需要"/hello/text.txt"这么一节就行了
		/*
		GET /hello/text.txt HTTP/1.1
		Host: localhost:8080
		User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
		Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
		Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
		Accept-Encoding: gzip, deflate
		Connection: keep-alive
		Upgrade-Insecure-Requests: 1
		*/
		index1 = requestString.indexOf(' ');//注意是空格,因为头信息是根据空格分隔到的	
		//如果请求头有效
	    if(index1!=-1){
	    	//下面的表示从index1+1的位置开始查找下一个" "的索引
			index2 = requestString.indexOf(' ',index1+1);
			
			if(index2 > index1) return requestString.subString(index1+1,index2);
			
		 }	
		
		//是在没有
		return null;
	}

}

下面展开response的方法

public class Response{
	//默认大小
	public static final int BUFFER_SIZE = 2048;
	//默认根路径
	public static final String WEB_ROOT = "D:";
	//输出流
	private OutputStream os;
	//请求对象
	private Request request;
	
	//创建方法
	public Response(OutputStream os){
		this.os = os;
	}
	//设置request方法
	public void setRequest(Request request){
		this.request = request;
	}
	
	public void sentStaticResource() throws Exception{
			//此处需要开启文件流,文件流最终是需要关闭的,所以必须先去做文件流的finally关闭
			FileInputStream fls = null;
			byte[] buffer = new byte[Response.BUFFER_SIZE];
			
			try{
				//创建文件
				File file = new File(WEB_ROOT,request.getUrl());
				//如果文件存在且不是一个文件夹,就创建文件流
				if(file!=null && !file.isDirectory()){
				
					fls = new FileInputSteam(file);
					//读取字节个数
					int ch = fls.read(buffer,0,BUFFER_SIZE);
					while(ch!=-1){
						os.write(buffer,0,ch);
						ch = fls.read(buffer,0,BUFFER_SIZE);
					}
		
				}else{
				                    //假如不存在
                    //编写响应内容
                    String retMessage = "

"+file.getName()+" file or directory not exists

"
; //编写响应头 String returnMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type:text/html\r\n"+ "Content-Length:"+retMessage.length() + "\r\n" + "\r\n"+ retMessage; //将响应信息的二进制信息写入到输出流中 output.write(returnMessage.getBytes()); } }catch(Exception ex){ ex.printStack(); }finally{ if(fls!=null){ fls.close(); } } } }

最后就是测试了

在D盘下新建text.txt文件,写入内容hello tomcat

手写一个简单的Tomcat_第3张图片

最后的效果

手写一个简单的Tomcat_第4张图片

最后需要注意的是

不要用谷歌浏览器去访问,因为我这边的Demo没有写200的响应头,所以谷歌是无法解析的,但是火狐浏览器是可以解析的

并且这个实验可以用maven做,不需要添加任何的组件

参考文章:
Tomcat原理以及详解:https://www.cnblogs.com/irockcode/p/6796531.html
阿里云社区手写tomcat:https://www.jianshu.com/p/bc9343a95554

你可能感兴趣的:(tomcat)