手写WebServer(简易版本)

手写WebServer(简易版本)

WebServer的核心

package cn.com.serverlet_end.core;
//对应着-
//    login
//    com.shsxt.LoginServlet
//    
public class Entity {
//    private String name;
//    private String clz;
//
//    public Entity() {
//    }
//
//    public String getName() {
//        return name;
//    }
//
//    public void setName(String name) {
//        this.name = name;
//    }
//
//    public String getClz() {
//        return clz;
//    }
//
//    public void setClz(String clz) {
//        this.clz = clz;
//    }
private String name;
    private String clz;
    public Entity() {

    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getClz() {
        return clz;
    }
    public void setClz(String clz) {
        this.clz = clz;
    }
}

package cn.com.serverlet_end.core;

import java.util.HashSet;
import java.util.Set;

// -
//    login
//    /login
//    /g
//    
public class Mapping {
//    private String name;
//    private Set patterns;
//
//    public Mapping() {
//        patterns = new HashSet<>();
//    }
//
//    public String getName() {
//        return name;
//    }
//
//    public void setName(String name) {
//        this.name = name;
//    }
//
//    public Set getPatterns() {
//        return patterns;
//    }
//
//    public void setPatterns(Set patterns) {
//        this.patterns = patterns;
//    }
//    public void addPattern(String pattern) {
//        this.patterns.add(pattern);
//    }

    private String name;
    private Set<String> patterns ;
    public Mapping() {
        patterns = new HashSet<String>();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Set<String> getPatterns() {
        return patterns;
    }
    public void setPatterns(Set<String> patterns) {
        this.patterns = patterns;
    }
    public void addPattern(String pattern) {
        this.patterns.add(pattern);
    }
}

package cn.com.serverlet_end.core;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.*;

//封装请求协议:封装请求参数为Map
public class Request {
    //协议信息
    private String requestInfo;
    //请求方式
    private String method;
    //请求的url
    private String url;
    //请求的参数
    private String par;
    //存储参数
    private Map<String,List<String>> pMap;
    private final String CRLF = "\n";
    //封装请求协议 获取method url 以及请求参数
    public Request(Socket client) throws IOException {
        this(client.getInputStream());
    }

    public Request(InputStream is) {
        pMap = new HashMap<>();
        byte[] datas = new byte[1024*1024*1024];
        int len ;
        try {
            len = is.read(datas);
            this.requestInfo = new String(datas,0,len);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        //分解字符串
        parseRequestInfo();
    }
    private void parseRequestInfo() {
        System.out.println("-------分解-------");
        System.out.println(requestInfo);
        System.out.println("---1,获取请求方式:开头到第一个/---");
        this.method = this.requestInfo.substring
                (0,this.requestInfo.indexOf("/")).toLowerCase();//转小写
        this.method = this.method.trim(); //去除前后空格
        System.out.println("---2,获取请求的url:第一个/到HTTP/");
        System.out.println("---可能包含请求参数?前面的为url---");
        //1 获取第一个/的位置
        int startIdx = this.requestInfo.indexOf("/")+1;
        //2 获取HTTP/的位置
        int endIdx = this.requestInfo.indexOf("HTTP/");
        //3 分割字符串
        this.url = this.requestInfo.substring(startIdx,endIdx).trim();
        //4 获取?的位置
        int queryidx = this.url.indexOf("?");
        if (queryidx >= 0) {//表示存在请求参数
            String[] urlArray = this.url.split("\\?");
            this.url = urlArray[0];
            par = urlArray[1];
        }
        System.out.println(this.url);
        System.out.println("---3,获取url后面的请求参数:如果是get,已经获取;如果是post,可能在请求体中---");

        if (method.equals("post")) {
            String qStr = this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim();
            if (par == null) {
                par= qStr;
            }else {
                par += "&"+qStr;
            }
        }
        par = par == null ? "": par;
        System.out.println(method+"-->"+url+"-->"+par);
        //转成map fav=1&fav=2&name=yuxiaohao&age=18&others=
        convertMap();
    }
    //处理请求参数为Map
    private void convertMap() {
        //1 分割字符串 按 & 分割
        String[] keyValues = this.par.split("&");
        for (String par1: keyValues) {
            //2 再次分割字符串 按 = 分割
            String[] kv = par1.split("=");
            kv = Arrays.copyOf(kv,2);
            //获取key和value
            String key = kv[0];
            String value = kv[1] == null ? null : decode(kv[1],"utf-8");
            //存储到map中
            if (!pMap.containsKey(key)) {//表示第一次
                pMap.put(key,new ArrayList<>());
            }
            pMap.get(key).add(value);
        }
    }
    //处理中文(post)
    private String decode(String value,String enc) {
        try {
            return java.net.URLDecoder.decode(value,enc);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    //通过(表单)name获取对应的多个值
    public String[] getParameterValues(String key) {
        List<String> list = this.pMap.get(key);
        if (list == null || list.size() < 1) {
            return null;
        }
        return list.toArray(new String[0]);
    }
    //通过name获取对应的一个值
    public String getParameter(String key) {
        String[] values =getParameterValues(key);
        return values == null ? null : values[0];
    }

    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    public String getPar() {
        return par;
    }



}

package cn.com.serverlet_end.core;

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

public class Response {
    private BufferedWriter bw;
    //正文
    private StringBuilder content;
    //协议头信息状态行 请求头 回车
    private StringBuilder headInfo;
    private int len;//正文的字节数
    private final String BLANK=" ";
    private final String CRLF="\r\n";
    private Response() {
        content = new StringBuilder();
        headInfo = new StringBuilder();
        len = 0;
    }
    public Response(Socket client) {
        this();
        try {
            bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        } catch (IOException e) {
            e.printStackTrace();
            headInfo = null;
        }
    }
    public Response(OutputStream os) {
        this();
        bw = new BufferedWriter(new OutputStreamWriter(os));
    }
    //动态添加内容
    public Response print(String info) {
        content.append(info);
        len+=info.getBytes().length;
        return this;
    }
    public Response println(String info) {
        content.append(info).append(CRLF);
        len+=(info+CRLF).getBytes().length;
        return this;
    }
    //推送响应信息
    public void pushToBrowser(int code) throws IOException {
        if (headInfo == null) {
            code = 505;
        }
        createHeadInfo(code);
        bw.append(headInfo);
        bw.append(content);
        bw.flush();
    }
    //构建头信息
    private void createHeadInfo(int code) {
        //1、响应行: HTTP/1.1 200 OK
        headInfo.append("HTTP/1.1").append(BLANK);
        headInfo.append(code).append(BLANK);
        switch(code) {
            case 200:
                headInfo.append("OK").append(CRLF);
            break;
            case 404:
                headInfo.append("NOT FOUND").append(CRLF);
                break;
            case 505:
                headInfo.append("SERVER ERROE").append(CRLF);
                break;
        }
        //2 响应头 最后一行有空行
        headInfo.append("Date:").append(new Date()).append(CRLF);
        headInfo.append("Server:shsxtServer/0.0.1;charset=GBK").append(CRLF);
        headInfo.append("Content-type:text/html").append(CRLF);
        headInfo.append("Content-length:").append(len).append(CRLF);
        headInfo.append(CRLF);
    }


}

package cn.com.serverlet_end.core;

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

public class Server {
    //目标处理404 505 和首页
    private ServerSocket serverSocket ;
    private boolean isRunning;
    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }
    //启动服务
    public void start() {
        try {
            serverSocket =  new ServerSocket(9999);
            isRunning = true;
            receive();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("服务器启动失败....");
            stop();
        }
    }
    //接受连接处理
    public void receive() {
        while(isRunning) {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一个客户端建立了连接....");
                //多线程的处理
                new Thread(new Dispatcher(client)).start();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客户端错误");
            }
        }
    }
    //停止服务
    public void stop() {
        isRunning = false;
        try {
            this.serverSocket.close();
            System.out.println("服务器已停止");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

package cn.com.serverlet_end.core;
//服务器小脚本接口
public interface Serverlet {
    void service(Request request, Response response);
}

package cn.com.serverlet_end.core;

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

public class WebApp {
    private static WebContext webContext;
    static {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //2 从解析工厂获取解析器
            SAXParser parse = factory.newSAXParser();
            //3 编写处理器

            //4 加载文档Document注册处理器
            WebHandler handler = new WebHandler();
            //5 解析
            parse.parse(Thread.currentThread().getContextClassLoader().
                    getResourceAsStream("web.xml"), handler);
            //获取数据
            webContext = new WebContext(handler.getEntitys(),handler.getMappings());
        }catch(Exception e) {
            System.out.println("解析配置文件错误");
        }
    }
    //通过url获取配置文件对应的Serverlet
    public static Serverlet getServerletFromUrl(String url) {
        String className = webContext.getClz("/"+url);
        Class clazz;
            try {
                System.out.println(url+"-->"+className+"-->");
                clazz = Class.forName(className);
                Serverlet serverlet = (Serverlet) clazz.getConstructor().newInstance();
                return serverlet;
            } catch (Exception e) {
                e.printStackTrace();
            }

        return null;
    }


}

package cn.com.serverlet_end.core;

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

public class WebContext {
    private List<Entity> entities = null;
    private List<Mapping> mappings = null;
    //key-->servelet-name  value-->servelet-class
    private Map<String,String> entityMap = new HashMap<>();
    //key-->url-pattern  value-->servelet-name;
    private Map<String,String> mappingMap = new HashMap<>();

    public WebContext(List<Entity> entities, List<Mapping> mappings) {
        this.entities = entities;
        this.mappings = mappings;
        //将entity的List转成了map
        for (Entity entity:entities) {
            entityMap.put(entity.getName(),entity.getClz());
        }
        //将map的List转成了对应的map
        for (Mapping mapping:mappings) {
            for (String pattern:mapping.getPatterns()) {
                mappingMap.put(pattern,mapping.getName());
            }
        }
    }
    public String getClz(String pattern) {
        String name = mappingMap.get(pattern);
        //通过URL的路径找到了对应的class
        return entityMap.get(name);
    }

}

package cn.com.serverlet_end.core;

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

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

//处理器
public class WebHandler extends DefaultHandler {
    private List<Entity> entitys  = new ArrayList<Entity>();
    private List<Mapping> mappings = new ArrayList<Mapping>();
    private Entity entity ;
    private Mapping mapping ;
    private String tag; //存储操作标签
    private boolean isMapping = false;


    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if(null!=qName) {
            tag = qName; //存储标签名
            if(tag.equals("servlet")) {
                entity = new Entity();
                isMapping = false;
            }else if(tag.equals("servlet-mapping")) {
                mapping = new Mapping();
                isMapping = true;
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String contents = new String(ch,start,length).trim();
        if(null!=tag) { //处理了空
            if(isMapping) { //操作servlet-mapping
                if(tag.equals("servlet-name")) {
                    mapping.setName(contents);
                }else if(tag.equals("url-pattern")) {
                    mapping.addPattern(contents);
                }
            }else { //操作servlet
                if(tag.equals("servlet-name")) {
                    entity.setName(contents);
                }else if(tag.equals("servlet-class")) {
                    entity.setClz(contents);
                }
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(null!=qName) {
            if(qName.equals("servlet")) {
                entitys.add(entity);
            }else if(qName.equals("servlet-mapping")) {
                mappings.add(mapping);
            }
        }
        tag = null; //tag丢弃了
    }

    public List<Entity> getEntitys() {
        return entitys;
    }

    public List<Mapping> getMappings() {
        return mappings;
    }



}

package cn.com.serverlet_end.core;

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

//分发器 :加入状态内容处理 404 505 以及首页
public class Dispatcher implements Runnable{
    private Socket client;
    private Request request;
    private Response response;
    public Dispatcher(Socket client) {
        this.client = client;
        try {
            //获取请求协议
            //获取响应协议
            request = new Request(client);
            response = new Response(client);
        } catch (IOException e) {
            e.printStackTrace();
            this.release();
        }
    }

    @Override
    public void run() {
        if (request.getUrl().equals("") || request.getUrl() == null) {

        }
        try {
            Serverlet serverlet = WebApp.getServerletFromUrl(request.getUrl());
            if (serverlet != null) {
                serverlet.service(request, response);
                //关注了状态码
                response.pushToBrowser(200);
            } else {
                //错误。。。
                response.pushToBrowser(404);
            }
        }catch(Exception e) {
            try {
                response.println("server is erroring");
                response.pushToBrowser(505);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        release();
    }
    //释放资源
    private void release() {
        try {
            client.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}

你可能感兴趣的:(手写WebServer(简易版本))