美文网首页Java开发
手写服务器-1~一次往返

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

作者: 风中小酌 | 来源:发表于2020-02-29 17:01 被阅读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();
                }
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:手写服务器-1~一次往返

        本文链接:https://www.haomeiwen.com/subject/ngdmfhtx.html