第13次文章:网络编程——httpserver服务器的搭建(中)

  • 2019 年 10 月 8 日
  • 筆記

这周的代码出了点问题,目前还没有调试完全,就先不把所有代码都粘贴上来了。下周默默的去调代码了!

对于上周的一个总结:

在上周我们搭建的服务器,仅仅具有一个传输一个界面的功能。在实际应用中,对于同一份资源的访问,往往可以通过多条路径进行访问,而且可以处理多个不同的请求。这些需求,就导致了我们在搭建服务器的时候,需要逐步去完善服务器的功能。

一、对于多请求的处理——多态

我们模拟一个具有注册和登录功能的网页,首先我们定义一个抽象类Servlet,为后面的具体功能实现提供一个规范的编写方法,然后再创建具体的注册和登录类,实现网页多请求处理,也就是多态。

第一步:创建抽象类Servlet

package com.peng.server.demo03;  /**   * 抽象为一个父类   */  public abstract class Servlet {    public void service(Response rep,Request req) throws Exception{      this.doGet(rep,req);      this.doPost(rep,req);    }    public abstract void doGet(Response rep,Request req) throws Exception;    public abstract void doPost(Response rep,Request req) throws Exception;  }

第二步:创建登录类

package com.peng.server.demo03;  /**   * 登录界面   */  public class LoginServlet extends Servlet {    @Override    public void doGet(Response rep, Request req) throws Exception {      String name = req.getParameter("uname");      String pwd = req.getParameter("pwd");      if(login(name,pwd)) {        rep.println("登陆成功");      }else {        rep.println("登录失败");      }    }    public boolean login(String name,String pwd) {      return name.equals("peng") && pwd.equals("123456");    }    @Override    public void doPost(Response rep, Request req) throws Exception {      // TODO Auto-generated method stub    }  }

第三步:创建注册类

package com.peng.server.demo03;  public class RegisterServlet extends Servlet{    @Override    public void doGet(Response rep, Request req) throws Exception {      // TODO Auto-generated method stub    }    @Override    public void doPost(Response rep, Request req) throws Exception {      rep.println("<html><head><title>返回注册</title>");      rep.println("</head><body>");      rep.println("你的用户名为:"+req.getParameter("uname"));      rep.println("</body></html>");    }  }

二、多条路径访问同一份资源

在实际情况下,对于同一份资源或者说文件的访问,往往具有多条路径可以指向目的地,所以我们需要解决多条路径访问同一份资源的问题。我们可以设想一下相关的解决方案,为了满足多条路径访问同一份资源,简单的思路就是多对一的解决方案。所以我们可以使用容器来解决这个问题,在容器的选择上,我们使用键值对map来存储目标资源和相关的所有路径,只要在键值对存在着相关路径,那么我们就可以在键值对map上进行搜索,然后将键值对中对应的键取出来,与其相对应的值进行比对。具体实现如下:

第一步:建立一个上下文,由于我们所有的网页信息均在Servlet中进行实现,所以,我们首先使用一个map为每个多态类别起一个别名,在后面的存储的时候,方便直接存储字符串,而不是存储整个类。然后再使用一个map,用来存储不同路径与Servlet类别的映射关系。根据每一条不同的路径,直接进行映射到同一份资源处。

package com.peng.server.demo03;    import java.util.HashMap;  import java.util.Map;    /**   * 上下文   */  public class ServletContext {    /**     * 使用两个map的主要作用在于:可以对于同一份资源LoginServlet,首先为其取一个别名,     * 然后可以使用多个路径访(/log --->login ||   /login --->login)问同一份资源LoginServlet     */    //为每一个servlet取一个别名    //login ---> com.peng.server.LoginServlet    private Map<String,String> servlet;    // mapping作为一个映射,对于多个访问路径,指向同一份资源    //  url --->login    //    /log --->login    //    /login --->login    private Map<String,String> mapping;    public ServletContext() {      servlet = new HashMap<String,String>();      mapping = new HashMap<String,String>();    }      //set和get方法    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;    }  }

第二步:将关于登录和注册的路径存储在相关的map中,比如对于路径 : /login ,将访问: /login;对于路径 : /log ,将访问: /login;对于路径: /reg ,将访问: /register。

package com.peng.server.demo03;  import java.util.Map;  public class WebApp {    private static ServletContext context;    static {      context = new ServletContext();      Map<String,String> mapping = context.getMapping();      /**       * 传送路径:       * 对于路径 : /login  ,将访问: /login       * 对于路径 : /log  ,将访问: /login       * 对于路径: /reg  ,将访问: /register       */      mapping.put("/login", "/login");      mapping.put("/log", "/login");      mapping.put("/reg", "/register");      Map<String,String> servlet = context.getServlet();      servlet.put("login","com.peng.server.LoginServlet");      servlet.put("register","com.peng.server.RegisterServlet");    }    //使用了反射的动态属性    public static Servlet getServlet(String url) throws InstantiationException, IllegalAccessException, ClassNotFoundException {      if(null == url || (url=url.trim()).equals("")) {        return null;      }      //根据字符串(完整路径)创建对象      String name = context.getServlet().get(context.getMapping().get(url));      return (Servlet)Class.forName(name).newInstance();//在动态使用的时候创建这个对象 确保空构造存在    }  }

三、分发器的封装

由于整个网页的请求服务属于多线程处理,所以我们需要建立一个分发器,来进行对各个用户的请求与应答进行相关的处理。

package com.peng.server.demo03;    import java.io.IOException;  import java.net.Socket;  import com.peng.server.util.CloseUtil;    public class Dispatcher implements Runnable{      private Socket client;    private Response rep;    private Request req;    private int code =200;      public Dispatcher(Socket client) {      this.client = client;      try {        req = new Request(client.getInputStream());        rep = new Response(client.getOutputStream());      } catch (IOException e) {        code = 500;//报错后,将代码状态推送1个500出去,表示代码有问题        return ;      }    }      @Override    public void run() {      try {        Servlet serv = WebApp.getServlet(req.getUrl());//动态的通过访问地址获取访问相关资源        if(null == serv) {          this.code = 404; //找不到处理        }else {          serv.service(rep, req);        }        rep.pushToClient(code);      } catch (Exception e) {        //若此处报错,则继续尝试推送一个500出去        this.code = 500;      }      try {        rep.pushToClient(500);      } catch (IOException e) {        // TODO Auto-generated catch block        e.printStackTrace();      }      CloseUtil.closeAll(client);    }  }

四、关于反射的一点概念:

在我们将多条路径和目的地资源写入到map中时,我们使用了一点反射的概念。下面简单介绍一些关于反射的东西。

1、动态语言:在运行期间随意的改变对象的类型 +结构 例如:ruby js

java不是动态语言,但具有动态属性(反射)

2、反射 镜子

1)在编译器创建对象 new 完整类名 反射—->通过对象 找出类名

2)在运行期动态的创建对象 分析对象(属性、方法)

3)jvm在创建对象时自动生成与之对应Class对象,同一个类的多个对象在jvm只有一个与之对应的class对象

3、获取Class类别的几种常用方法

1)、可以看成类的元数据。每一个对象在创建的时候,jvm会自动生成一个与之对应的class。同类型的对象对应一个class

2)、获取class对象的三种方式

a、Object中的getClass方法 : 对象.getClass()

b、类.class形式 : 如student.class.

c、最常用,Class类的静态方法 :Class.forName("完整类名")