0
点赞
收藏
分享

微信扫一扫

自定义tomcat

村里搬砖的月野兔 2022-01-20 阅读 90
  • MyCatServer
package com.yc.tomcat;

import java.net.ServerSocket;
import java.net.Socket;

public class MyCatServer {
    //创建一个日志输出对象      当前类的反射字节码
    //private Logger logger = Logger.getLogger(MyCatServer.class);

    public static void main(String[] args) {
        MyCatServer mcs = new MyCatServer();
        mcs.startServer();
    }

    public void startServer() {
        //TODO 最好将8080的端口配置放到“个 conf/server.xml 文件中
        int port = 8080;
        boolean flag = false;

        //TODO 实例化线程池

        try (ServerSocket ss = new ServerSocket(port)) {
            //logger.debug("服务器" + ss.getLocalSocketAddress() + "启动了,监听了:" + ss.getLocalPort() + "端口");

            while (!flag) {
                Socket s = ss.accept();
                //logger.debug("客户端: " + s.getRemoteSocketAddress() + "联接了服务器");

                //TODO 增加一个任务, 再将任务放到线程池

                Thread t = new Thread(new TaskService(s));
                t.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            //logger.error("服务器启动错误:" + e.getMessage());
        }
    }
}
  • TaskService
package com.yc.tomcat;

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

/**
 * 任务类   处理请求
 */
public class TaskService implements Runnable {
    private Socket socket;
    private InputStream in;
    private OutputStream out;
    private boolean flag;
    private String connection = "close";
    //private Logger logger = Logger.getLogger(TaskService.class);

    public TaskService(Socket socket) {
        this.socket = socket;
        flag = true;
        try {
            in = socket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
            //logger.error("获取输入流错误");
            flag = false;
        }
        try {
            out = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            //logger.error("获取输出流错误");
            flag = false;
        }
    }


    @Override
    public void run() {
        while (flag) {
            //Connection close Socket立马关闭
            //Connection keep-alive Socket不能用完就关闭
            try {
                //解析请求信息

                //1.解析HTTP请求
                HttpServletRequest request = new HttpServletRequest(this.in, socket);
                //2.根据请求中的资源地址,读取数据,拼接HTTP响应,以输出流返回到浏览器
                HttpServletResponse response = new HttpServletResponse(request, this.out);

            } catch (Exception e) {
                e.printStackTrace();
                //logger.error("线程运行错误:" + e.getMessage());
            } finally {
                //如果try中没有()加finally做Connection==close是则关闭

                //看协议  如果是keep-alive就不会关闭资源 flag=true 循环继续处理  节省线程开关时间,性能提升
                //争对协议****************短链接就直接关了*********长连接继续循环
                if ("close".equals(connection)) {
                    flag = false;
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (out != null) {
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (socket != null) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
  • HttpServletRequest
package com.yc.tomcat;

import java.io.File;
import java.io.InputStream;
import java.net.Socket;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: thread
 * @description: 请求处理
 * @author: 作者
 * @create: 2022-01-18 13:41
 */
public class HttpServletRequest {
    private InputStream in;
    private Socket socket;

    //解析HTTP请求  属性 version method requestURI header(Map)头域信息 参数parameterMap
    private String method;
    private String requestURL;//标识符 http://locahost:81/res/xxx.action
    private String requestURI;//定位符 /res/xxx.action
    private String contextPath;//上下文 /res
    private String queryString;//地址栏参数
    private Map<String, String[]> parameterMap = new ConcurrentHashMap<>();//参数
    private String scheme;//协议类型
    private String protocol;//协议
    private String realPath;//真实路径

    public HttpServletRequest(InputStream in, Socket socket) {
        this.in = in;
        this.socket = socket;

        //解析
        parseRequest();
    }

    private void parseRequest() {
        String requestInfoString = readFromInpuStream();
        //System.out.println("请求:" + requestInfoString);
        if (requestInfoString == null || "".equals(requestInfoString)) {
            throw new RuntimeException("读取输入流异常");
        }
        parseRequestInfoString(requestInfoString);
    }

    /**
     * 解析请求参数
     */
    private void parseRequestInfoString(String requestInfoString) {
        //字符串分割器——根据自然分隔符分割
        StringTokenizer st = new StringTokenizer(requestInfoString);
        this.method = st.nextToken();
        this.requestURI = st.nextToken();

        int questionIndex = this.requestURI.indexOf("?");
        if (questionIndex >= 0) {
            this.queryString = this.requestURI.substring(questionIndex + 1);
            this.requestURI = this.requestURI.substring(0, questionIndex);
        }
        this.protocol = st.nextToken();
        this.scheme = this.protocol.substring(0, this.protocol.indexOf("/"));//http

        int slashIndex = this.requestURI.indexOf("/", 1);
        if (slashIndex >= 0) {
            this.contextPath = this.requestURI.substring(0, slashIndex);
        } else {
            this.contextPath = this.requestURI;
        }

        this.requestURL = this.scheme + "://" + this.socket.getRemoteSocketAddress() + ":" + this.requestURI;

        //暂时先没有考虑表单提交的参数
        if (this.queryString != null && this.queryString.length() > 0) {
            String[] ps = this.queryString.split("&");
            for (String s : ps) {
                String[] params = s.split("=");
                this.parameterMap.put(params[0], params[1].split(","));
            }
        }

        this.realPath = System.getProperty("user.dir") + File.separator + "webapps";
    }

    /**
     * 从输入流读取所有HTTP协议
     *
     * @return
     */
    private String readFromInpuStream() {
        //只考虑普通文本
        String requestInfoString = null;
        StringBuffer sb = new StringBuffer(100 * 1024);
        int length = -1;
        byte[] bs = new byte[100 * 1024];
        try {
            length = this.in.read(bs);//实际读取长度
        } catch (Exception e) {
            e.printStackTrace();
            length = 0;
        }

        //拼接协议
        for (int i = 0; i < length; i++) {
            sb.append((char) bs[i]);
        }

        requestInfoString = sb.toString();
        return requestInfoString;
    }

    public Socket getSocket() {
        return socket;
    }

    public String getContextPath() {
        return contextPath;
    }

    public String getRealPath() {
        return realPath;
    }

    public String getMethod() {
        return method;
    }

    public InputStream getIn() {
        return in;
    }

    public String getRequestURL() {
        return requestURL;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getQueryString() {
        return queryString;
    }

    public Map<String, String[]> getParameterMap() {
        return parameterMap;
    }

    public String getScheme() {
        return scheme;
    }

    public String getProtocol() {
        return protocol;
    }
}
  • HttpServletResponse
package com.yc.tomcat;

import java.io.*;

/**
 * @program: thread
 * @description:
 * @author: 作者
 * @create: 2022-01-18 13:41
 */
public class HttpServletResponse {
    private HttpServletRequest request;
    private OutputStream out;

    public HttpServletResponse(HttpServletRequest request, OutputStream out) {
        this.request = request;
        this.out = out;

        //读取数据 拼接响应 完成输出   从request的requestURI属性获取资源名字
        outResult();
    }

    /**
     * 完成输出
     * 1.从request中去除要输出的文件
     * 2.读文件
     * 3.拼接HTTP协议
     * 4.调用流输出
     */
    private void outResult() {
        //要输出的文件字节数组
        byte[] fileContent = null;
        //响应的协议
        String responseProtocol = null;

        //keaiwu/index.html
        String uri = this.request.getRequestURI();
        // 项目的webapps位置
        String fileName = this.request.getRealPath() + uri;
        //System.out.println("请求的文件路径:" + fileName);

        File file = new File(fileName);
        if (file.exists() == false) {
            File file404 = new File(this.request.getRealPath(), "404.html");
            fileContent = readFile(file404);
            responseProtocol = get404Protocol(fileContent);
        } else {
            //非404
            fileContent = readFile(file);
            responseProtocol = get200Protocol(fileContent);
        }

        try {
            this.out.write(responseProtocol.getBytes());
            this.out.flush();
            this.out.write(fileContent);
            this.out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (this.out != null) {
                try {
                    this.out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private String get200Protocol(byte[] fileContent) {
        String uri = this.request.getRequestURI();
        int index = uri.lastIndexOf(".");
        if (index >= 0) {
            index = index + 1;
        }
        String extension = uri.substring(index);
        String pro200 = "";

        //TODO 策略模式
        if ("html".equalsIgnoreCase(extension) || "htm".equalsIgnoreCase(extension)) {
            pro200 = "HTTP/1.1 200\r\n" +
                    "Content-Type: text/html\r\n" +
                    "Content-Length: " + fileContent.length + "\r\n\r\n";
        } else if ("css".equalsIgnoreCase(extension)) {
            pro200 = "HTTP/1.1 200\r\n" +
                    "Content-Type: text/css\r\n" +
                    "Content-Length: " + fileContent.length + "\r\n\r\n";
        } else if ("js".equalsIgnoreCase(extension)) {
            pro200 = "HTTP/1.1 200\r\n" +
                    "Content-Type: text/javascript\r\n" +
                    "Content-Length: " + fileContent.length + "\r\n\r\n";
        } else if ("jpg".equalsIgnoreCase(extension) || "jpeg".equalsIgnoreCase(extension)) {
            pro200 = "HTTP/1.1 200\r\n" +
                    "Content-Type: text/jpeg\r\n" +
                    "Content-Length: " + fileContent.length + "\r\n\r\n";
        }
        return pro200;
    }

    /**
     * 生成404协议
     *
     * @param fileContent
     * @return
     */
    private String get404Protocol(byte[] fileContent) {
        //协议的拼接
        String pro404 = "HTTP/1.1 404\r\n" +
                "Content-Type: text/html;charset=utf-8\r\n" +
                "Content-Language: zh-CN\r\n" +
                "Content-Length: " + fileContent.length + "\r\n\r\n";
        return pro404;
    }

    /**
     * 读文件
     *
     * @param file
     * @return
     */
    private byte[] readFile(File file) {
        FileInputStream fin = null;
        //字节数组内存流-----数据可能很大
        //ByteArrayOutputStream 写在内存上使用
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            fin = new FileInputStream(file);
            byte[] bs = new byte[10 * 1024];
            int length = -1;
            while ((length = fin.read(bs, 0, bs.length)) != -1) {
                baos.write(bs, 0, length);
                baos.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return baos.toByteArray();
    }
}
举报

相关推荐

0 条评论