一、绪论
tomcat实现了基于http协议的双向通信。为简单模拟该功能,我们需要自定义Request,Response,Servlet,并通过Socket为浏览器和服务器建立通信通道。
故,此次简易版Tomcat项目结构因如下所示
二、创建核心对象
2.1 MyRequest
简易request需要获取请求体中的访问路径、请求方法、参数内容
/**
* @author sunyiran
* @date 2018-09-28
*/
public class MyRequest {
/**请求相对路径*/
private String url;
/**协议请求方式*/
private String method;
/**请求参数(简易版不处理)*/
private HashMap<String,Object> params = new HashMap<>();
public MyRequest(InputStream inputStream) throws IOException {
//available()效率高但是不可靠
byte[] b = new byte[inputStream.available()];
inputStream.read(b);
String head = new String(b);
/*
* http请求体内容格式
* GET /favicon.ico HTTP/1.1
* Host: localhost:8080
* Connection: keep-alive
* .......
* */
String[] info = head.split("\\n")[0].split("\\s");
method = info[0];
if (info.length > 1) {
url = info[1];
}
}
public String getUrl() {
return url;
}
public String getMethod() {
return method;
}
public HashMap<String, Object> getParams() {
return params;
}
}
2.2 MyResponse
/**
* @author sunyiran
* @date 2018-09-28
*/
public class MyResponse {
private OutputStream outputStream;
public MyResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void write(String content) {
StringBuilder sb = new StringBuilder();
//模拟http响应体
sb.append("HTTP/1.1 200 OK \n")
.append("Content-Type: text/html\n")
.append("\r\n").append("<html><body>")
.append(content).append("</body></html>");
try {
outputStream.write(sb.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.3 MyServlet
/**
* @author sunyiran
* @date 2018-09-28
*/
public abstract class MyServlet {
public abstract void doPost(MyRequest request, MyResponse response);
public abstract void doGet(MyRequest request, MyResponse response);
/**
* 处理请求
*
* @param request
* @param response
*/
public void service(MyRequest request, MyResponse response) {
if (request.getMethod().equals("GET")) {
doPost(request, response);
} else {
doGet(request, response);
}
}
}
2.4 MyCat
/**
* @author sunyiran
* @date 2018-09-28
*/
public class MyCat {
/**
* 默认端口
*/
private int port = 8080;
/**
* servlet映射
*/
private static Map<String, String> mappers = new HashMap<>();
public void start() throws Exception {
initMapping();
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("MyCat is starting ......");
while (true) {
//监听port端口
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
//请求分发
dispatch(new MyRequest(inputStream), new MyResponse(outputStream));
}
}
public void initMapping() {
//测试映射
mappers.put("/hello", "com.syr.HelloServlet");
}
/**
* 根据请求寻找具体处理servlet
*
* @param request
* @param response
* @throws Exception
*/
public void dispatch(MyRequest request, MyResponse response) throws Exception {
String url = request.getUrl();
if (url == null) {
response.write("can not find the path");
return;
}
if (url.equalsIgnoreCase("/favicon.ico")) {
//可以自定义略缩图
return;
}
if (!mappers.containsKey(url)) {
response.write("can not find the path");
return;
}
//反射获取处理对象
Class<?> aClass = Class.forName(mappers.get(url));
aClass.getMethod("service", MyRequest.class, MyResponse.class).invoke(aClass.newInstance(), request, response);
}
public static void main(String[] args) throws Exception {
new MyCat().start();
}
}
2.5 HelloServlet(测试用)
/**
* @author sunyiran
* @date 2018-09-28
*/
public class HelloServlet extends MyServlet {
@Override
public void doPost(MyRequest request, MyResponse response) {
doGet(request, response);
}
@Override
public void doGet(MyRequest request, MyResponse response) {
response.write("welcome to myCat");
}
}