[自己造轮子] 动手写一个 Tomcat 容器

版权声明:有问题欢迎留言,转载请声明出处 https://blog.csdn.net/larger5/article/details/82872643

一、前言

IT 行业变化多端,流行的技术层出不穷,
而万变不离其宗,学习其设计思想,才是最为重要。
Tomcat,基于 HTTP 协议,根据请求链接,返回相应的资源。

[自己造轮子] 动手写一个 JPA 框架
[自己造轮子] 动手写一个 SpringMVC 框架

二、代码

代码参考了网上,做了一定的修改,使之符合 Maven 的项目规范
在这里插入图片描述

1、Tomcat 启动文件

package testTomcat;

import myTomcat.Servlet;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

public class TestServer {

	//定义一个变量,存放服务端WebContent目录的绝对路径
	public static String WEB_ROOT=System.getProperty("user.dir")+"\\src\\main\\webapp";
	//定义静态变量,用于存放本次请求的静态页面名称
	private static String url="";
	
	//定义一个静态类型MAP,存储服务端conf.properties中的配置信息
	private static Map<String,String> map=new HashMap<String,String>();
	
	static {
		//服务器启动之前将配置参数中的信息加载到MAP中
		//创建一个Properties对象
		Properties prop=new Properties();
		try {
			//加载WebContent目录下的conf.properties文件
			prop.load(new FileInputStream(System.getProperty("user.dir")+"\\src\\main\\resources\\conf.properties"));
			//将配置文件中的数据读取到MAP中	
			Set set=prop.keySet();
			Iterator iterator = set.iterator();
			while(iterator.hasNext()) {
				String key=(String)iterator.next();
				String value = prop.getProperty(key);
				map.put(key, value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	public static void main(String[] args) throws IOException {
		System.out.println(map);
		ServerSocket serverSocket=null;
		Socket socket=null;
		InputStream is=null;
		OutputStream ops=null;
		try {
			//创建ServerSocket,监听本机器的80端口,等待来自客户端的请求
			serverSocket=new ServerSocket(8987);
			while(true) {
				//获取到客户端对应的socket
				socket= serverSocket.accept();
				//获取到输入流对象
				is = socket.getInputStream();
				//获取到输出流对象
				ops = socket.getOutputStream();
				//获取HTTP协议的请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url demo01.html aa
				parse(is);
				//判断本次请求的是静态demo.html页面还是运行在服务端一段JAVA小程序
				if(null!=url) {
					if(url.indexOf(".")!=-1) {
						//发送静态资源文件
						sendStaticResource(ops);
					}else {
						//发送动态资源
						sendDynamicResource(is,ops);
					}
				}	
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//释放资源
			if(null!=is) {
				is.close();
				is=null;
			}
			if(null!=ops) {
				ops.close();
				ops=null;
			}
			if(null!=socket) {
				socket.close();
				socket=null;
			}
		}
	}

	private static void sendDynamicResource(InputStream is, OutputStream ops) throws Exception {
		//将HTTP协议的响应行和响应头发送到客户端
		ops.write("HTTP/1.1 200 ok\n".getBytes());
		ops.write("Server:Apache\n".getBytes());
		ops.write("Content-type:text/html;charset=utf-8\n".getBytes());
		ops.write("\n".getBytes());
		//判断map中的是否存在一个key,这个key是否和本次待请求的资源路径一致
		if(map.containsKey(url)) {
			//如果包含指定的key,获取到MAP中key对应的value部分
			String value=map.get(url);
			//通过反射将对应的JAVA程序加载到内存
			Class clazz=Class.forName(value);
			Servlet servlet=(Servlet)clazz.newInstance();
			//执行init方法
			servlet.init();
			//执行service方法
			servlet.Service(is, ops);
		}
	}

	//获取HTTP协议的请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
	private static void parse(InputStream is) throws IOException {
		//定义一个变量,存放HTTP协议请求部分数据
		StringBuffer content=new StringBuffer(2048);
		//定义一个数组,存放HTTP协议请求部分数据
		byte[] buffer=new byte[2048];
		//定义一个变量i,代表读取数据到数组中之后,数据量的大小
		int i=-1;
		//读取客户端发送过来的数据,将数据读取到字节数组buffer中.i代表读取数据量的大小
		i=is.read(buffer);
		//遍历字节数组,将数组中的数据追加到content变量中
		for(int j=0;j<i;j++) {
			content.append((char)buffer[j]);
		}
		//打印HTTP协议请求部分数据
		System.out.println(content);
		//截取客户端要请求的资源路径 demo.html,赋值给url
		parseUrl(content.toString());
	}

	//截取客户端要请求的资源路径 demo.html,赋值给url
	private static void parseUrl(String content) {
		//定义2个变量,存放请求行的2个空格的位置
		int index1,index2;
		//获取http请求部分第1个空格的位置
		index1=content.indexOf(" ");
		if(index1!=-1) {
			index2=content.indexOf(" ",index1+1);
			//获取http请求部分第2个空格的位置
			if(index2>index1) {
				//截取字符串获取到本次请求资源的名称
				url=content.substring(index1+2, index2);
			}
		}
		//打印本次请求静态资源名称
		System.out.println(url);
	}

	//发送静态资源
	private static void sendStaticResource(OutputStream ops) throws IOException {
		//定义一个字节数组,用于存放本次请求的静态资源demo01.html的内容
		byte[] bytes=new byte[2048];
		//定义一个文件输入流,用户获取静态资源demo01.html中的内容
		FileInputStream fis=null;
		try {
			//创建文件对象File,代表本次要请求的资源demo01.html
			File file=new File(WEB_ROOT,url);
			//如果文件存在
			if(file.exists()) {
				//向客户端输出HTTP协议的响应行/响应头
				ops.write("HTTP/1.1 200 OK\n".getBytes());
				ops.write("Server:apache-Coyote/1.1\n".getBytes());
				ops.write("Content-Type:text/html;charset=utf-8\n".getBytes());
				ops.write("\n".getBytes());
				//获取到文件输入流对象
				fis=new FileInputStream(file);
				//读取静态资源demo01.html中的内容到数组中
				int ch=fis.read(bytes);
				while(ch!=-1) {
					//将读取到数组中的内容通过输出流发送到客户端				
					ops.write(bytes, 0, ch);
					ch=fis.read(bytes);
				}
			}else {
				//如果文件不存在
			    //向客户端响应文件不存在消息 	
				ops.write("HTTP/1.1 404 not found\n".getBytes());
				ops.write("Server:apache-Coyote/1.1\n".getBytes());
				ops.write("Content-Type:text/html;charset=utf-8\n".getBytes());
				ops.write("\n".getBytes());
				String errorMessage="file not found";
				ops.write(errorMessage.getBytes());
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			//释放文件输入流对象
			if(null!=fis) {
				fis.close();
				fis=null;
			}
		}

	}
}

2、servlet

接口

package myTomcat;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

//所有服务端的JAVA小程序要实现的接口
public interface Servlet {
	//初始化
	public void init();
	//服务
	public void Service(InputStream is, OutputStream ops) throws IOException;
	//销毁
	public void destroy();
}

实现

package myTomcat;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class helloServlet implements Servlet {
    public void init() {

    }

    public void Service(InputStream is, OutputStream ops) throws IOException {
        ops.write("Hello world".getBytes());
        ops.flush(); // 立马输出
    }

    public void destroy() {

    }
}

3、配置

conf.properties

aa=myTomcat.helloServlet

4、测试页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>cun</title>
</head>
<body>
    cun
</body>
</html>

三、效果

测试 servlet

在这里插入图片描述

测试静态资源

在这里插入图片描述

四、其他

真正做到和 Tomcat 功能完备,还需有很长的路要走,毕竟前辈们多年的努力不是白费的。

猜你喜欢

转载自blog.csdn.net/larger5/article/details/82872643