【Java Web】如何写一个简单的TomCat服务器
如何写一个简单的TomCat服务器
1. 目标
1)提供服务,接收请求(Socket通信)
2)请求信息封装成Request对象(Response对象)
3)客户端请求资源,资源分为静态资源(html)和动态资源(Servlet)
4)资源返回给客户端浏览器
2.流程图
3.pom文件配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>SimpleTomCat</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> </plugins> </build> </project>
4.资源配置
4.1 h1.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>h1页面测试</title> </head> <body> <h1> 手写TomCat静态页面1 </h1> </body> </html>
4.2 web.xml
<?xml version="1.0" encoding="UTF-8" ?> <web-app> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.tomcat.servlet.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/my</url-pattern> </servlet-mapping> </web-app>
5. Response 与 Request 对象
package com.tomcat.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.Properties; /** * @author sz * @DATE 2022/5/1 15:32 */ @Data public class Request { /** * 请求方法 */ private String method; /** * 请求路径 */ private String url; private InputStream inputStream; public Request(InputStream inputStream) throws IOException { this.inputStream=inputStream; byte[] bytes = new byte[1024]; int start; String str = ""; while (-1 != (start = inputStream.read(bytes))) { str += new String(bytes,0,start); if (start<1024){ break; } } String[] split = str.split("\\n"); //请求头第一行 try { method = split[0].split(" ")[0]; } catch (Exception e) { method="GET"; } try { url=split[0].split(" ")[1]; } catch (Exception e) { url="/"; } }; }
package com.tomcat.pojo; import com.tomcat.util.HttpUtils; import com.tomcat.util.ResponseUtils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.*; import java.nio.charset.StandardCharsets; /** * @author sz * @DATE 2022/5/1 15:32 */ @Data public class Response { private OutputStream outputStream; public Response(OutputStream outputStream){ this.outputStream=outputStream; } /** * 根据url 返回静态资源 * @param str 静态资源url */ public void outputHtml(String str) throws IOException { //获取资源的绝对路径 String absPath = ResponseUtils.getAbsPath(str); File file = new File(absPath); if (file.exists() && file.isFile()){ //输出资源 ResponseUtils.writeStaticHtml(new FileInputStream(file),outputStream); }else { //返回404 outputStream.write(HttpUtils.writeNotFound().getBytes(StandardCharsets.UTF_8)); outputStream.close(); } } public void output(String str) throws IOException { outputStream.write(str.getBytes(StandardCharsets.UTF_8)); outputStream.close(); } }
6. HttpUtils 与 ResponseUtils 工具类
package com.tomcat.util; /** * @author sz * @DATE 2022/5/1 15:32 */ public class HttpUtils { private HttpUtils(){}; //输出成功的内容信息 public static String writeSuccess(){ String str = "HTTP/1.1 200 OK"+"\n" +"Content-Type: text/html;charset=utf-8"+"\n" +"\r\n"; return str; } //输出404的内容信息 public static String writeNotFound(){ String str = "HTTP/1.1 404 NotFound"+"\n" +"Content-Type: text/html;charset=utf-8"+"\n" +"\r\n" +"<h1> HTTP/1.1 404 NotFound </h1>"; return str; } }
package com.tomcat.util; import lombok.Data; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; /** * @author sz * @DATE 2022/5/1 16:11 */ @Data public class ResponseUtils { private ResponseUtils() { } ; /** * 获取静态资源的绝对路径 * * @param path * @return */ public static String getAbsPath(String path) { return (ResponseUtils.class.getResource("/") + path.substring(1)).split("file:/")[1]; } /** * 输出静态资源 * * @param inputStream * @param outputStream */ public static void writeStaticHtml(InputStream inputStream, OutputStream outputStream) throws IOException { //首先输出请求头 outputStream.write(HttpUtils.writeSuccess().getBytes(StandardCharsets.UTF_8)); //再输出请求体 byte[] bytes = new byte[1024]; int len; if (-1 != (len = inputStream.read(bytes))) { outputStream.write(bytes,0,len); } outputStream.close(); } }
7. Serlvet 处理动态资源
package com.tomcat.inter; import com.tomcat.pojo.Request; import com.tomcat.pojo.Response; public interface Servlet { void init(); void destory(); void service(Request request, Response response); }
package com.tomcat.abs; import com.tomcat.inter.Servlet; import com.tomcat.pojo.Request; import com.tomcat.pojo.Response; /** * @author sz * @DATE 2022/5/1 20:16 */ public abstract class HttpServlet implements Servlet { public abstract void doGet(Request request, Response response); public abstract void doPost(Request request, Response response); @Override public void service(Request request, Response response) { if ("GET".equals(request.getMethod())) { doGet(request, response); } else { doPost(request, response); } } }
package com.tomcat.servlet; import com.tomcat.abs.HttpServlet; import com.tomcat.pojo.Request; import com.tomcat.pojo.Response; import com.tomcat.util.HttpUtils; import java.io.IOException; import java.util.concurrent.TimeUnit; /** * @author sz * @DATE 2022/5/1 20:20 */ public class MyServlet extends HttpServlet { @Override public void doGet(Request request, Response response) { // try { // TimeUnit.DAYS.sleep(1); // } catch (InterruptedException e) { // e.printStackTrace(); // } String context = "<h1> MyServlet GET </h1>" + "\r\n"+Thread.currentThread().getName(); try { response.output(HttpUtils.writeSuccess()+context); } catch (IOException e) { e.printStackTrace(); } } @Override public void doPost(Request request, Response response) { String context = "<h1> MyServlet POST </h1>"+ "\r\n"+Thread.currentThread().getName(); try { response.output(HttpUtils.writeSuccess()+context); } catch (IOException e) { e.printStackTrace(); } } @Override public void init() { } @Override public void destory() { } }
8. RequestProcess 请求处理器
package com.tomcat.process; import com.tomcat.abs.HttpServlet; import com.tomcat.pojo.Request; import com.tomcat.pojo.Response; import lombok.AllArgsConstructor; import java.net.ServerSocket; import java.net.Socket; import java.util.Map; /** * @author sz * @DATE 2022/5/1 21:02 */ @AllArgsConstructor public class RequestProcess extends Thread{ private Socket accept; private Map<String, HttpServlet> servletMap; @Override public void run() { try { //获取Request对象 Request request = new Request(accept.getInputStream()); //获取Response对象 Response response = new Response(accept.getOutputStream()); //写回资源 HttpServlet httpServlet = servletMap.get(request.getUrl()); if (null==httpServlet){ //为nulll,静态资源 response.outputHtml(request.getUrl()); }else { //不为null 动态资源 httpServlet.service(request,response); } }catch (Exception e){ } } }
9. Main 方法 服务器启动入口
package com.tomcat.main; import com.tomcat.abs.HttpServlet; import com.tomcat.process.RequestProcess; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author sz * @DATE 2022/5/1 10:30 */ public class Main { /*设置端口号*/ private static Integer port = 8080; public static void main(String[] args) throws IOException { Main main = new Main(); main.start(); } public void start() throws IOException { loadServlet(); //创建线程池 ThreadPoolExecutor tomcatThreadPool = new ThreadPoolExecutor( 10, 100, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100) , new ThreadFactory() { int i = 0; @Override public Thread newThread(Runnable r) { return new Thread(r, "TomCat线程" + "---" + (++i)); } }, new ThreadPoolExecutor.CallerRunsPolicy() ); //创建连接 ServerSocket serverSocket = new ServerSocket(port); while (true) { //获取套接字 Socket accept = serverSocket.accept(); RequestProcess requestProcess = new RequestProcess(accept, servletMap); tomcatThreadPool.execute(requestProcess); } } private Map<String, HttpServlet> servletMap = new HashMap<String, HttpServlet>(); /** * 加载解析web.xml,初始化Servlet */ private void loadServlet() { InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml"); SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(resourceAsStream); Element rootElement = document.getRootElement(); List<Element> selectNodes = rootElement.selectNodes("//servlet"); for (int i = 0; i < selectNodes.size(); i++) { Element element = selectNodes.get(i); // <servlet-name>myServlet</servlet-name> Element servletnameElement = (Element) element.selectSingleNode("servlet-name"); String servletName = servletnameElement.getStringValue(); // <servlet-class>com.tomcat.servlet.MyServlet</servlet-class> Element servletclassElement = (Element) element.selectSingleNode("servlet-class"); String servletClass = servletclassElement.getStringValue(); // 根据servlet-name的值找到url-pattern Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']"); // /my String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue(); servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance()); } } catch (DocumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public Integer getPort() { return port; } public void setPort(Integer port) { port = port; } }#Java开发##Java##面经##笔试题目#