-
参考了"https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247487938&idx=1&sn=6b195038397f261c401fce11c3e8f0de&chksm=eb5394f4dc241de287ece3aba5c5d8c866ed01bd5fba6d19874d7b15888154c98071f44fe63a&mpshare=1&scene=1&srcid=0218x1u8CCLgfl5TpTFZ9OWV#rd"这篇文章,修改了部分代码,添加了一些自己的理解
-
(1) 提供Socket服务
Tomcat的启动是Socket服务,只不过它支持HTTP协议而已
(Tomcat既然是基于Socket,那么是基于BIO or NIO or AIO 呢?)(Tomcat 8 以后默认NIO)
(2) 进行请求的分发
Tomcat可以为多个Web应用提供服务,所以Tomcat可以把URL下发到不同的Web应用
(3) 把请求和响应封装成 request / response
-
从头开始手写Tomcat都需要完成哪些步骤
(1) 一个类用于记录url、Servlet名称\Servlet的class名称三者的对应关系
真实的tomcat是靠web.xml中的配置做到的
<servlet> <servlet-name>CompressionFilterTestServlet</servlet-name> <servlet-class>compressionFilters.CompressionFilterTestServlet</servlet-class> </servlet> ... <servlet-mapping> <servlet-name>CompressionFilterTestServlet</servlet-name> <url-pattern>/CompressionTest</url-pattern> </servlet-mapping>
示例
public class ServletMappingConfig { public static List<ServletMapping> servletMappingList = new ArrayList<>(); static { servletMappingList.add(new ServletMapping("haha", "/haha", "myTomcat.HahaServlet")); } } 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 String getUrl() { return url; } public String getClazz() { return clazz; } }
(2) 一个类用于解析输入流,从中提取URL、GET/POST
示例
public class MyRequest { private String url; private String method; public MyRequest(InputStream inputStream) throws IOException { // Step 1: Read request from stream String httpRequest = ""; byte[] httpRequestBytes = new byte[1024]; int requestLength = inputStream.read(httpRequestBytes); if (requestLength > 0) { httpRequest = new String(httpRequestBytes, 0, requestLength); } // Step 2: Parse the request /* A typical Http request protocol GET /favicon.ico HTTP/1.1 Accept: ./. Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Windows NT/6.1) Host: localhost:8080 Connection: Keep-Alive */ String httpHead = httpRequest.split("\n")[0]; // GET /favicon.ico HTTP/1.1 this.url = httpHead.split("\\s")[1]; // /favicon.ico this.method = httpHead.split("\\s")[0]; // GET } public String getMethod() { return method; } public String getUrl() { return url; } }
(3) 一个类用于包装输出流
示例
public class MyResponse { private OutputStream outputStream; public MyResponse(OutputStream outputStream) { this.outputStream = outputStream; } public void write(String content) throws IOException { /* A typical Http response protocol HTTP/1.1 200 OK Content-Type: text/html <html><body>...</body></html> */ String httpResponse = "HTTP/1.1 200 OK\n" + "Content-Type: text/html\n" + "\r\n" + "<html><body>" + content + "</body></html>"; outputStream.write(httpResponse.getBytes()); outputStream.close(); } }
(4) 和Tomcat类似的, 实现一个具体的Servlet
public abstract class AbstractServlet { public abstract void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException; public abstract void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException; public void service(MyRequest myRequest, MyResponse myResponse) throws IOException{ if (myRequest.getMethod().toUpperCase().equals("POST")) { doPost(myRequest, myResponse); } else if (myRequest.getMethod().toUpperCase().equals("GET")) { doGet(myRequest, myResponse); } } } public class HahaServlet extends AbstractServlet { @Override public void doGet(MyRequest myRequest, MyResponse myResponse) throws IOException { myResponse.write("Get haha"); } @Override public void doPost(MyRequest myRequest, MyResponse myResponse) throws IOException { myResponse.write("Post haha"); } }
(4) Tomcat的启动类
1° 读取URL和Servlet class的映射关系
2° 根据端口号(默认8080)创建一个ServerSocket
3° 主循环中这个ServerSocket不断accept, 等待请求到来
4° 创建新线程(或者用线程池),处理当前的请求
I. 从到来的Socket中取出输入流InputStream和输出流OutputStream II. 包装输入流和输出流为Request和Response III. 根据Request中的URL和映射关系找到对应Servlet的class位置 IV. 利用反射机制创建这个Servlet的实例对象 V. 用这个Servlet的实例对象进行操作
示例
public class MyTomcat { private final int port; private final Map<String, String> urlServletMap = new HashMap<>(); public MyTomcat(int port) { this.port = port; initServletMapping(); } private void initServletMapping() { for (ServletMapping servletMapping: ServletMappingConfig.servletMappingList) { urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz()); } } public void start() { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(this.port); while (true) { Socket currentSocket = serverSocket.accept(); Thread thread = new Thread(new SingleRequestAndResponseRunnable( currentSocket, urlServletMap)); thread.start(); } } catch (IOException e) { e.printStackTrace(); } finally { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { MyTomcat tomcat = new MyTomcat(8080); tomcat.start(); } } public class SingleRequestAndResponseRunnable implements Runnable { private final Socket socket; private final Map<String, String> urlServletMapping; public SingleRequestAndResponseRunnable(Socket socket, Map<String, String> urlServletMap) { this.socket = socket; this.urlServletMapping = urlServletMap; } @Override public void run() { try { InputStream inputStream = this.socket.getInputStream(); OutputStream outputStream = this.socket.getOutputStream(); MyRequest myRequest = new MyRequest(inputStream); MyResponse myResponse = new MyResponse(outputStream); dispatch(myRequest, myResponse); } catch (IOException e) { e.printStackTrace(); } finally { if (!this.socket.isClosed()) { try { this.socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } private void dispatch(MyRequest myRequest, MyResponse myResponse) { String clazz = this.urlServletMapping.get(myRequest.getUrl()); // Reflection used try { Class<AbstractServlet> abstractServletClass = (Class<AbstractServlet>) Class.forName(clazz); AbstractServlet abstractServlet = abstractServletClass.getDeclaredConstructor().newInstance(); abstractServlet.service(myRequest, myResponse); } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException | IOException e) { e.printStackTrace(); } } }
3_Tomcat都做了什么
最新推荐文章于 2024-04-24 07:28:52 发布