自定义tomcat服务器

log4j.properties

在这里插入图片描述

MyCatServer

package com.yc.tomcat;
import java.net.ServerSocket;
import java.net.Socket;

public class MyCatServer {
	//日志 log4j包
    //private Logger logger = Logger.getLogger(MyCatServer.class);

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

    public void startServer() {
    	boolean flag = false;
    	
        //TODO 1.将8080的端口配置放到 conf/server.xml文件中 -> xml解析 -> DOM模型
        //TODO 2.线程池
        Map<String, Map<String, String>> map = parseServerXml();
        ThreadPoolExecutor executor = null;
        int port = 8080;
        if (map.get("Connector").get("port") != null) {
            port = Integer.parseInt(map.get("Connector").get("port"));
        }
        boolean openPool = false;
        if (map.get("Connector").get("executor") != null && "true".equalsIgnoreCase(map.get("Connector").get("executor"))) {
            openPool = true;
        }

        if (openPool) {
            int corePoolSize = Runtime.getRuntime().availableProcessors();
            int maxPoolSize = corePoolSize * 2;
            long keepAliveTime = 10;
            TimeUnit unit = TimeUnit.SECONDS;
            BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(maxPoolSize * 4);
            ThreadFactory threadFactory = new NameThreadFactory();
            RejectedExecutionHandler handler = new MyIgnorePolicy();
            executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
            //预启动所有核心线程,提升效率
            executor.prestartAllCoreThreads();
        }

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

            while (!flag) {
                Socket s = ss.accept();
                //logger.debug("客户端: " + s.getRemoteSocketAddress() + "联接了服务器");
				
				if (openPool) {
                    executor.submit(new TaskService(s));
                } else {
                    Thread t = new Thread(new TaskService(s));
                    t.start();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            //logger.error("服务器启动错误:" + e.getMessage());
        } finally {
            if (executor != null && openPool) {
                executor.shutdown();
            }
        }
    }

	/**
     * 解析xml文件
     * 解析方式:DOM(文档对象模型)、SAX(事件解析)
     */
    private Map<String, Map<String, String>> parseServerXml() {
        Map<String, Map<String, String>> map = new ConcurrentHashMap<>();
        //创建文档工厂对象
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            //构建器
            DocumentBuilder builder = factory.newDocumentBuilder();
            //文件路径
            String basePath = System.getProperty("user.dir");
            String serverXmlPath = basePath + File.separator + "conf" + File.separator + "server.xml";
            Document doc = builder.parse(serverXmlPath);
            NodeList nodeList = doc.getElementsByTagName("Connector");
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element element = (Element) nodeList.item(i);
                Map<String, String> sonMap = new ConcurrentHashMap<>();
                sonMap.put("port", element.getAttribute("port"));
                sonMap.put("executor", element.getAttribute("executor"));
                map.put("Connector", sonMap);
            }
        } catch (Exception e) {
            e.printStackTrace();
            //Logger.error("解析server.xml错误"+e.getMessage());
        }
        return map;
    }

    //线程工厂
    static class NameThreadFactory implements ThreadFactory {
        private final AtomicInteger threadId = new AtomicInteger(1);
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "线程--" + threadId.getAndIncrement());//相当于i++
            System.out.println(t.getName() + "已被创建");
            return t;
        }
    }

    //线程池拒绝策略
    public static class MyIgnorePolicy implements RejectedExecutionHandler {
        @Override                      //被拒绝的对象       线程池对象
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            doLog(r, executor);
        }
        private void doLog(Runnable runnable, ThreadPoolExecutor e) {
            System.out.println("线程池:" + e.toString() + runnable.toString() + "被拒绝执行");
        }
    }
}

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();
            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 {
                //看协议
                //如果是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;

/**
 * 请求处理
 */
public class HttpServletRequest {
    private InputStream in;
    private Socket socket;

    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();
        if (requestInfoString == null || "".equals(requestInfoString)) {
            throw new RuntimeException("读取输入流异常");
        }
        parseRequestInfoString(requestInfoString);
    }

	/**
     * 从输入流读取所有HTTP协议,拼接成字符串返回
     */
    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;
    }

    /**
     * 解析请求参数
     */
    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";
    }

    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.*;
/**
 * 读取数据,拼接响应,完成输出
 */
public class HttpServletResponse {
    private HttpServletRequest request;
    private OutputStream out;

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

        //读取数据 拼接响应 完成输出
        outResult();
    }

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

        //keaiwu/index.html
        String uri = this.request.getRequestURI();
        //项目的webapps位置
        String fileName = this.request.getRealPath() + uri;
        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 {
            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();
                }
            }
        }
    }
    
	/**
     * 读本地HTML文件,返回字节码数组
     */
    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();
    }

	/**
     * 生成404协议
     */
    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;
    }

	/**
     * 生成200协议
     */
    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 3.策略模式
        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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值