【Java Web】如何写一个简单的TomCat服务器

举报
秋日的晚霞 发表于 2022/05/10 22:07:36 2022/05/10
【摘要】 如何写一个简单的TomCat服务器 1. 目标1)提供服务,接收请求(Socket通信)2)请求信息封装成Request对象(Response对象)3)客户端请求资源,资源分为静态资源(html)和动态资源(Servlet)4)资源返回给客户端浏览器 2.流程图 3.pom文件配置<?xml version="1.0" encoding="UTF-8"?><project xmlns="h...

如何写一个简单的TomCat服务器

1. 目标

1)提供服务,接收请求(Socket通信)

2)请求信息封装成Request对象(Response对象)

3)客户端请求资源,资源分为静态资源(html)和动态资源(Servlet)

4)资源返回给客户端浏览器

idea64_nO8ZJMKT92

2.流程图

TomCat-导出

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 处理动态资源

image-20220501215359989

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;
    }
}

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。