zoukankan      html  css  js  c++  java
  • 1000行代码手写服务器

    1000行代码手写服务器

    开发技术&开发环境和工具

    使用技术

    基于Java IO,多线程,Socket网络编程,XML解析,只引入Junit,dom4j(解析xml),熟悉javaweb基本应用。

    环境参数

    • Java环境 JDK1.8 maven maven3.6
    • 开发工具 IDEA

    环境搭建

    pom.xml文件配置

    只需在xml导入dom4j jar包

    <dependencies>
            <dependency>
                <groupId>org.dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>2.1.1</version>
            </dependency>
        </dependencies>
    

    手写服务器 整体架构,编写XML文件

    配置解析XML文件

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app >
        <servlet>
            <servlet-name>login</servlet-name>
            <servlet-class>com.feng.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/login</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/log</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    POJO类

    package com.feng.server;
    
    public class Entity {  //servlet-name或每一个servlet-name所对应得实体类
        private String name;
        private String clazz;
    
        public Entity() {
        }
    
        public Entity(String name, String clazz) {
            this.name = name;
            this.clazz = 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;
        }
    }
    

    Mapping类

    package com.feng.server;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Mapping { //映射关系,多个路径可以访问资源
        private String name; //servlet-name
        private List<String> urlPattern;//url-pattern
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public List<String> getUrlPattern() {
            return urlPattern;
        }
    
        public void setUrlPattern(List<String> urlPattern) {
            this.urlPattern = urlPattern;
        }
    
        public Mapping() {
            this.urlPattern = new ArrayList<String>();
        }
    
        public Mapping(String name, List<String> urlPattern) {
            this.name = name;
            this.urlPattern = urlPattern;
        }
    }
    

    配置dom4j

    package com.feng.server;
    
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class WebDom4j { //用于解析xml
        private List<Entity> entityList; //用于储存实体类,每一个实体类一个servlet-name对应一个servlet-class
        private List<Mapping> mappingList;//用于储存映射类,每一个servlet-name对应一个url-pattern
    public List<Entity> getEntityList() {
        return entityList;
    }
    
    public void setEntityList(List<Entity> entityList) {
        this.entityList = entityList;
    }
    
    public List<Mapping> getMappingList() {
        return mappingList;
    }
    
    public void setMappingList(List<Mapping> mappingList) {
        this.mappingList = mappingList;
    }
    
    //构造方法
    public WebDom4j() {
        entityList = new ArrayList<Entity>();
        mappingList = new ArrayList<Mapping>();
    }
    
    //获取Document对象的方法
    public Document getDocument(){
    
        try {
            //创建SAXReader对象
            SAXReader reader = new SAXReader();
            //调用read方法
            return reader.read(new File("web/WEB-INF/web.xml"));
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }
    //获取元素
    public void parse(Document doc) {
        //1获取根元素
        Element root = doc.getRootElement(); //web-app
        //2获取servlet子元素
        for (Iterator<Element> ite = root.elementIterator("servlet"); ite.hasNext();) {
            Element subElement = ite.next(); //得到每一个servlet
            //创建一个实体类
            Entity ent = new Entity(); //用于储存servlet-name与servlet-class
            for (Iterator<Element> subIte = subElement.elementIterator(); subIte.hasNext();) {
                Element ele = subIte.next(); //servlet-name与servlet-class都有可能
                if ("servlet-name".equals(ele.getName())) {
                    ent.setName(ele.getText());
                } else if ("servlet-class".equals(ele.getName())) {
                    ent.setClazz(ele.getText());
                }
            }
            entityList.add(ent);//放到集合中
        }
        //测试
       /* for (Entity entity : entityList) {
            System.out.println(entity.getName() + "	" + entity.getClazz());
        }*/
    
        //解析servlet-mapping
    
        for (Iterator<Element> ite = root.elementIterator("servlet-mapping"); ite.hasNext();){
            Element subEle = ite.next();//得到每一个servlet-mapping
            //创建一个mapping对象
            Mapping map = new Mapping();
            //解析mapping下面每一个子元素
            for (Iterator<Element> subIte = subEle.elementIterator(); subIte.hasNext();){
                Element ele = subIte.next();//可能是name,也可能是url
                if ("servlet-name".equals(ele.getName())) {
                    map.setName(ele.getText());
                } else if ("url-pattern".equals(ele.getName())) {
                    //获取集合对象,调用集合对象的添加方法,添加元素
                    map.getUrlPattern().add(ele.getText());
                }
            }
            mappingList.add(map);
    
        }
        //测试
        /*for (Mapping m : mappingList) {
            System.out.println(m.getName());
            for (String s : m.getUrlPattern()) {
                System.out.println(s);
            }
        }*/
    }
    
    //用于测试
        public static void main(String[] args) {
            WebDom4j web = new WebDom4j();
            web.parse(web.getDocument());
        }
    }
    

    servlet和servlet-mapping解析结果

    更新编写XML

    先更新编写才得出上面的结果

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app>
        <servlet>
            <servlet-name>login</servlet-name>
            <servlet-class>com.feng.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/log</url-pattern>
            <url-pattern>/login</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>register</servlet-name>
            <servlet-class>com.feng.servlet.RegisterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>register</servlet-name>
            <url-pattern>/reg</url-pattern>
            <url-pattern>/regis</url-pattern>
            <url-pattern>/register</url-pattern>
        </servlet-mapping>
       
    </web-app>
    

    编写servletContext Servlet 上下文,容器用于存储映射关系

    package com.feng.server;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
    *ServletContext 上下文 就是一个容器
    
    */
    public class ServletContext { //Entity与Mapping之间的映射关系
         private Map<String, String> servlet;//key是servlet-name(实体类中的name),值servlet-class,实体类中的值
         private Map<String, String> mapping;//key是url-pattern(Map中的每一个元素),值servlet-name,实体类中的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(){
         servlet = new HashMap<String, String>();
         mapping = new HashMap<String, String>();
     }
    }
    

    编写WebAPP 初始化程序数据

    package com.feng.server;
    
    import com.feng.servlet.Servlet;
    
    import java.util.List;
    import java.util.Map;
    
    public class WebApp {  //应用程序
        private static ServletContext context;
        static {
            context = new ServletContext();
            //分别获取对应关系的Map集合
            Map<String, String> servlet = context.getServlet();
            Map<String, String> mapping = context.getMapping();
            //创建解析XML文件对象
            WebDom4j web = new WebDom4j();
            //解析xml
            web.parse(web.getDocument());
            //获取解析xml之后的List集合
            List<Entity> entityList = web.getEntityList();
            List<Mapping> mappingList = web.getMappingList();
    
            //将List集合储存到map集合中
            for (Entity entity : entityList) {
                servlet.put(entity.getName(),entity.getClazz());
            }
            System.out.println(servlet);
            for (Mapping map : mappingList) {
                //遍历url-pattern的集合
                List<String> urlPattern = map.getUrlPattern();
                for (String s : urlPattern) {
                    mapping.put(s,map.getName());
                }
            }
            System.out.println(mapping);
        }
    
       
        public static void main(String[] args) {
           WebDom4j web = new WebDom4j();
            web.parse(web.getDocument());
        }
    }
    
    

    获取解析xml集合的结果

    创建不同的Servlet对象

    /*
         * 根据url创建不同的Servlet对象
         * */
        public static Servlet getServlet(String url){
            if (url ==null||url.trim().equals("")){
                return null;
            }
            try {
                //如果url正确
                //根据url的key获取servlet-name的值
                String servletName = context.getMapping().get(url);
                //根据servlet-name找到对应得servlet-class
                String servletClass = context.getServlet().get(servletName);
                //得到的是一个完整的包+类名的字符串
    
                //使用反射创建servlet对象
                Class<?> clazz = Class.forName(servletClass);
                Servlet servlet = (Servlet) clazz.newInstance();
                return servlet;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    在WebApp类调用getServlet方法得到如下结果:

    编写服务(启动或关闭服务)

    public class Server { //服务器,用于启动或停止服务
        private ServerSocket server;
    
        public static void main(String[] args) {
            Server server = new Server();//创建服务器
            server.start();
        }
    
        private void start() {
            this.start(8888);
        }
    
        public void start(int port){
            try {
                server = new ServerSocket(port);
                this.receive();//调用接受请求的方式
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void receive() {
    
            try {
              
                    //1监听
                    Socket client = server.accept();
                      
                //获取用户的请求
                InputStream is = client.getInputStream();
                byte[] buf = new byte[20480];
                int len = is.read(buf);
                System.out.println(new String(buf, 0, len));
              
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
        public void stop(){
    
        }
    }
    
    

    在浏览器访问localhost:8888,控制台会出现如下结果:

    编写login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登入</title>
    </head>
    <body>
        <form action="http://localhost:8888/log" method="post">
            <p>用户名:<input type="text" name="username" id="username"></p>
            <p>密码:<input type="password" name="pwd" id="password"></p>
            <p>
                爱好:<input type="checkbox" name="hobby" value="ball"/>足球
                     <input type="checkbox" name="hobby" value="read"/>读书
                    <input type="checkbox" name="hobby" value="paint"/>画画
            </p>
            <p><input type="submit" value="登入"></p>
        </form>
    </body>
    </html>
    

    封装Request

    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.security.PrivateKey;
    import java.util.*;
    
    public class Request { //请求
    
        //输入流
        private InputStream is;
        private String requestInfo; //请求字符串,请求方式,请求路径,参数,协议,协议版本,请求正文。。
        private String method;//请求的方式
        private String url;//请求的路径
    
        public String getUrl() {
            return url;
        }
    
        /*
        * 输入框的name为key,值为value
        * */
        private Map<String, List<String>> parameterMapValues;//参数
        private static final String CRLF="
    ";//换行
        private static final String BLANK =" ";//空格
        //构造方法,初始化属性
        public Request(){
            parameterMapValues=new HashMap<String, List<String>>();
            method="";
            url="";
            requestInfo="";
        }
        public Request(InputStream is){
            this();//调用本类无参构造方法
            this.is=is;
    
            try {
                byte[] buf = new byte[20480];
                int len = this.is.read(buf);
                requestInfo = new String(buf, 0, len);
            } catch (IOException e) {
                return;
            }
            //调用本类中的分解信息的方法
            this.parseRequestInfo();
        }
        //分解请求信息的方法
        /**
        * 请求方式
        * 请求路径
        * 请求的参数
        * */
        public void parseRequestInfo() {
            String paraString="";//用于存储请求参数
            //获取参数的第一行
            String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF)).trim();//从0开始到第一个换行的位置
            //分解出请求方式
            int index = firstLine.indexOf("/");
            this.method = firstLine.substring(0, index).trim();
            //分解url ,get可能包含的参数,也可能不包含的参数post
            String urlString = firstLine.substring(index, firstLine.indexOf("HTTP/")).trim();
            //判断请求方式是GET或POST
            if ("get".equalsIgnoreCase(this.method)) {   //包含请求参数
                if (urlString.contains("?")){
                    String[] urlArray = urlString.split("\?");
                    this.url=urlArray[0];
                    paraString=urlArray[1];
                }else {
                    this.url=urlString;
                }
            }else {//post不包含请求参数
                this.url=urlString;
                paraString=requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();
             }
            if (paraString.equals("")){
                return;
            }
            //请求参数
            System.out.println(paraString);
           
        }
        //用于测试
        public void show(){
            System.out.println(this.url);
            System.out.println(this.method);
        }
    
      
    }
    
    

    成功封装后启动服务,控制台输出如下:

    储存参数,处理中文

    /**
       *username = admin
        * pwd = 123
        * hobby =ball
        * hobby = paint
        * */
       public void parseParam(String prarString){
           String[] token = prarString.split("&");
           for (int i=0;i<token.length;i++){
               String keyValues = token[i];
               String[] keyValue = keyValues.split("=");//把=分割掉,得到K和V
               if (keyValue.length==1){   //username=
                   keyValue = Arrays.copyOf(keyValue, 2);
                   keyValue[1] = null;
               }
               //将表单中的元素的name与name对应的值储存到Map集合
               String key = keyValue[0].trim();
               String value = keyValue[1]==null?null:decode(keyValue[1].trim(),"utf-8");
               //放到集合中存储
               if (!parameterMapValues.containsKey(key)) {  //键不存在就创建
                   parameterMapValues.put(key,new ArrayList<String>());
               }
               List<String> values = parameterMapValues.get(key);
               values.add(value); //创建一个集合添加值
           }
    
       }
        //根剧表单元素的name获取多个值
        public String [] getParamterValues(String name) {
           //根据key获取value
            List<String> values = parameterMapValues.get(name);
            if (values == null ){
                return null;
            } else {
                return values.toArray(new String[0] );
            }
        }
        public String getParamter(String name){
           //调用本类中根据name获取单个值的方法
            String[] values = this.getParamterValues(name);
            if (values ==null){
                return null;
            } else {
                return values[0];
            }
        }
         //处理中文,因浏览器对中文进行了编码,进行解码
            private String decode(String value,String code){
                try {
                    return URLDecoder.decode(value,code);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
       
    
        public static void main(String[] args) {
            Request req = new Request();
            //调用分解参数的方法
            req.parseParam("username=中文加密&pwd=123&hobby=ball&hobby=read");
            System.out.println(req.parameterMapValues);
    
            //调用获取多个值的方法
            String[] str = req.getParamterValues("hobby");
            for (String string : str) {
                System.out.println(string);
            }
            //调用单个获取值的方法
            System.out.println(req.getParamter("pwd"));
        }
    

    备注:中文参数加密(在百度上随便搜索,截取wd后面的参数)例如:苹果举例

    https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E8%8B%B9%E6%9E%9C&fenlei=256&rsv_pq=b162262500064c7f&rsv_t=3f02zT3Tvav%2FKayD%2BwKuGWxcDuG3ZnH0aQoblznxlDr2LxisTdsiuUCkLbE&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=13&rsv_sug1=17&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&inputT=13852&rsv_sug4=43101
    
    %E8%8B%B9%E6%9E%9C(这个就是加密参数)
    

    封装之后结果如下:

    加到Server类中,测试响应

      //封装请求信息
                Request req = new Request(client.getInputStream());
               
                /**
                 * 做出响应
                 * */
                StringBuilder sb = new StringBuilder();
                sb.append("HTTP/1.1").append(" ").append(200).append(" ").append("OK").append("
    ");
                sb.append("Content-Type:text/html;charset=utf-8").append("
    ");
                //内容
                String str = "<html><head><title>响应结果</title></head><body>成功</body></html>";
                sb.append("Content-Length:"+str.getBytes("utf-8").length).append("
    ");
                sb.append("
    ");
                sb.append(str);
    
                //通过输出流发送出去
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream(),"utf-8"));
                bw.write(sb.toString());
                bw.flush();
                bw.close();
    

    模拟响应结果,测试浏览器是否在登入之后出现成功状态

    编写Response

    package com.feng.server;
    import com.feng.util.IOCloseUtil;
    import java.io.*;
    
    public class Response { //响应
        private StringBuilder headInfo;  //响应头
        private StringBuilder content; //响应内容
        private int length; //响应内容的长度
    
        //流
        private BufferedWriter bw;
    
        //两个常量,换行和空格
        private static final String CRLF="
    ";  //换行
        private static final String BLANK=" ";  //空格
    
        //构造方法
        public Response() {
            headInfo= new StringBuilder();
            content = new StringBuilder();
        }
        //带构造方法
    
        public Response(OutputStream os) {
            this();//调用本类的无参构造方法
            try {
                bw = new BufferedWriter(new OutputStreamWriter(os,"utf-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        //构造正文部分
        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;
        }
    
        //构造响应头
        private void createHeadInfo(int code){
            headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
            switch (code){
                case 200:
                    headInfo.append("OK");
                    break;
                case 500:
                    headInfo.append("SERVER ERROR");
                default:
                    headInfo.append("NOT FOUND");
                    break;
            }
            headInfo.append(CRLF);
            headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF);
            headInfo.append("Content-Length:"+length).append(CRLF);
            headInfo.append(CRLF);
        }
    
        /**
         * 推送到客户机的浏览器
         * */
        public void pushToClient(int code){
            if (headInfo==null){
                code=500;
            }
            //调用本类中的构造响应头
            this.createHeadInfo(code);
            try {
                bw.write(headInfo.toString());
                bw.write(content.toString());
                bw.flush();
                this.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        public void close() {
            IOCloseUtil.closeAll(bw);
        }
    }
    
    

    然后再把Server里面做出响应那段代码注释掉

    package com.feng.server;
    
    import com.feng.servlet.Servlet;
    import com.feng.util.IOCloseUtil;
    
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server { //服务器,用于启动或停止服务
        private ServerSocket server;
      
        public static void main(String[] args) {
            Server server = new Server();//创建服务器
            server.start();
        }
    
        private void start() {
            this.start(8888);
        }
    
        public void start(int port){
            try {
                server = new ServerSocket(port);
                this.receive();//调用接受请求的方式
            } catch (IOException e) {
                 e.printStackTrace();
            }
        }
    
        private void receive() {
    
            try {
                //1监听
                Socket client = server.accept();
                //创建线程类
                Dispatcher dis = new Dispatcher(client);
                //创建代理线程
                new Thread(dis).start();
                
              
               //封装请求信息
                Request req = new Request(client.getInputStream());
                //req.show();
                /**
                 * 做出响应
                 * */
                /*StringBuilder sb = new StringBuilder();
                sb.append("HTTP/1.1").append(" ").append(200).append(" ").append("OK").append("
    ");
                sb.append("Content-Type:text/html;charset=utf-8").append("
    ");
                //内容
                String str = "<html><head><title>响应结果</title></head><body>成功</body></html>";
                sb.append("Content-Length:"+str.getBytes("utf-8").length).append("
    ");
                sb.append("
    ");
                sb.append(str);
    
                //通过输出流发送出去
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream(),"utf-8"));
                bw.write(sb.toString());
                bw.flush();
                bw.close();*/
                Response rep = new Response(client.getOutputStream());
                Servlet servlet = WebApp.getServlet(req.getUrl());
                int code=200;
                if (servlet ==null){
                    code=404;
                }
                //调用Servlet中的服务方法
                try {
                    servlet.service(req,rep);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                rep.pushToClient(code);
                IOCloseUtil.closeAll(client);  //单线程已被删除
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
        public void stop(){
    
        }
    }
    
    

    封装Response

    编写servlet

    public abstract class Servlet { //是所有请求servlet的父类
        public void service(Request req, Response rep) throws Exception {
            this.deGet(req,rep);
            this.doPost(req,rep);
        }
    
        public abstract void deGet(Request req, Response rep) throws Exception;
        public abstract void doPost(Request req, Response rep) throws Exception;
    
    }
    

    编写LoginServlet

    public class LoginServlet extends Servlet {
        @Override
        public void deGet(Request req, Response rep) throws Exception {
            //获取请求参数
            String name = req.getParamter("username");
            String pwd = req.getParamter("pwd");
    
            if (this.login(name,pwd)) {
                //调用响应中的构建内容的方法
                rep.printLn(name+"登入成功");
            }else {
                rep.printLn(name+"登录失败,对不起,账号和密码不准确");
            }
        }
        private boolean login(String name,String pwd){
            if ("admin".equals(name)&&"123".equals(pwd)) {
                return true;
            }
            return false;
        }
    
        @Override
        public void doPost(Request req, Response rep) throws Exception {
    
        }
    }
    
    

    在Server创建Response对象,用Servlet调用service方法,请求和响应之后并推送到客服端,用户名和密码正确的话如图所示:

    如果用户名和密码不匹配的话:

    封装分发器,实现多线程(Dispatcher)

    package com.feng.server;
    
    import com.feng.servlet.Servlet;
    import com.feng.util.IOCloseUtil;
    
    import java.io.IOException;
    import java.net.Socket;
    
    /**
     * 一个请求与响应就是一个Dispatcher
     *
     * @author asus
     * */
    public class Dispatcher implements Runnable {
        private Request req;
        private Response rep;
        private Socket client;
        private int code =200;//状态码
    
        //构造方法初始化属性
    
        public Dispatcher(Socket client) {
            this.client = client;
            try {
                req=new Request(this.client.getInputStream());
                rep=new Response(this.client.getOutputStream());
            } catch (IOException e) {
                code=500;
                return;
            }
    
        }
    
        @Override
        public void run() {
            //根据不同的url创建指定的servlet对象
            Servlet servlet = WebApp.getServlet(req.getUrl());
            if (servlet==null){
                this.code=404;
            }else {
                //调用响应的servlet中service方法
                try {
                    servlet.service(req,rep);
                } catch (Exception e) {
                    this.code=500;
                }
            }
            //将有响应的结果推送到客户端的浏览器
            rep.pushToClient(code);
            IOCloseUtil.closeAll(client);
        }
    }
    
    

    实现多线程

    这是修改后的服务器主线程类

    public class Server { //服务器,用于启动或停止服务
        private ServerSocket server;
        private boolean isShutDown=false; //默认没有出错
    
        public static void main(String[] args) {
            Server server = new Server();//创建服务器
            server.start();
        }
    
        private void start() {
            this.start(8888);
        }
    
        public void start(int port){
            try {
                server = new ServerSocket(port);
                this.receive();//调用接受请求的方式
            } catch (IOException e) {
                isShutDown=true;
            }
        }
    
        private void receive() {
    
            try {
                while (!isShutDown){
                    //1监听
                    Socket client = server.accept();
                    //创建线程类
                    Dispatcher dis = new Dispatcher(client);
                    //创建代理线程
                    new Thread(dis).start();
                }
               
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
        public void stop(){
    	isShutDown=true;
    	IOCloseUtil.closeAll(server);
        }
    }
    
    

    但是这样还没有成功,控制台还会出现bug,虽然服务器没有停止,可能会出现潜在的风险,控制台出现空指针异常:

    修改空指针异常

    为了更好查看bug,我在Dispatcher类中的run方法里面添加了一个测试路径

    System.out.println(req.getUrl());    //测试
    

    控制台会出现一个新增的类:

    缺少favicon映射类,在xml里面配置

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app>
        <servlet>
            <servlet-name>login</servlet-name>
            <servlet-class>com.feng.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/log</url-pattern>
            <url-pattern>/login</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>register</servlet-name>
            <servlet-class>com.feng.servlet.RegisterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>register</servlet-name>
            <url-pattern>/reg</url-pattern>
            <url-pattern>/regis</url-pattern>
            <url-pattern>/register</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>favicon</servlet-name>
            <servlet-class>com.feng.servlet.FaviconServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>favicon</servlet-name>
            <url-pattern>/favicon.ico</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    添加FaviconServlet

    package com.feng.servlet;
    
    import com.feng.server.Request;
    import com.feng.server.Response;
    
    public class FaviconServlet extends Servlet {
        @Override
        public void deGet(Request req, Response rep) throws Exception {
    
        }
    
        @Override
        public void doPost(Request req, Response rep) throws Exception {
    
        }
    }
    
    

    最后运行成功,控制台没有出现bug

    避坑bug经验

    return reader.read(new File("web/WEB-INF/web.xml"));
    

    假如要配置正确的路径格式,那么先从根目录下开始写相对路径,当路径格式配置有问题时,例如WEB-INF看错了可能写成WEB_INF,控制台马上会出现如下错误:

  • 相关阅读:
    HTML向Flex传参
    Flex数据推送
    Flex+BlazeDS+Spring整合
    Clone Graph
    Word Break II
    Word Break
    Pascal's Triangle
    N-Queens II
    N-Queens
    Length of Last Word
  • 原文地址:https://www.cnblogs.com/zzy8080/p/13902848.html
Copyright © 2011-2022 走看看