第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("完整类名")