Java开发

手写服务器-1~一次往返

2020-02-29  本文已影响0人  风中小酌
  1. XML配置文件:用于存储servlet与http访问路径的对应关系
  2. server 服务器相关的类
    Request 请求
    Response 响应
    Server 服务器的启动和停止
    WebApp 初始化程序运行的数据,根据不同的url通过反射机制创建servlet对象
    WebDom4J 解析XML文件
  3. servlet类:用于保存接口类
  4. 工具类
创建XML配置文件

(建立名称与servlet类名,名称与http的url路径之间的映射关系)

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.test.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <servlet-pattern>/login</servlet-pattern>
        <servlet-pattern>/log</servlet-pattern>
    </servlet-mapping>
</web-app>
创建Request请求对象
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//请求
public class Request {
    private String requestInfo; //请求信息
    private String method; //请求方法
    private String url; //URL
    //存储表单数据
    Map<String, List<String>> formvalues;
    //换行
    private final static String CRLF = "\r\n";
    //构造方法
    public Request(){
        formvalues = new HashMap<String, List<String>>();
        method = "";
        url = "";
        requestInfo = "";
    }
    //带参构造
    public Request(InputStream is ){
        this();
        byte[] buf = new byte[20480];
        int len = 0;
        try {
            len = is.read(buf);
            this.requestInfo = new String(buf, 0, len);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //解析请求
        this.parseRequest(requestInfo);
    }
    
    public void parseRequest(String reqinfo){
        //存储请求参数
        String reqParameters = "";
        //解析收到请求的第一行
        String firstLine = reqinfo.substring(0, reqinfo.indexOf(CRLF));
        int index = firstLine.indexOf("/");
        //分析出请求方式
        method = firstLine.substring(0, index).trim();
        //分解URL,可能包含参数,也可能不含参数
        String urlString = firstLine.substring(index, firstLine.indexOf("HTTP/")).trim();
        if("get".equalsIgnoreCase(method)){
            //如果URL中包含?,后边即为请求参数
            if(urlString.contains("?")){
                //需要对问号进行转义
                String[] urlarr = urlString.split("\\?");
                this.url = urlarr[0];
                reqParameters = urlarr[1];
            }else{
                this.url = urlString;
            }
        }else{//请求为POST
            this.url = urlString;
            reqParameters = reqinfo.substring(reqinfo.lastIndexOf(CRLF)).trim();
        }
        
        if(reqParameters.equals("")){
            return;
        }
        //解析请求中的各个参数
        this.parseParam(reqParameters);
    }
    
    public void parseParam(String reqparam){
        //分离不同的参数
        String[] token = reqparam.split("&");
        //分离参数及其对应的数值
        for(String item : token){
            //获取键和值
            String[] arritem = item.split("=");
            
            //判断键对应的数据是否存在
            if(arritem.length == 1){
                arritem = Arrays.copyOf(arritem, 2);
                //不存在时,将值设置为NULL
                arritem[1] = null;
            }
            
            String key = arritem[0];
            String value = arritem[1] == null ? null : decode(arritem[1], "UTF-8");
            //判断键在MAP中是否存在
            if(!formvalues.containsKey(key)){
                //不存在时,对数值的容器进行初始化
                formvalues.put(key, new ArrayList<String>());
            }
            
            List<String> values = formvalues.get(key);
            values.add(value);
            formvalues.put(key, values);
        }       
    }
    
    public String getRequestInfo() {
        return requestInfo;
    }
    public void setRequestInfo(String requestInfo) {
        this.requestInfo = requestInfo;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public Map<String, List<String>> getFormvalues() {
        return formvalues;
    }
    public void setFormvalues(Map<String, List<String>> formvalues) {
        this.formvalues = formvalues;
    }
    
    /**
     * 根据键获取表单中对应的参数值列表
     */
    public String[] getParameters(String name){
        List<String> values = formvalues.get(name);
        if(values == null){
            return null;
        }else{
            return values.toArray(new String[0]);
        }
    }
    
    //根据键获取参数名,获取参数
    public String getParameter(String name){
        String[] values = this.getParameters(name);
        if(values == null){
            return null;
        }else{
            return values[0];
        }
    }
    
    /**
     * 对请求中的数据进行解码
     * @param value
     * @param code
     * @return
     */
    public String decode(String value, String code){
        try {
            return URLDecoder.decode(value, code);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
创建Reponse响应对象
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

import com.test.util.IOCloseUtil;

//响应
public class Response {
    //响应头
    private StringBuffer header;
    //响应内容
    private StringBuffer content;
    //响应内容的长度
    private int length = 0;
    //流
    private BufferedWriter bw;
    //字符串常量
    private static String CRLF = "\r\n";
    private static String BLANK = " ";
    
    public Response(){
        this.header = new StringBuffer();
        this.content = new StringBuffer();
    }
    
    public Response(OutputStream os) {
        this();
        try {
            bw = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
        } catch (UnsupportedEncodingException e) {
            header = null;
        }
    }
    
    //构造单行文本
    public Response print(String info){
        content.append(info);
        try {
            length += info.getBytes("utf-8").length;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return this;
        
    }
    
    //构造多行文本
    public Response println(String info){
        content.append(info).append(CRLF);
        
        try {
            length += (info + CRLF).getBytes("UTF-8").length;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        
        return this;
    }
    
    //构造响应头
    public void createHeaderInfo(int code){
        header.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
        switch(code){
        case 200:
            header.append("OK");
            break;
        case 500:
            header.append("SERVER ERROR");
            break;
        default:
            header.append("NOT FOUND");
        }
        
        header.append(CRLF  );
        header.append("Content-Type:text/html;charset=utf-8 ").append(CRLF);
        header.append("Content-Length:" + length).append(CRLF);
        header.append(CRLF);
    }
    
    //推送数据到客户端
    public void push2Client(int code){
        if(header == null){
            code = 500;
        }
        
        //调用构造响应头
        this.createHeaderInfo(code);
        try {
            bw.write(header.toString());
            bw.write(content.toString());
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        close();
    }
    
    public void close(){
        IOCloseUtil.closeAll(bw);
    }
}
创建Server启动停止服务器
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.test.util.IOCloseUtil;

/**
 * 服务的启动与停止
 * @author Administrator
 *
 */
public class Server {
    private ServerSocket server;
    private boolean isShutdown = false;
    
    public static void main(String[] args) {
        Server ss = new Server();
        ss.start(8888);
    }
    
    //开启服务
    public void start(int port){
        try {
            this.server = new ServerSocket(port);
            this.receive();
        } catch (IOException e) {
            isShutdown = true;
        }
    }
    
    //接收请求
    public void receive(){
        while(!isShutdown){
            try {
                Socket client = server.accept();
            } catch (IOException e) {
                shutdown();
            }
        }
    }
    
    public void shutdown(){
        isShutdown = true;
        IOCloseUtil.closeAll(server);
    }
}
创建servlet名称与servlet类映射关系类
//存储servlet-name 与 servlet-class 映射关系
public class Entity {
    private String name;
    private String clazz;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getClazz() {
        return clazz;
    }
    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
    public Entity(String name, String clazz) {
        this.name = name;
        this.clazz = clazz;
    }
    @Override
    public String toString() {
        return "Entity [name=" + name + ", clazz=" + clazz + "]";
    }
    public Entity() {
    }
}
创建servlet名称与HTTP请求的URL之间映射关系类
import java.util.ArrayList;
import java.util.List;

//存储servlet-name servlet-mapping映射关系
public class Mapping {
    private String name;
    private List<String> pattern;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<String> getPattern() {
        return pattern;
    }
    public void setPattern(List<String> pattern) {
        this.pattern = pattern;
    }
    public Mapping(String name, List<String> pattern) {
        super();
        this.name = name;
        this.pattern = pattern;
    }
    public Mapping() {
        pattern = new ArrayList<String>();
    }
    @Override
    public String toString() {
        return "Mapping [name=" + name + ", pattern=" + pattern + "]";
    }
}
用于存储XML中servlet与servlet-mapping之间映射关系
import java.util.HashMap;
import java.util.Map;

/**
 * 用于存储servlet 与 mapping 的映射关系
 * @author Administrator
 *
 */
public class ServletContext {
    private Map<String, String> servlet; //servlet-name, servlet-class, 一对一
    private Map<String, String> mapping; //servlet-pattern, servlet-name, 多对一
    public Map<String, String> getServlet() {
        return servlet;
    }
    public void setServlet(Map<String, String> servlet) {
        this.servlet = servlet;
    }
    public Map<String, String> getMapping() {
        return mapping;
    }
    public void setMapping(Map<String, String> mapping) {
        this.mapping = mapping;
    }
    public ServletContext(Map<String, String> servlet,
            Map<String, String> mapping) {
        super();
        this.servlet = servlet;
        this.mapping = mapping;
    }
    public ServletContext() {
        servlet = new HashMap<String, String>();
        mapping = new HashMap<String, String>();
    }
}
解析XML文件的类
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

//读取服务器XML配置
public class WebDom4J {
    private List<Entity> entity;
    private List<Mapping> mapping;
    public List<Entity> getEntity() {
        return entity;
    }
    public void setEntity(List<Entity> entity) {
        this.entity = entity;
    }
    public List<Mapping> getMapping() {
        return mapping;
    }
    public void setMapping(List<Mapping> mapping) {
        this.mapping = mapping;
    }
    
    public WebDom4J(List<Entity> entity, List<Mapping> mapping) {
        super();
        this.entity = entity;
        this.mapping = mapping;
    }
    public WebDom4J() {
        entity = new ArrayList<Entity>();
        mapping = new ArrayList<Mapping>();
    }
    
    //获取Document对象
    public Document getDocument(){
        //创建SAVReader对象
        SAXReader sr = new SAXReader();
        Document doc = null;
        try {
            doc = sr.read(new FileInputStream("src/WEB_INFO/web.xml"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return doc;
    }
    
    //解析XML
    public void parseXML(Document doc){
        //获取XML文件顶级节点
        Element root = doc.getRootElement();
        for(Iterator<Element> eleite = root.elementIterator("servlet"); eleite.hasNext();){
            Element ele = eleite.next();
            Entity ent = new Entity();
            for(Iterator<Element> subite = ele.elementIterator();subite.hasNext();){
                Element subele = subite.next();
                String name = subele.getName();
                if(name.equals("servlet-name")){
                    ent.setName(subele.getText());
                }else{
                    ent.setClazz(subele.getText());
                }
            }
            
            entity.add(ent);
        }
        
        for(Iterator<Element> eleite = root.elementIterator("servlet-mapping");eleite.hasNext();){
            Element ele = eleite.next();
            Mapping map = new Mapping();
            for(Iterator<Element> subite = ele.elementIterator();subite.hasNext();){
                Element subele = subite.next();
                String name = subele.getName();
                if(name.equals("servlet-name")){
                    map.setName(subele.getText());
                }else{
                    map.getPattern().add(subele.getText());
                }
            }
            
            mapping.add(map);
        }
    }
}
创建WebApp类,用于初始化程序运行的数据
import java.util.List;
import java.util.Map;

import com.test.servlet.Servlet;

// 初始化程序运行的数据,根据不同的url创建所请求的servlet对象
public class WebApp {
    private static ServletContext context;
    
    static{
        context = new ServletContext();
        //分别获取对应关系的Map集合
        Map<String, String> servletEntity = context.getServlet();
        Map<String, String> mapping = context.getMapping();
        //读取XML
        WebDom4J web = new WebDom4J();
        web.parseXML(web.getDocument());
        //获取解析XML后得到的集合
        List<Entity> entlist = web.getEntity();
        List<Mapping> maplist = web.getMapping();
        
        //将集合中的数据存储到Map
        for(Entity ent : entlist){
            servletEntity.put(ent.getName(), ent.getClazz());
        }
        
        for(Mapping m : maplist){
            List<String> urlPattern = m.getPattern();
            for(String url : urlPattern){
                mapping.put(url, m.getName());
            }
        }
    }
    
    /**
     * 根据URL创建不同的Servlet对象
     */
    public static Servlet getServlet(String url){
        //判断URL是否为空
        if(url == null || url.trim().equals("")){
            return null;
        }

        //根据URL获取对应的Servlet-name
        String servletName = context.getMapping().get(url);
        System.out.println(servletName);
        //根据servlet-name获取对应的Servlet-class
        String clazz = context.getServlet().get(servletName);

        try {
            //通过反射获取对象
            Class<?> servCls = Class.forName(clazz);
            //根据无参构造方法,创建Servlet对象
            Servlet servlet = (Servlet) servCls.newInstance();
            return servlet;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        
        return null;
    }
    
    public static void main(String[] args) {
        System.out.println(WebApp.getServlet("/log"));
        System.out.println(WebApp.getServlet("/login"));
    }
}
创建Servlet父类
import com.test.server.Request;
import com.test.server.Response;

/**
 * 所有请求Servlet的父类
 * @author Administrator
 *
 */
public abstract class Servlet {
    public void Service(Request req, Response res) throws Exception{
        doGet(req, res);
        doPost(req, res);
    }
    
    public abstract void doGet(Request req, Response res) throws Exception;
    
    public abstract void doPost(Request req, Response res) throws Exception;
    
}
创建用于处理登录请求的LoginServlet类
import com.test.server.Request;
import com.test.server.Response;

public class LoginServlet extends Servlet {

    @Override
    public void doGet(Request req, Response res) throws Exception {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        
        if(login(username, password)){
            res.println("登录成功, 欢迎你: " + username);
        }else{
            res.println("抱歉, 用户: " + username + "登录失败!");
        }
    }

    @Override
    public void doPost(Request req, Response res) throws Exception {
        // TODO Auto-generated method stub
        
    }
    
    private boolean login(String username, String pwd){
        if(username.equals("test") && pwd.equals("123456")){
            return true;
        }else{
            return false;
        }
    }
}
工具类,用于关闭对象流
import java.io.Closeable;
import java.io.IOException;

public class IOCloseUtil {
    /**
     * 关闭对象流
     * @param ables
     */
    public static void closeAll(Closeable...ables){
        for(Closeable c : ables){
            try {
                c.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读