Rhyme/ 手写服务器 实现一个基于xml解析的简单的Tomcat服务器

手写服务器 实现一个基于xml解析的简单的Tomcat服务器

效果图:

实现了基于xml配置的servlet动态匹配

这里写图片描述

这里写图片描述

源代码:

Server.java 服务器启动类

package com.maple.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.maple.util.IOUtil;

/**
 * Server
 * 
 * 1.request
 * 2.response
 * 
 * @author RhymeChiang
 * @date 2017/10/23
 */
/**
 * @author RhymeChiang
 * @date 2017/10/23
 */
public class Server {
    private ServerSocket server = null;
    private boolean isShutDown = false;


    public void start() {
        start(8888);
    }

    public void start(int port) {
        try {
            server = new ServerSocket(port);
            this.receive();
        } catch (IOException e) {
            // e.printStackTrace();
            stop();
        }
    }

    private void receive() {
        try {
            while(!isShutDown) {
                Socket client = server.accept();
                new Thread(new Dispatcher(client)).start();
            }
        } catch (IOException e) {
            //e.printStackTrace();
            stop();
        }
    }

    public void stop() {
        isShutDown = true;
        IOUtil.closeAll(server);
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

}

Request.java 处理解析浏览器的URL请求

package com.maple.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * deal with client request
 * @author RhymeChiang
 * @date 2017/10/23
 */
public class Request {
    /*      
     *      ===============Request================
     *      GET /index.jsp?name=Rhyme&pwd=123 HTTP/1.1
            Host: localhost:8888
            Upgrade-Insecure-Requests: 1
            User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
            Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*;q=0.8
            Accept-Encoding: gzip, deflate, br
            Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
            Connection: close

            ==============Response=================
            POST /index.jsp HTTP/1.1
            Host: localhost:8888
            Content-Length: 18
            Cache-Control: max-age=0
            Upgrade-Insecure-Requests: 1
            Origin: null
            Content-Type: application/x-www-form-urlencoded
            User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
            Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*;q=0.8
            Accept-Encoding: gzip, deflate, br
            Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
            Connection: close

            name=Rhyme&pwd=123
    */

    private String requestMethod;
    private String requestURI;
    private String requestParams;
    private final String CRLF = "\r\n";
    private final String BLANK = " ";
    private Map<String,List<String>>parmsMap = null;
    public Request(Socket client) {
        requestMethod = "";
        requestURI = "";
        requestParams = "";
        parmsMap  = new HashMap<>();
        try {
            this.parseRequest(client);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void parseRequest(Socket client) throws IOException {
        InputStream in = client.getInputStream();
        byte b [] = new byte[20480];
        int len = in.read(b);
        String requestInfo = new String(b,0,len);
        requestMethod  =requestInfo.substring(0,requestInfo.indexOf(BLANK));
        requestURI  =requestInfo.substring(requestInfo.indexOf("/"),requestInfo.indexOf("HTTP/1.1")).trim();
        if(requestURI.contains("?")) {
            requestParams = requestURI.substring(requestURI.indexOf("?")+1);
            requestURI = requestURI.substring(0,requestURI.indexOf("?"));
        }
        if(requestMethod.equalsIgnoreCase("post")) {
            requestParams = requestInfo.substring(requestInfo.lastIndexOf(CRLF)+2);
        }
        parseParams(requestParams);
    }

    private void parseParams(String requestParams) {
        StringTokenizer tokens  =  new StringTokenizer(requestParams, "&");
        while(tokens.hasMoreTokens()) {
            String token = tokens.nextToken();
            String[] split = token.split("=");
            String key = split[0];
            String values =  split.length>1?split[1]:null;
            if(!parmsMap.containsKey(key)) {
                List<String>list = new ArrayList<>();
                list.add(values);
                parmsMap.put(key, list);
            }else {
                parmsMap.get(key).add(values);
            }
        }
    }

    public String getParameter(String key) {
        if(parmsMap.containsKey(key)) {
            return parmsMap.get(key).get(0);
        }
        return null;
    }

    public String getParameters(String key) {
        if(parmsMap.containsKey(key)) {
            return parmsMap.get(key).toString();
        }
        return null;
    }

    public String getRequestMethod() {
        return requestMethod;
    }

    public void setRequestMethod(String requestMethod) {
        this.requestMethod = requestMethod;
    }

    public String getRequestURI() {
        return requestURI;
    }

    public void setRequestURI(String requestURI) {
        this.requestURI = requestURI;
    }

    public String getRequestParams() {
        return requestParams;
    }

    public void setRequestParams(String requestParams) {
        this.requestParams = requestParams;
    }

    public Map<String, List<String>> getParmsMap() {
        return parmsMap;
    }

    public void setParmsMap(Map<String, List<String>> parmsMap) {
        this.parmsMap = parmsMap;
    }


}

Response.java 返回响应给用户的数据

package com.maple.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;

import com.maple.util.IOUtil;

/**
 * package response
 * @author RhymeChiang
 * @date 2017/10/23
 */
public class Response {

    private final String  BLANK = " ";
    private final String CRLF = "\r\n";
    private StringBuilder context = new StringBuilder();
    /**
     * create the header of response
     * @param code
     *              status code
     *              200 OK
     *              404 NOT FOUND
     *              500 SERVER ERROR
     * @param len
     *              length of context
     * @return
     */
    private StringBuilder createHeadInfo(int code,int len) {
        StringBuilder headInfo = new StringBuilder();
        headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
        switch (code) {
        case 200:
            headInfo.append("OK");
            break;
        case 404:
            headInfo.append("NOT FOUND");
            break;
        case 500:
            headInfo.append("SERVER ERROR");
            break;
        default:
            break;
        }
        headInfo.append(CRLF);
        headInfo.append("Server:Rhyme").append(BLANK).append("Rhyme/1.0.0").append(CRLF);
        headInfo.append("Date:").append(new Date()).append(BLANK).append("GMT").append(CRLF);
        headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF);
        headInfo.append("Content-Length:").append(len).append(CRLF);
        headInfo.append(CRLF);
        return headInfo;
    }

    /**
     * create the context of response
     * @param msg
     *          the context of response 
     * @return
     *          Response
     */
    public Response println(String msg) {
        context.append(msg).append(CRLF);
        return this;
    }
    /**
     * push repsonse to client
     * @param code
     */
    public void pushToClient(int code,Socket client) {
        StringBuilder response = new StringBuilder();
        int len = context.toString().getBytes().length;
        response.append(createHeadInfo(code, len));
        response.append(CRLF);
        response.append(context.toString());
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
            bw.write(response.toString());
            bw.flush();
        } catch (IOException e) {
            //e.printStackTrace();
            IOUtil.closeAll(bw);
        }

    }

}

Dispatcher.java

package com.maple.server;

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

import com.maple.servlet.Servlet;

/**
 * Servlet Dispatcher
 * @author RhymeChiang
 * @date 2017/10/23
 */
public class Dispatcher implements Runnable {

    private Request request;
    private Response response;
    private int code = 200;
    private Socket client;

    public Dispatcher(Socket client) {
        this.client = client;
        request = new Request(client);
        response = new Response();
    }

    @Override
    public void run() {
        Servlet servlet = WebApp.getServlet(request.getRequestURI());
        if (servlet == null) {
            response.pushToClient(404, client);
            return;
        }else {
            try {
                servlet.service(request, response);
                response.pushToClient(code, client);
            } catch (IOException e) {
                // e.printStackTrace();
                response.pushToClient(500, client);
            }
            if (null != client) {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Webapp.java

package com.maple.server;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

import com.maple.servlet.Servlet;
import com.maple.servlet.ServletContext;
import com.maple.servlet.ServletEntity;
import com.maple.servlet.ServletMappingEntity;

/**
 * xml parse and prepare resourses
 * @author RhymeChiang
 * @date 2017/10/24
 */
public class WebApp {

    public static ServletContext context;

    static {
        context = new ServletContext();
        try {
            init();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void init() throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        WebXmlHandler web = new WebXmlHandler();
        parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("web.xml"), web);
        List<ServletEntity> l1 = web.getServletList();
        List<ServletMappingEntity> l2 = web.getMappingList();

        Map<String, String> servlet = context.getServlet();
        Map<String, String> mapping = context.getServletMapping();
        for(ServletEntity s:l1) {
            servlet.put(s.getServletName(), s.getServletClass());
        }
        for(ServletMappingEntity s:l2) {
            mapping.put(s.getUrlPatterns(), s.getServletName());
        }
    }

    public static Servlet getServlet(String requestURI) {
        if (null == requestURI || requestURI.trim().equals("")) {
            return null;
        }
        String url = context.getServletMapping().get(requestURI);
        if (url == null) {
            return null;
        }

        String reflect = context.getServlet().get(url);

        Servlet s = null;
        try {
            Class<?> clazz = Class.forName(reflect);
            s = (Servlet) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return s;
    }

}

package com.maple.server;

import java.util.ArrayList;
import java.util.List;


import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.maple.servlet.ServletEntity;
import com.maple.servlet.ServletMappingEntity;

/**
 * @author RhymeChiang
 * @date 2017/10/24
 */
public class WebXmlHandler extends DefaultHandler {

    private List<ServletEntity> servletList;
    private List<ServletMappingEntity> mappingList;

    private ServletEntity servlet = null;
    private ServletMappingEntity mapping = null;
    private boolean isMapping = false;
    private String beginTag;

    public WebXmlHandler() {
        servletList = new ArrayList<>();
        mappingList = new ArrayList<>();
    }

    @Override
    public void startDocument() throws SAXException {

    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (qName != null) {
            beginTag = qName;
            if (qName.equals("servlet")) {
                servlet = new ServletEntity();
                isMapping = false;
            } else if (qName.equals("servlet-mapping")) {
                mapping = new ServletMappingEntity();
                isMapping = true;
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String content = new String(ch, start, length);
        if (!isMapping) {
            if (beginTag.equals("servlet-name")) {
                servlet.setServletName(content);
            } else if (beginTag.equals("servlet-class")) {
                servlet.setServletClass(content);
                servletList.add(servlet);
            }
        } else if(isMapping){
            if (beginTag.equals("servlet-name")) {
                mapping.setServletName(content);
            } else if (beginTag.equals("url-parttens")) {
                mapping.setUrlPatterns(content);
                mappingList.add(mapping);
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        beginTag = "";
    }

    @Override
    public void endDocument() throws SAXException {
    }

    public List<ServletEntity> getServletList() {
        return servletList;
    }

    public void setServletList(List<ServletEntity> servletList) {
        this.servletList = servletList;
    }

    public List<ServletMappingEntity> getMappingList() {
        return mappingList;
    }

    public void setMappingList(List<ServletMappingEntity> mappingList) {
        this.mappingList = mappingList;
    }
}

package com.maple.servlet;

import java.io.IOException;

import com.maple.server.Request;
import com.maple.server.Response;

/**
 * @author RhymeChiang
 * @date 2017/10/23
 */
public abstract class Servlet {

    public void service(Request request, Response response) throws IOException {
        this.doGet(request, response);
        this.doPost(request, response);
    }

    public abstract void doGet(Request request, Response response) throws IOException;

    public abstract void doPost(Request request, Response response) throws IOException;


}

package com.maple.servlet;

import java.io.IOException;

import com.maple.server.Request;
import com.maple.server.Response;

/**
 * @author RhymeChiang
 * @date 2017/10/23
 */
public class LoginServlet extends Servlet {

    @Override
    public void doGet(Request request, Response response) throws IOException {

    }

    @Override
    public void doPost(Request request, Response response) throws IOException {
        System.out.println("doPost");
        response.println("<html>");
        response.println("<head>");
        response.println("</head>");
        response.println("<body>");
        response.println("<h1>");
        response.println("Hello,I'm " + request.getParameter("name"));
        response.println("My pwd is " + request.getParameter("pwd"));
        response.println("My Hobby is " + request.getParameters("hobby"));
        response.println("</h1>");
        response.println("</body>");
        response.println("<html>");
    }
}

package com.maple.servlet;

import java.util.HashMap;
import java.util.Map;

/**
 * @author RhymeChiang
 * @date 2017/10/24
 */
public class ServletContext {

    private Map<String, String> servlet;
    private Map<String, String> servletMapping;

    public ServletContext() {
        super();
        servlet = new HashMap<>();
        servletMapping = new HashMap<>();
    }

    public Map<String, String> getServlet() {
        return servlet;
    }

    public void setServlet(Map<String, String> servlet) {
        this.servlet = servlet;
    }

    public Map<String, String> getServletMapping() {
        return servletMapping;
    }

    public void setServletMapping(Map<String, String> servletMapping) {
        this.servletMapping = servletMapping;
    }



}

package com.maple.servlet;

/**
 * @author RhymeChiang
 * @date 2017/10/25
 */
public class ServletEntity {

    private String servletName;

    private String servletClass;

    public ServletEntity() {
    }

    public ServletEntity(String servletName, String servletClass) {
        this.servletName = servletName;
        this.servletClass = servletClass;
    }

    public String getServletName() {
        return servletName;
    }

    public void setServletName(String servletName) {
        this.servletName = servletName;
    }

    public String getServletClass() {
        return servletClass;
    }

    public void setServletClass(String servletClass) {
        this.servletClass = servletClass;
    }

}

package com.maple.servlet;

/**
 * @author RhymeChiang
 * @date 2017/10/25
 */
public class ServletMappingEntity {
    private String urlPatterns;

    private String servletName;

    public ServletMappingEntity() {
    }

    public ServletMappingEntity(String urlPatterns, String servletName) {
        this.urlPatterns = urlPatterns;
        this.servletName = servletName;
    }

    public String getUrlPatterns() {
        return urlPatterns;
    }

    public void setUrlPatterns(String urlPatterns) {
        this.urlPatterns = urlPatterns;
    }

    public String getServletName() {
        return servletName;
    }

    public void setServletName(String servletName) {
        this.servletName = servletName;
    }

}

package com.maple.util;

import java.io.Closeable;
import java.io.IOException;

/**
 * @author RhymeChiang
 * @date 2017/10/23
 */
public class IOUtil {
    public static void closeAll(Closeable...io) {
        for(Closeable i :io) {
            if(null!=i) {
                try {
                    i.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.maple.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-parttens>/login</url-parttens>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-parttens>/log</url-parttens>
    </servlet-mapping>
</web-app>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值